]>
Commit | Line | Data |
---|---|---|
de7c6d15 JK |
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"); |