diff options
-rw-r--r-- | Documentation/fb/cmap_xfbdev.txt | 53 | ||||
-rw-r--r-- | Documentation/fb/metronomefb.txt | 38 | ||||
-rw-r--r-- | drivers/video/Kconfig | 14 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fb_defio.c | 22 | ||||
-rw-r--r-- | drivers/video/metronomefb.c | 999 |
6 files changed, 1125 insertions, 2 deletions
diff --git a/Documentation/fb/cmap_xfbdev.txt b/Documentation/fb/cmap_xfbdev.txt new file mode 100644 index 000000000000..55e1f0a3d2b4 --- /dev/null +++ b/Documentation/fb/cmap_xfbdev.txt | |||
@@ -0,0 +1,53 @@ | |||
1 | Understanding fbdev's cmap | ||
2 | -------------------------- | ||
3 | |||
4 | These notes explain how X's dix layer uses fbdev's cmap structures. | ||
5 | |||
6 | *. example of relevant structures in fbdev as used for a 3-bit grayscale cmap | ||
7 | struct fb_var_screeninfo { | ||
8 | .bits_per_pixel = 8, | ||
9 | .grayscale = 1, | ||
10 | .red = { 4, 3, 0 }, | ||
11 | .green = { 0, 0, 0 }, | ||
12 | .blue = { 0, 0, 0 }, | ||
13 | } | ||
14 | struct fb_fix_screeninfo { | ||
15 | .visual = FB_VISUAL_STATIC_PSEUDOCOLOR, | ||
16 | } | ||
17 | for (i = 0; i < 8; i++) | ||
18 | info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16; | ||
19 | memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8); | ||
20 | memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8); | ||
21 | |||
22 | *. X11 apps do something like the following when trying to use grayscale. | ||
23 | for (i=0; i < 8; i++) { | ||
24 | char colorspec[64]; | ||
25 | memset(colorspec,0,64); | ||
26 | sprintf(colorspec, "rgb:%x/%x/%x", i*36,i*36,i*36); | ||
27 | if (!XParseColor(outputDisplay, testColormap, colorspec, &wantedColor)) | ||
28 | printf("Can't get color %s\n",colorspec); | ||
29 | XAllocColor(outputDisplay, testColormap, &wantedColor); | ||
30 | grays[i] = wantedColor; | ||
31 | } | ||
32 | There's also named equivalents like gray1..x provided you have an rgb.txt. | ||
33 | |||
34 | Somewhere in X's callchain, this results in a call to X code that handles the | ||
35 | colormap. For example, Xfbdev hits the following: | ||
36 | |||
37 | xc-011010/programs/Xserver/dix/colormap.c: | ||
38 | |||
39 | FindBestPixel(pentFirst, size, prgb, channel) | ||
40 | |||
41 | dr = (long) pent->co.local.red - prgb->red; | ||
42 | dg = (long) pent->co.local.green - prgb->green; | ||
43 | db = (long) pent->co.local.blue - prgb->blue; | ||
44 | sq = dr * dr; | ||
45 | UnsignedToBigNum (sq, &sum); | ||
46 | BigNumAdd (&sum, &temp, &sum); | ||
47 | |||
48 | co.local.red are entries that were brought in through FBIOGETCMAP which come | ||
49 | directly from the info->cmap.red that was listed above. The prgb is the rgb | ||
50 | that the app wants to match to. The above code is doing what looks like a least | ||
51 | squares matching function. That's why the cmap entries can't be set to the left | ||
52 | hand side boundaries of a color range. | ||
53 | |||
diff --git a/Documentation/fb/metronomefb.txt b/Documentation/fb/metronomefb.txt new file mode 100644 index 000000000000..b9a2e7b7e838 --- /dev/null +++ b/Documentation/fb/metronomefb.txt | |||
@@ -0,0 +1,38 @@ | |||
1 | Metronomefb | ||
2 | ----------- | ||
3 | Maintained by Jaya Kumar <jayakumar.lkml.gmail.com> | ||
4 | Last revised: Nov 20, 2007 | ||
5 | |||
6 | Metronomefb is a driver for the Metronome display controller. The controller | ||
7 | is from E-Ink Corporation. It is intended to be used to drive the E-Ink | ||
8 | Vizplex display media. E-Ink hosts some details of this controller and the | ||
9 | display media here http://www.e-ink.com/products/matrix/metronome.html . | ||
10 | |||
11 | Metronome is interfaced to the host CPU through the AMLCD interface. The | ||
12 | host CPU generates the control information and the image in a framebuffer | ||
13 | which is then delivered to the AMLCD interface by a host specific method. | ||
14 | Currently, that's implemented for the PXA's LCDC controller. The display and | ||
15 | error status are each pulled through individual GPIOs. | ||
16 | |||
17 | Metronomefb was written for the PXA255/gumstix/lyre combination and | ||
18 | therefore currently has board set specific code in it. If other boards based on | ||
19 | other architectures are available, then the host specific code can be separated | ||
20 | and abstracted out. | ||
21 | |||
22 | Metronomefb requires waveform information which is delivered via the AMLCD | ||
23 | interface to the metronome controller. The waveform information is expected to | ||
24 | be delivered from userspace via the firmware class interface. The waveform file | ||
25 | can be compressed as long as your udev or hotplug script is aware of the need | ||
26 | to uncompress it before delivering it. metronomefb will ask for waveform.wbf | ||
27 | which would typically go into /lib/firmware/waveform.wbf depending on your | ||
28 | udev/hotplug setup. I have only tested with a single waveform file which was | ||
29 | originally labeled 23P01201_60_WT0107_MTC. I do not know what it stands for. | ||
30 | Caution should be exercised when manipulating the waveform as there may be | ||
31 | a possibility that it could have some permanent effects on the display media. | ||
32 | I neither have access to nor know exactly what the waveform does in terms of | ||
33 | the physical media. | ||
34 | |||
35 | Metronomefb uses the deferred IO interface so that it can provide a memory | ||
36 | mappable frame buffer. It has been tested with tinyx (Xfbdev). It is known | ||
37 | to work at this time with xeyes, xclock, xloadimage, xpdf. | ||
38 | |||
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e0b0580705e4..1bd5fb30237d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1893,6 +1893,20 @@ config FB_XILINX | |||
1893 | framebuffer. ML300 carries a 640*480 LCD display on the board, | 1893 | framebuffer. ML300 carries a 640*480 LCD display on the board, |
1894 | ML403 uses a standard DB15 VGA connector. | 1894 | ML403 uses a standard DB15 VGA connector. |
1895 | 1895 | ||
1896 | config FB_METRONOME | ||
1897 | tristate "Metronome display controller support" | ||
1898 | depends on FB && ARCH_PXA && MMU | ||
1899 | select FB_SYS_FILLRECT | ||
1900 | select FB_SYS_COPYAREA | ||
1901 | select FB_SYS_IMAGEBLIT | ||
1902 | select FB_SYS_FOPS | ||
1903 | select FB_DEFERRED_IO | ||
1904 | help | ||
1905 | This enables support for the Metronome display controller. Tested | ||
1906 | with an E-Ink 800x600 display and Gumstix Connex through an AMLCD | ||
1907 | interface. Please read <file:Documentation/fb/metronomefb.txt> | ||
1908 | for more information. | ||
1909 | |||
1896 | config FB_VIRTUAL | 1910 | config FB_VIRTUAL |
1897 | tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" | 1911 | tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" |
1898 | depends on FB | 1912 | depends on FB |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 03371c789039..11c0e5e05f21 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -103,6 +103,7 @@ obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o | |||
103 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o | 103 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o |
104 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o | 104 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o |
105 | obj-$(CONFIG_FB_MAXINE) += maxinefb.o | 105 | obj-$(CONFIG_FB_MAXINE) += maxinefb.o |
106 | obj-$(CONFIG_FB_METRONOME) += metronomefb.o | ||
106 | obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o | 107 | obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o |
107 | obj-$(CONFIG_FB_IMX) += imxfb.o | 108 | obj-$(CONFIG_FB_IMX) += imxfb.o |
108 | obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o | 109 | obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o |
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 0f8cfb988c90..24843fdd5395 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright (C) 2006 Jaya Kumar | 4 | * Copyright (C) 2006 Jaya Kumar |
5 | * | 5 | * |
6 | * This file is subject to the terms and conditions of the GNU General Public | 6 | * This file is subject to the terms and conditions of the GNU General Public |
7 | * License. See the file COPYING in the main directory of this archive | 7 | * License. See the file COPYING in the main directory of this archive |
8 | * for more details. | 8 | * for more details. |
9 | */ | 9 | */ |
10 | 10 | ||
@@ -31,7 +31,7 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma, | |||
31 | unsigned long offset; | 31 | unsigned long offset; |
32 | struct page *page; | 32 | struct page *page; |
33 | struct fb_info *info = vma->vm_private_data; | 33 | struct fb_info *info = vma->vm_private_data; |
34 | /* info->screen_base is in System RAM */ | 34 | /* info->screen_base is virtual memory */ |
35 | void *screen_base = (void __force *) info->screen_base; | 35 | void *screen_base = (void __force *) info->screen_base; |
36 | 36 | ||
37 | offset = vmf->pgoff << PAGE_SHIFT; | 37 | offset = vmf->pgoff << PAGE_SHIFT; |
@@ -43,6 +43,15 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma, | |||
43 | return VM_FAULT_SIGBUS; | 43 | return VM_FAULT_SIGBUS; |
44 | 44 | ||
45 | get_page(page); | 45 | get_page(page); |
46 | |||
47 | if (vma->vm_file) | ||
48 | page->mapping = vma->vm_file->f_mapping; | ||
49 | else | ||
50 | printk(KERN_ERR "no mapping available\n"); | ||
51 | |||
52 | BUG_ON(!page->mapping); | ||
53 | page->index = vmf->pgoff; | ||
54 | |||
46 | vmf->page = page; | 55 | vmf->page = page; |
47 | return 0; | 56 | return 0; |
48 | } | 57 | } |
@@ -138,11 +147,20 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_init); | |||
138 | 147 | ||
139 | void fb_deferred_io_cleanup(struct fb_info *info) | 148 | void fb_deferred_io_cleanup(struct fb_info *info) |
140 | { | 149 | { |
150 | void *screen_base = (void __force *) info->screen_base; | ||
141 | struct fb_deferred_io *fbdefio = info->fbdefio; | 151 | struct fb_deferred_io *fbdefio = info->fbdefio; |
152 | struct page *page; | ||
153 | int i; | ||
142 | 154 | ||
143 | BUG_ON(!fbdefio); | 155 | BUG_ON(!fbdefio); |
144 | cancel_delayed_work(&info->deferred_work); | 156 | cancel_delayed_work(&info->deferred_work); |
145 | flush_scheduled_work(); | 157 | flush_scheduled_work(); |
158 | |||
159 | /* clear out the mapping that we setup */ | ||
160 | for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) { | ||
161 | page = vmalloc_to_page(screen_base + i); | ||
162 | page->mapping = NULL; | ||
163 | } | ||
146 | } | 164 | } |
147 | EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup); | 165 | EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup); |
148 | 166 | ||
diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c new file mode 100644 index 000000000000..e9a89fd82757 --- /dev/null +++ b/drivers/video/metronomefb.c | |||
@@ -0,0 +1,999 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/metronomefb.c -- FB driver for Metronome controller | ||
3 | * | ||
4 | * Copyright (C) 2008, Jaya Kumar | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive for | ||
8 | * more details. | ||
9 | * | ||
10 | * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. | ||
11 | * | ||
12 | * This work was made possible by help and equipment support from E-Ink | ||
13 | * Corporation. http://support.eink.com/community | ||
14 | * | ||
15 | * This driver is written to be used with the Metronome display controller. | ||
16 | * It was tested with an E-Ink 800x600 Vizplex EPD on a Gumstix Connex board | ||
17 | * using the Lyre interface board. | ||
18 | * | ||
19 | * General notes: | ||
20 | * - User must set metronomefb_enable=1 to enable it. | ||
21 | * - See Documentation/fb/metronomefb.txt for how metronome works. | ||
22 | */ | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/string.h> | ||
27 | #include <linux/mm.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/vmalloc.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/fb.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/platform_device.h> | ||
35 | #include <linux/list.h> | ||
36 | #include <linux/firmware.h> | ||
37 | #include <linux/dma-mapping.h> | ||
38 | #include <linux/uaccess.h> | ||
39 | #include <linux/irq.h> | ||
40 | |||
41 | #include <asm/arch/pxa-regs.h> | ||
42 | #include <asm/unaligned.h> | ||
43 | |||
44 | #define DEBUG 1 | ||
45 | #ifdef DEBUG | ||
46 | #define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) | ||
47 | #else | ||
48 | #define DPRINTK(f, a...) | ||
49 | #endif | ||
50 | |||
51 | |||
52 | /* Display specific information */ | ||
53 | #define DPY_W 832 | ||
54 | #define DPY_H 622 | ||
55 | |||
56 | struct metromem_desc { | ||
57 | u32 mFDADR0; | ||
58 | u32 mFSADR0; | ||
59 | u32 mFIDR0; | ||
60 | u32 mLDCMD0; | ||
61 | }; | ||
62 | |||
63 | struct metromem_cmd { | ||
64 | u16 opcode; | ||
65 | u16 args[((64-2)/2)]; | ||
66 | u16 csum; | ||
67 | }; | ||
68 | |||
69 | struct metronomefb_par { | ||
70 | unsigned char *metromem; | ||
71 | struct metromem_desc *metromem_desc; | ||
72 | struct metromem_cmd *metromem_cmd; | ||
73 | unsigned char *metromem_wfm; | ||
74 | unsigned char *metromem_img; | ||
75 | u16 *metromem_img_csum; | ||
76 | u16 *csum_table; | ||
77 | int metromemsize; | ||
78 | dma_addr_t metromem_dma; | ||
79 | dma_addr_t metromem_desc_dma; | ||
80 | struct fb_info *info; | ||
81 | wait_queue_head_t waitq; | ||
82 | u8 frame_count; | ||
83 | }; | ||
84 | |||
85 | /* frame differs from image. frame includes non-visible pixels */ | ||
86 | struct epd_frame { | ||
87 | int fw; /* frame width */ | ||
88 | int fh; /* frame height */ | ||
89 | }; | ||
90 | |||
91 | static struct epd_frame epd_frame_table[] = { | ||
92 | { | ||
93 | .fw = 832, | ||
94 | .fh = 622 | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | static struct fb_fix_screeninfo metronomefb_fix __devinitdata = { | ||
99 | .id = "metronomefb", | ||
100 | .type = FB_TYPE_PACKED_PIXELS, | ||
101 | .visual = FB_VISUAL_STATIC_PSEUDOCOLOR, | ||
102 | .xpanstep = 0, | ||
103 | .ypanstep = 0, | ||
104 | .ywrapstep = 0, | ||
105 | .line_length = DPY_W, | ||
106 | .accel = FB_ACCEL_NONE, | ||
107 | }; | ||
108 | |||
109 | static struct fb_var_screeninfo metronomefb_var __devinitdata = { | ||
110 | .xres = DPY_W, | ||
111 | .yres = DPY_H, | ||
112 | .xres_virtual = DPY_W, | ||
113 | .yres_virtual = DPY_H, | ||
114 | .bits_per_pixel = 8, | ||
115 | .grayscale = 1, | ||
116 | .nonstd = 1, | ||
117 | .red = { 4, 3, 0 }, | ||
118 | .green = { 0, 0, 0 }, | ||
119 | .blue = { 0, 0, 0 }, | ||
120 | .transp = { 0, 0, 0 }, | ||
121 | }; | ||
122 | |||
123 | static unsigned int metronomefb_enable; | ||
124 | |||
125 | struct waveform_hdr { | ||
126 | u8 stuff[32]; | ||
127 | |||
128 | u8 wmta[3]; | ||
129 | u8 fvsn; | ||
130 | |||
131 | u8 luts; | ||
132 | u8 mc; | ||
133 | u8 trc; | ||
134 | u8 stuff3; | ||
135 | |||
136 | u8 endb; | ||
137 | u8 swtb; | ||
138 | u8 stuff2a[2]; | ||
139 | |||
140 | u8 stuff2b[3]; | ||
141 | u8 wfm_cs; | ||
142 | } __attribute__ ((packed)); | ||
143 | |||
144 | /* main metronomefb functions */ | ||
145 | static u8 calc_cksum(int start, int end, u8 *mem) | ||
146 | { | ||
147 | u8 tmp = 0; | ||
148 | int i; | ||
149 | |||
150 | for (i = start; i < end; i++) | ||
151 | tmp += mem[i]; | ||
152 | |||
153 | return tmp; | ||
154 | } | ||
155 | |||
156 | static u16 calc_img_cksum(u16 *start, int length) | ||
157 | { | ||
158 | u16 tmp = 0; | ||
159 | |||
160 | while (length--) | ||
161 | tmp += *start++; | ||
162 | |||
163 | return tmp; | ||
164 | } | ||
165 | |||
166 | /* here we decode the incoming waveform file and populate metromem */ | ||
167 | #define EXP_WFORM_SIZE 47001 | ||
168 | static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, | ||
169 | u8 *frame_count) | ||
170 | { | ||
171 | int tta; | ||
172 | int wmta; | ||
173 | int trn = 0; | ||
174 | int i; | ||
175 | unsigned char v; | ||
176 | u8 cksum; | ||
177 | int cksum_idx; | ||
178 | int wfm_idx, owfm_idx; | ||
179 | int mem_idx = 0; | ||
180 | struct waveform_hdr *wfm_hdr; | ||
181 | |||
182 | if (size != EXP_WFORM_SIZE) { | ||
183 | printk(KERN_ERR "Error: unexpected size %d != %d\n", size, | ||
184 | EXP_WFORM_SIZE); | ||
185 | return -EINVAL; | ||
186 | } | ||
187 | |||
188 | wfm_hdr = (struct waveform_hdr *) mem; | ||
189 | |||
190 | if (wfm_hdr->fvsn != 1) { | ||
191 | printk(KERN_ERR "Error: bad fvsn %x\n", wfm_hdr->fvsn); | ||
192 | return -EINVAL; | ||
193 | } | ||
194 | if (wfm_hdr->luts != 0) { | ||
195 | printk(KERN_ERR "Error: bad luts %x\n", wfm_hdr->luts); | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | cksum = calc_cksum(32, 47, mem); | ||
199 | if (cksum != wfm_hdr->wfm_cs) { | ||
200 | printk(KERN_ERR "Error: bad cksum %x != %x\n", cksum, | ||
201 | wfm_hdr->wfm_cs); | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | wfm_hdr->mc += 1; | ||
205 | wfm_hdr->trc += 1; | ||
206 | for (i = 0; i < 5; i++) { | ||
207 | if (*(wfm_hdr->stuff2a + i) != 0) { | ||
208 | printk(KERN_ERR "Error: unexpected value in padding\n"); | ||
209 | return -EINVAL; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | /* calculating trn. trn is something used to index into | ||
214 | the waveform. presumably selecting the right one for the | ||
215 | desired temperature. it works out the offset of the first | ||
216 | v that exceeds the specified temperature */ | ||
217 | if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size) | ||
218 | return -EINVAL; | ||
219 | |||
220 | for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) { | ||
221 | if (mem[i] > t) { | ||
222 | trn = i - sizeof(*wfm_hdr) - 1; | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | /* check temperature range table checksum */ | ||
228 | cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1; | ||
229 | if (cksum_idx > size) | ||
230 | return -EINVAL; | ||
231 | cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem); | ||
232 | if (cksum != mem[cksum_idx]) { | ||
233 | printk(KERN_ERR "Error: bad temperature range table cksum" | ||
234 | " %x != %x\n", cksum, mem[cksum_idx]); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | |||
238 | /* check waveform mode table address checksum */ | ||
239 | wmta = le32_to_cpu(get_unaligned((__le32 *) wfm_hdr->wmta)); | ||
240 | wmta &= 0x00FFFFFF; | ||
241 | cksum_idx = wmta + m*4 + 3; | ||
242 | if (cksum_idx > size) | ||
243 | return -EINVAL; | ||
244 | cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); | ||
245 | if (cksum != mem[cksum_idx]) { | ||
246 | printk(KERN_ERR "Error: bad mode table address cksum" | ||
247 | " %x != %x\n", cksum, mem[cksum_idx]); | ||
248 | return -EINVAL; | ||
249 | } | ||
250 | |||
251 | /* check waveform temperature table address checksum */ | ||
252 | tta = le32_to_cpu(get_unaligned((int *) (mem + wmta + m*4))); | ||
253 | tta &= 0x00FFFFFF; | ||
254 | cksum_idx = tta + trn*4 + 3; | ||
255 | if (cksum_idx > size) | ||
256 | return -EINVAL; | ||
257 | cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); | ||
258 | if (cksum != mem[cksum_idx]) { | ||
259 | printk(KERN_ERR "Error: bad temperature table address cksum" | ||
260 | " %x != %x\n", cksum, mem[cksum_idx]); | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | |||
264 | /* here we do the real work of putting the waveform into the | ||
265 | metromem buffer. this does runlength decoding of the waveform */ | ||
266 | wfm_idx = le32_to_cpu(get_unaligned((__le32 *) (mem + tta + trn*4))); | ||
267 | wfm_idx &= 0x00FFFFFF; | ||
268 | owfm_idx = wfm_idx; | ||
269 | if (wfm_idx > size) | ||
270 | return -EINVAL; | ||
271 | while (wfm_idx < size) { | ||
272 | unsigned char rl; | ||
273 | v = mem[wfm_idx++]; | ||
274 | if (v == wfm_hdr->swtb) { | ||
275 | while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) && | ||
276 | wfm_idx < size) | ||
277 | metromem[mem_idx++] = v; | ||
278 | |||
279 | continue; | ||
280 | } | ||
281 | |||
282 | if (v == wfm_hdr->endb) | ||
283 | break; | ||
284 | |||
285 | rl = mem[wfm_idx++]; | ||
286 | for (i = 0; i <= rl; i++) | ||
287 | metromem[mem_idx++] = v; | ||
288 | } | ||
289 | |||
290 | cksum_idx = wfm_idx; | ||
291 | if (cksum_idx > size) | ||
292 | return -EINVAL; | ||
293 | cksum = calc_cksum(owfm_idx, cksum_idx, mem); | ||
294 | if (cksum != mem[cksum_idx]) { | ||
295 | printk(KERN_ERR "Error: bad waveform data cksum" | ||
296 | " %x != %x\n", cksum, mem[cksum_idx]); | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | *frame_count = (mem_idx/64); | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | /* register offsets for gpio control */ | ||
305 | #define LED_GPIO_PIN 51 | ||
306 | #define STDBY_GPIO_PIN 48 | ||
307 | #define RST_GPIO_PIN 49 | ||
308 | #define RDY_GPIO_PIN 32 | ||
309 | #define ERR_GPIO_PIN 17 | ||
310 | #define PCBPWR_GPIO_PIN 16 | ||
311 | |||
312 | #define AF_SEL_GPIO_N 0x3 | ||
313 | #define GAFR0_U_OFFSET(pin) ((pin - 16) * 2) | ||
314 | #define GAFR1_L_OFFSET(pin) ((pin - 32) * 2) | ||
315 | #define GAFR1_U_OFFSET(pin) ((pin - 48) * 2) | ||
316 | #define GPDR1_OFFSET(pin) (pin - 32) | ||
317 | #define GPCR1_OFFSET(pin) (pin - 32) | ||
318 | #define GPSR1_OFFSET(pin) (pin - 32) | ||
319 | #define GPCR0_OFFSET(pin) (pin) | ||
320 | #define GPSR0_OFFSET(pin) (pin) | ||
321 | |||
322 | static void metronome_set_gpio_output(int pin, int val) | ||
323 | { | ||
324 | u8 index; | ||
325 | |||
326 | index = pin >> 4; | ||
327 | |||
328 | switch (index) { | ||
329 | case 1: | ||
330 | if (val) | ||
331 | GPSR0 |= (1 << GPSR0_OFFSET(pin)); | ||
332 | else | ||
333 | GPCR0 |= (1 << GPCR0_OFFSET(pin)); | ||
334 | break; | ||
335 | case 2: | ||
336 | break; | ||
337 | case 3: | ||
338 | if (val) | ||
339 | GPSR1 |= (1 << GPSR1_OFFSET(pin)); | ||
340 | else | ||
341 | GPCR1 |= (1 << GPCR1_OFFSET(pin)); | ||
342 | break; | ||
343 | default: | ||
344 | printk(KERN_ERR "unimplemented\n"); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | static void __devinit metronome_init_gpio_pin(int pin, int dir) | ||
349 | { | ||
350 | u8 index; | ||
351 | /* dir 0 is output, 1 is input | ||
352 | - do 2 things here: | ||
353 | - set gpio alternate function to standard gpio | ||
354 | - set gpio direction to input or output */ | ||
355 | |||
356 | index = pin >> 4; | ||
357 | switch (index) { | ||
358 | case 1: | ||
359 | GAFR0_U &= ~(AF_SEL_GPIO_N << GAFR0_U_OFFSET(pin)); | ||
360 | |||
361 | if (dir) | ||
362 | GPDR0 &= ~(1 << pin); | ||
363 | else | ||
364 | GPDR0 |= (1 << pin); | ||
365 | break; | ||
366 | case 2: | ||
367 | GAFR1_L &= ~(AF_SEL_GPIO_N << GAFR1_L_OFFSET(pin)); | ||
368 | |||
369 | if (dir) | ||
370 | GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); | ||
371 | else | ||
372 | GPDR1 |= (1 << GPDR1_OFFSET(pin)); | ||
373 | break; | ||
374 | case 3: | ||
375 | GAFR1_U &= ~(AF_SEL_GPIO_N << GAFR1_U_OFFSET(pin)); | ||
376 | |||
377 | if (dir) | ||
378 | GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); | ||
379 | else | ||
380 | GPDR1 |= (1 << GPDR1_OFFSET(pin)); | ||
381 | break; | ||
382 | default: | ||
383 | printk(KERN_ERR "unimplemented\n"); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | static void __devinit metronome_init_gpio_regs(void) | ||
388 | { | ||
389 | metronome_init_gpio_pin(LED_GPIO_PIN, 0); | ||
390 | metronome_set_gpio_output(LED_GPIO_PIN, 0); | ||
391 | |||
392 | metronome_init_gpio_pin(STDBY_GPIO_PIN, 0); | ||
393 | metronome_set_gpio_output(STDBY_GPIO_PIN, 0); | ||
394 | |||
395 | metronome_init_gpio_pin(RST_GPIO_PIN, 0); | ||
396 | metronome_set_gpio_output(RST_GPIO_PIN, 0); | ||
397 | |||
398 | metronome_init_gpio_pin(RDY_GPIO_PIN, 1); | ||
399 | |||
400 | metronome_init_gpio_pin(ERR_GPIO_PIN, 1); | ||
401 | |||
402 | metronome_init_gpio_pin(PCBPWR_GPIO_PIN, 0); | ||
403 | metronome_set_gpio_output(PCBPWR_GPIO_PIN, 0); | ||
404 | } | ||
405 | |||
406 | static void metronome_disable_lcd_controller(struct metronomefb_par *par) | ||
407 | { | ||
408 | LCSR = 0xffffffff; /* Clear LCD Status Register */ | ||
409 | LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ | ||
410 | |||
411 | /* we reset and just wait for things to settle */ | ||
412 | msleep(200); | ||
413 | } | ||
414 | |||
415 | static void metronome_enable_lcd_controller(struct metronomefb_par *par) | ||
416 | { | ||
417 | LCSR = 0xffffffff; | ||
418 | FDADR0 = par->metromem_desc_dma; | ||
419 | LCCR0 |= LCCR0_ENB; | ||
420 | } | ||
421 | |||
422 | static void __devinit metronome_init_lcdc_regs(struct metronomefb_par *par) | ||
423 | { | ||
424 | /* here we do: | ||
425 | - disable the lcd controller | ||
426 | - setup lcd control registers | ||
427 | - setup dma descriptor | ||
428 | - reenable lcd controller | ||
429 | */ | ||
430 | |||
431 | /* disable the lcd controller */ | ||
432 | metronome_disable_lcd_controller(par); | ||
433 | |||
434 | /* setup lcd control registers */ | ||
435 | LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS | ||
436 | | LCCR0_QDM | LCCR0_BM | LCCR0_OUM; | ||
437 | |||
438 | LCCR1 = (epd_frame_table[0].fw/2 - 1) /* pixels per line */ | ||
439 | | (27 << 10) /* hsync pulse width - 1 */ | ||
440 | | (33 << 16) /* eol pixel count */ | ||
441 | | (33 << 24); /* bol pixel count */ | ||
442 | |||
443 | LCCR2 = (epd_frame_table[0].fh - 1) /* lines per panel */ | ||
444 | | (24 << 10) /* vsync pulse width - 1 */ | ||
445 | | (2 << 16) /* eof pixel count */ | ||
446 | | (0 << 24); /* bof pixel count */ | ||
447 | |||
448 | LCCR3 = 2 /* pixel clock divisor */ | ||
449 | | (24 << 8) /* AC Bias pin freq */ | ||
450 | | LCCR3_16BPP /* BPP */ | ||
451 | | LCCR3_PCP; /* PCP falling edge */ | ||
452 | |||
453 | /* setup dma descriptor */ | ||
454 | par->metromem_desc->mFDADR0 = par->metromem_desc_dma; | ||
455 | par->metromem_desc->mFSADR0 = par->metromem_dma; | ||
456 | par->metromem_desc->mFIDR0 = 0; | ||
457 | par->metromem_desc->mLDCMD0 = epd_frame_table[0].fw | ||
458 | * epd_frame_table[0].fh; | ||
459 | /* reenable lcd controller */ | ||
460 | metronome_enable_lcd_controller(par); | ||
461 | } | ||
462 | |||
463 | static int metronome_display_cmd(struct metronomefb_par *par) | ||
464 | { | ||
465 | int i; | ||
466 | u16 cs; | ||
467 | u16 opcode; | ||
468 | static u8 borderval; | ||
469 | u8 *ptr; | ||
470 | |||
471 | /* setup display command | ||
472 | we can't immediately set the opcode since the controller | ||
473 | will try parse the command before we've set it all up | ||
474 | so we just set cs here and set the opcode at the end */ | ||
475 | |||
476 | ptr = par->metromem; | ||
477 | |||
478 | if (par->metromem_cmd->opcode == 0xCC40) | ||
479 | opcode = cs = 0xCC41; | ||
480 | else | ||
481 | opcode = cs = 0xCC40; | ||
482 | |||
483 | /* set the args ( 2 bytes ) for display */ | ||
484 | i = 0; | ||
485 | par->metromem_cmd->args[i] = 1 << 3 /* border update */ | ||
486 | | ((borderval++ % 4) & 0x0F) << 4 | ||
487 | | (par->frame_count - 1) << 8; | ||
488 | cs += par->metromem_cmd->args[i++]; | ||
489 | |||
490 | /* the rest are 0 */ | ||
491 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | ||
492 | |||
493 | par->metromem_cmd->csum = cs; | ||
494 | par->metromem_cmd->opcode = opcode; /* display cmd */ | ||
495 | |||
496 | i = wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ); | ||
497 | return i; | ||
498 | } | ||
499 | |||
500 | static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) | ||
501 | { | ||
502 | int i; | ||
503 | u16 cs; | ||
504 | |||
505 | /* setup power up command */ | ||
506 | par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */ | ||
507 | cs = par->metromem_cmd->opcode; | ||
508 | |||
509 | /* set pwr1,2,3 to 1024 */ | ||
510 | for (i = 0; i < 3; i++) { | ||
511 | par->metromem_cmd->args[i] = 1024; | ||
512 | cs += par->metromem_cmd->args[i]; | ||
513 | } | ||
514 | |||
515 | /* the rest are 0 */ | ||
516 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | ||
517 | |||
518 | par->metromem_cmd->csum = cs; | ||
519 | |||
520 | msleep(1); | ||
521 | metronome_set_gpio_output(RST_GPIO_PIN, 1); | ||
522 | |||
523 | msleep(1); | ||
524 | metronome_set_gpio_output(STDBY_GPIO_PIN, 1); | ||
525 | |||
526 | i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | ||
527 | return i; | ||
528 | } | ||
529 | |||
530 | static int __devinit metronome_config_cmd(struct metronomefb_par *par) | ||
531 | { | ||
532 | int i; | ||
533 | u16 cs; | ||
534 | |||
535 | /* setup config command | ||
536 | we can't immediately set the opcode since the controller | ||
537 | will try parse the command before we've set it all up | ||
538 | so we just set cs here and set the opcode at the end */ | ||
539 | |||
540 | cs = 0xCC10; | ||
541 | |||
542 | /* set the 12 args ( 8 bytes ) for config. see spec for meanings */ | ||
543 | i = 0; | ||
544 | par->metromem_cmd->args[i] = 15 /* sdlew */ | ||
545 | | 2 << 8 /* sdosz */ | ||
546 | | 0 << 11 /* sdor */ | ||
547 | | 0 << 12 /* sdces */ | ||
548 | | 0 << 15; /* sdcer */ | ||
549 | cs += par->metromem_cmd->args[i++]; | ||
550 | |||
551 | par->metromem_cmd->args[i] = 42 /* gdspl */ | ||
552 | | 1 << 8 /* gdr1 */ | ||
553 | | 1 << 9 /* sdshr */ | ||
554 | | 0 << 15; /* gdspp */ | ||
555 | cs += par->metromem_cmd->args[i++]; | ||
556 | |||
557 | par->metromem_cmd->args[i] = 18 /* gdspw */ | ||
558 | | 0 << 15; /* dispc */ | ||
559 | cs += par->metromem_cmd->args[i++]; | ||
560 | |||
561 | par->metromem_cmd->args[i] = 599 /* vdlc */ | ||
562 | | 0 << 11 /* dsi */ | ||
563 | | 0 << 12; /* dsic */ | ||
564 | cs += par->metromem_cmd->args[i++]; | ||
565 | |||
566 | /* the rest are 0 */ | ||
567 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | ||
568 | |||
569 | par->metromem_cmd->csum = cs; | ||
570 | par->metromem_cmd->opcode = 0xCC10; /* config cmd */ | ||
571 | |||
572 | i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | ||
573 | return i; | ||
574 | } | ||
575 | |||
576 | static int __devinit metronome_init_cmd(struct metronomefb_par *par) | ||
577 | { | ||
578 | int i; | ||
579 | u16 cs; | ||
580 | |||
581 | /* setup init command | ||
582 | we can't immediately set the opcode since the controller | ||
583 | will try parse the command before we've set it all up | ||
584 | so we just set cs here and set the opcode at the end */ | ||
585 | |||
586 | cs = 0xCC20; | ||
587 | |||
588 | /* set the args ( 2 bytes ) for init */ | ||
589 | i = 0; | ||
590 | par->metromem_cmd->args[i] = 0; | ||
591 | cs += par->metromem_cmd->args[i++]; | ||
592 | |||
593 | /* the rest are 0 */ | ||
594 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | ||
595 | |||
596 | par->metromem_cmd->csum = cs; | ||
597 | par->metromem_cmd->opcode = 0xCC20; /* init cmd */ | ||
598 | |||
599 | i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | ||
600 | return i; | ||
601 | } | ||
602 | |||
603 | static int __devinit metronome_init_regs(struct metronomefb_par *par) | ||
604 | { | ||
605 | int res; | ||
606 | |||
607 | metronome_init_gpio_regs(); | ||
608 | metronome_init_lcdc_regs(par); | ||
609 | |||
610 | res = metronome_powerup_cmd(par); | ||
611 | if (res) | ||
612 | return res; | ||
613 | |||
614 | res = metronome_config_cmd(par); | ||
615 | if (res) | ||
616 | return res; | ||
617 | |||
618 | res = metronome_init_cmd(par); | ||
619 | if (res) | ||
620 | return res; | ||
621 | |||
622 | return res; | ||
623 | } | ||
624 | |||
625 | static void metronomefb_dpy_update(struct metronomefb_par *par) | ||
626 | { | ||
627 | u16 cksum; | ||
628 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; | ||
629 | |||
630 | /* copy from vm to metromem */ | ||
631 | memcpy(par->metromem_img, buf, DPY_W*DPY_H); | ||
632 | |||
633 | cksum = calc_img_cksum((u16 *) par->metromem_img, | ||
634 | (epd_frame_table[0].fw * DPY_H)/2); | ||
635 | *((u16 *) (par->metromem_img) + | ||
636 | (epd_frame_table[0].fw * DPY_H)/2) = cksum; | ||
637 | metronome_display_cmd(par); | ||
638 | } | ||
639 | |||
640 | static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index) | ||
641 | { | ||
642 | int i; | ||
643 | u16 csum = 0; | ||
644 | u16 *buf = (u16 __force *) (par->info->screen_base + index); | ||
645 | u16 *img = (u16 *) (par->metromem_img + index); | ||
646 | |||
647 | /* swizzle from vm to metromem and recalc cksum at the same time*/ | ||
648 | for (i = 0; i < PAGE_SIZE/2; i++) { | ||
649 | *(img + i) = (buf[i] << 5) & 0xE0E0; | ||
650 | csum += *(img + i); | ||
651 | } | ||
652 | return csum; | ||
653 | } | ||
654 | |||
655 | /* this is called back from the deferred io workqueue */ | ||
656 | static void metronomefb_dpy_deferred_io(struct fb_info *info, | ||
657 | struct list_head *pagelist) | ||
658 | { | ||
659 | u16 cksum; | ||
660 | struct page *cur; | ||
661 | struct fb_deferred_io *fbdefio = info->fbdefio; | ||
662 | struct metronomefb_par *par = info->par; | ||
663 | |||
664 | /* walk the written page list and swizzle the data */ | ||
665 | list_for_each_entry(cur, &fbdefio->pagelist, lru) { | ||
666 | cksum = metronomefb_dpy_update_page(par, | ||
667 | (cur->index << PAGE_SHIFT)); | ||
668 | par->metromem_img_csum -= par->csum_table[cur->index]; | ||
669 | par->csum_table[cur->index] = cksum; | ||
670 | par->metromem_img_csum += cksum; | ||
671 | } | ||
672 | |||
673 | metronome_display_cmd(par); | ||
674 | } | ||
675 | |||
676 | static void metronomefb_fillrect(struct fb_info *info, | ||
677 | const struct fb_fillrect *rect) | ||
678 | { | ||
679 | struct metronomefb_par *par = info->par; | ||
680 | |||
681 | cfb_fillrect(info, rect); | ||
682 | metronomefb_dpy_update(par); | ||
683 | } | ||
684 | |||
685 | static void metronomefb_copyarea(struct fb_info *info, | ||
686 | const struct fb_copyarea *area) | ||
687 | { | ||
688 | struct metronomefb_par *par = info->par; | ||
689 | |||
690 | cfb_copyarea(info, area); | ||
691 | metronomefb_dpy_update(par); | ||
692 | } | ||
693 | |||
694 | static void metronomefb_imageblit(struct fb_info *info, | ||
695 | const struct fb_image *image) | ||
696 | { | ||
697 | struct metronomefb_par *par = info->par; | ||
698 | |||
699 | cfb_imageblit(info, image); | ||
700 | metronomefb_dpy_update(par); | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * this is the slow path from userspace. they can seek and write to | ||
705 | * the fb. it is based on fb_sys_write | ||
706 | */ | ||
707 | static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf, | ||
708 | size_t count, loff_t *ppos) | ||
709 | { | ||
710 | struct metronomefb_par *par = info->par; | ||
711 | unsigned long p = *ppos; | ||
712 | void *dst; | ||
713 | int err = 0; | ||
714 | unsigned long total_size; | ||
715 | |||
716 | if (info->state != FBINFO_STATE_RUNNING) | ||
717 | return -EPERM; | ||
718 | |||
719 | total_size = info->fix.smem_len; | ||
720 | |||
721 | if (p > total_size) | ||
722 | return -EFBIG; | ||
723 | |||
724 | if (count > total_size) { | ||
725 | err = -EFBIG; | ||
726 | count = total_size; | ||
727 | } | ||
728 | |||
729 | if (count + p > total_size) { | ||
730 | if (!err) | ||
731 | err = -ENOSPC; | ||
732 | |||
733 | count = total_size - p; | ||
734 | } | ||
735 | |||
736 | dst = (void __force *) (info->screen_base + p); | ||
737 | |||
738 | if (copy_from_user(dst, buf, count)) | ||
739 | err = -EFAULT; | ||
740 | |||
741 | if (!err) | ||
742 | *ppos += count; | ||
743 | |||
744 | metronomefb_dpy_update(par); | ||
745 | |||
746 | return (err) ? err : count; | ||
747 | } | ||
748 | |||
749 | static struct fb_ops metronomefb_ops = { | ||
750 | .owner = THIS_MODULE, | ||
751 | .fb_write = metronomefb_write, | ||
752 | .fb_fillrect = metronomefb_fillrect, | ||
753 | .fb_copyarea = metronomefb_copyarea, | ||
754 | .fb_imageblit = metronomefb_imageblit, | ||
755 | }; | ||
756 | |||
757 | static struct fb_deferred_io metronomefb_defio = { | ||
758 | .delay = HZ, | ||
759 | .deferred_io = metronomefb_dpy_deferred_io, | ||
760 | }; | ||
761 | |||
762 | static irqreturn_t metronome_handle_irq(int irq, void *dev_id) | ||
763 | { | ||
764 | struct fb_info *info = dev_id; | ||
765 | struct metronomefb_par *par = info->par; | ||
766 | |||
767 | wake_up_interruptible(&par->waitq); | ||
768 | return IRQ_HANDLED; | ||
769 | } | ||
770 | |||
771 | static int __devinit metronomefb_probe(struct platform_device *dev) | ||
772 | { | ||
773 | struct fb_info *info; | ||
774 | int retval = -ENOMEM; | ||
775 | int videomemorysize; | ||
776 | unsigned char *videomemory; | ||
777 | struct metronomefb_par *par; | ||
778 | const struct firmware *fw_entry; | ||
779 | int cmd_size, wfm_size, img_size, padding_size, totalsize; | ||
780 | int i; | ||
781 | |||
782 | /* we have two blocks of memory. | ||
783 | info->screen_base which is vm, and is the fb used by apps. | ||
784 | par->metromem which is physically contiguous memory and | ||
785 | contains the display controller commands, waveform, | ||
786 | processed image data and padding. this is the data pulled | ||
787 | by the pxa255's LCD controller and pushed to Metronome */ | ||
788 | |||
789 | videomemorysize = (DPY_W*DPY_H); | ||
790 | videomemory = vmalloc(videomemorysize); | ||
791 | if (!videomemory) | ||
792 | return retval; | ||
793 | |||
794 | memset(videomemory, 0, videomemorysize); | ||
795 | |||
796 | info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); | ||
797 | if (!info) | ||
798 | goto err_vfree; | ||
799 | |||
800 | info->screen_base = (char __iomem *) videomemory; | ||
801 | info->fbops = &metronomefb_ops; | ||
802 | |||
803 | info->var = metronomefb_var; | ||
804 | info->fix = metronomefb_fix; | ||
805 | info->fix.smem_len = videomemorysize; | ||
806 | par = info->par; | ||
807 | par->info = info; | ||
808 | init_waitqueue_head(&par->waitq); | ||
809 | |||
810 | /* this table caches per page csum values. */ | ||
811 | par->csum_table = vmalloc(videomemorysize/PAGE_SIZE); | ||
812 | if (!par->csum_table) | ||
813 | goto err_csum_table; | ||
814 | |||
815 | /* the metromem buffer is divided as follows: | ||
816 | command | CRC | padding | ||
817 | 16kb waveform data | CRC | padding | ||
818 | image data | CRC | ||
819 | and an extra 256 bytes for dma descriptors | ||
820 | eg: IW=832 IH=622 WS=128 | ||
821 | */ | ||
822 | |||
823 | cmd_size = 1 * epd_frame_table[0].fw; | ||
824 | wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1) | ||
825 | / epd_frame_table[0].fw) * epd_frame_table[0].fw; | ||
826 | img_size = epd_frame_table[0].fh * epd_frame_table[0].fw; | ||
827 | padding_size = 4 * epd_frame_table[0].fw; | ||
828 | totalsize = cmd_size + wfm_size + img_size + padding_size; | ||
829 | par->metromemsize = PAGE_ALIGN(totalsize + 256); | ||
830 | DPRINTK("desired memory size = %d\n", par->metromemsize); | ||
831 | dev->dev.coherent_dma_mask = 0xffffffffull; | ||
832 | par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize, | ||
833 | &par->metromem_dma, GFP_KERNEL); | ||
834 | if (!par->metromem) { | ||
835 | printk(KERN_ERR | ||
836 | "metronomefb: unable to allocate dma buffer\n"); | ||
837 | goto err_vfree; | ||
838 | } | ||
839 | |||
840 | info->fix.smem_start = par->metromem_dma; | ||
841 | par->metromem_cmd = (struct metromem_cmd *) par->metromem; | ||
842 | par->metromem_wfm = par->metromem + cmd_size; | ||
843 | par->metromem_img = par->metromem + cmd_size + wfm_size; | ||
844 | par->metromem_img_csum = (u16 *) (par->metromem_img + | ||
845 | (epd_frame_table[0].fw * DPY_H)); | ||
846 | DPRINTK("img offset=0x%x\n", cmd_size + wfm_size); | ||
847 | par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size | ||
848 | + wfm_size + img_size + padding_size); | ||
849 | par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size | ||
850 | + img_size + padding_size; | ||
851 | |||
852 | /* load the waveform in. assume mode 3, temp 31 for now */ | ||
853 | /* a) request the waveform file from userspace | ||
854 | b) process waveform and decode into metromem */ | ||
855 | |||
856 | retval = request_firmware(&fw_entry, "waveform.wbf", &dev->dev); | ||
857 | if (retval < 0) { | ||
858 | printk(KERN_ERR "metronomefb: couldn't get waveform\n"); | ||
859 | goto err_dma_free; | ||
860 | } | ||
861 | |||
862 | retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, | ||
863 | par->metromem_wfm, 3, 31, &par->frame_count); | ||
864 | if (retval < 0) { | ||
865 | printk(KERN_ERR "metronomefb: couldn't process waveform\n"); | ||
866 | goto err_ld_wfm; | ||
867 | } | ||
868 | release_firmware(fw_entry); | ||
869 | |||
870 | retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), metronome_handle_irq, | ||
871 | IRQF_DISABLED, "Metronome", info); | ||
872 | if (retval) { | ||
873 | dev_err(&dev->dev, "request_irq failed: %d\n", retval); | ||
874 | goto err_ld_wfm; | ||
875 | } | ||
876 | set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQT_FALLING); | ||
877 | |||
878 | retval = metronome_init_regs(par); | ||
879 | if (retval < 0) | ||
880 | goto err_free_irq; | ||
881 | |||
882 | info->flags = FBINFO_FLAG_DEFAULT; | ||
883 | |||
884 | info->fbdefio = &metronomefb_defio; | ||
885 | fb_deferred_io_init(info); | ||
886 | |||
887 | retval = fb_alloc_cmap(&info->cmap, 8, 0); | ||
888 | if (retval < 0) { | ||
889 | printk(KERN_ERR "Failed to allocate colormap\n"); | ||
890 | goto err_fb_rel; | ||
891 | } | ||
892 | |||
893 | /* set cmap */ | ||
894 | for (i = 0; i < 8; i++) | ||
895 | info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16; | ||
896 | memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8); | ||
897 | memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8); | ||
898 | |||
899 | retval = register_framebuffer(info); | ||
900 | if (retval < 0) | ||
901 | goto err_cmap; | ||
902 | |||
903 | platform_set_drvdata(dev, info); | ||
904 | |||
905 | printk(KERN_INFO | ||
906 | "fb%d: Metronome frame buffer device, using %dK of video" | ||
907 | " memory\n", info->node, videomemorysize >> 10); | ||
908 | |||
909 | return 0; | ||
910 | |||
911 | err_cmap: | ||
912 | fb_dealloc_cmap(&info->cmap); | ||
913 | err_fb_rel: | ||
914 | framebuffer_release(info); | ||
915 | err_free_irq: | ||
916 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); | ||
917 | err_ld_wfm: | ||
918 | release_firmware(fw_entry); | ||
919 | err_dma_free: | ||
920 | dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem, | ||
921 | par->metromem_dma); | ||
922 | err_csum_table: | ||
923 | vfree(par->csum_table); | ||
924 | err_vfree: | ||
925 | vfree(videomemory); | ||
926 | return retval; | ||
927 | } | ||
928 | |||
929 | static int __devexit metronomefb_remove(struct platform_device *dev) | ||
930 | { | ||
931 | struct fb_info *info = platform_get_drvdata(dev); | ||
932 | |||
933 | if (info) { | ||
934 | struct metronomefb_par *par = info->par; | ||
935 | fb_deferred_io_cleanup(info); | ||
936 | dma_free_writecombine(&dev->dev, par->metromemsize, | ||
937 | par->metromem, par->metromem_dma); | ||
938 | fb_dealloc_cmap(&info->cmap); | ||
939 | vfree(par->csum_table); | ||
940 | unregister_framebuffer(info); | ||
941 | vfree((void __force *)info->screen_base); | ||
942 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); | ||
943 | framebuffer_release(info); | ||
944 | } | ||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static struct platform_driver metronomefb_driver = { | ||
949 | .probe = metronomefb_probe, | ||
950 | .remove = metronomefb_remove, | ||
951 | .driver = { | ||
952 | .name = "metronomefb", | ||
953 | }, | ||
954 | }; | ||
955 | |||
956 | static struct platform_device *metronomefb_device; | ||
957 | |||
958 | static int __init metronomefb_init(void) | ||
959 | { | ||
960 | int ret; | ||
961 | |||
962 | if (!metronomefb_enable) { | ||
963 | printk(KERN_ERR | ||
964 | "Use metronomefb_enable to enable the device\n"); | ||
965 | return -ENXIO; | ||
966 | } | ||
967 | |||
968 | ret = platform_driver_register(&metronomefb_driver); | ||
969 | if (!ret) { | ||
970 | metronomefb_device = platform_device_alloc("metronomefb", 0); | ||
971 | if (metronomefb_device) | ||
972 | ret = platform_device_add(metronomefb_device); | ||
973 | else | ||
974 | ret = -ENOMEM; | ||
975 | |||
976 | if (ret) { | ||
977 | platform_device_put(metronomefb_device); | ||
978 | platform_driver_unregister(&metronomefb_driver); | ||
979 | } | ||
980 | } | ||
981 | return ret; | ||
982 | |||
983 | } | ||
984 | |||
985 | static void __exit metronomefb_exit(void) | ||
986 | { | ||
987 | platform_device_unregister(metronomefb_device); | ||
988 | platform_driver_unregister(&metronomefb_driver); | ||
989 | } | ||
990 | |||
991 | module_param(metronomefb_enable, uint, 0); | ||
992 | MODULE_PARM_DESC(metronomefb_enable, "Enable communication with Metronome"); | ||
993 | |||
994 | module_init(metronomefb_init); | ||
995 | module_exit(metronomefb_exit); | ||
996 | |||
997 | MODULE_DESCRIPTION("fbdev driver for Metronome controller"); | ||
998 | MODULE_AUTHOR("Jaya Kumar"); | ||
999 | MODULE_LICENSE("GPL"); | ||