]>
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. | |
03c33a4f JK |
16 | * It is intended to be architecture independent. A board specific driver |
17 | * must be used to perform all the physical IO interactions. An example | |
18 | * is provided as am200epd.c | |
de7c6d15 | 19 | * |
de7c6d15 JK |
20 | */ |
21 | #include <linux/module.h> | |
22 | #include <linux/kernel.h> | |
23 | #include <linux/errno.h> | |
24 | #include <linux/string.h> | |
25 | #include <linux/mm.h> | |
26 | #include <linux/slab.h> | |
27 | #include <linux/vmalloc.h> | |
28 | #include <linux/delay.h> | |
29 | #include <linux/interrupt.h> | |
30 | #include <linux/fb.h> | |
31 | #include <linux/init.h> | |
32 | #include <linux/platform_device.h> | |
33 | #include <linux/list.h> | |
34 | #include <linux/firmware.h> | |
35 | #include <linux/dma-mapping.h> | |
36 | #include <linux/uaccess.h> | |
37 | #include <linux/irq.h> | |
38 | ||
03c33a4f JK |
39 | #include <video/metronomefb.h> |
40 | ||
de7c6d15 JK |
41 | #include <asm/unaligned.h> |
42 | ||
03c33a4f | 43 | |
de7c6d15 JK |
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 | ||
de7c6d15 JK |
56 | /* frame differs from image. frame includes non-visible pixels */ |
57 | struct epd_frame { | |
58 | int fw; /* frame width */ | |
59 | int fh; /* frame height */ | |
60 | }; | |
61 | ||
62 | static struct epd_frame epd_frame_table[] = { | |
63 | { | |
64 | .fw = 832, | |
65 | .fh = 622 | |
66 | }, | |
67 | }; | |
68 | ||
69 | static struct fb_fix_screeninfo metronomefb_fix __devinitdata = { | |
70 | .id = "metronomefb", | |
71 | .type = FB_TYPE_PACKED_PIXELS, | |
72 | .visual = FB_VISUAL_STATIC_PSEUDOCOLOR, | |
73 | .xpanstep = 0, | |
74 | .ypanstep = 0, | |
75 | .ywrapstep = 0, | |
76 | .line_length = DPY_W, | |
77 | .accel = FB_ACCEL_NONE, | |
78 | }; | |
79 | ||
80 | static struct fb_var_screeninfo metronomefb_var __devinitdata = { | |
81 | .xres = DPY_W, | |
82 | .yres = DPY_H, | |
83 | .xres_virtual = DPY_W, | |
84 | .yres_virtual = DPY_H, | |
85 | .bits_per_pixel = 8, | |
86 | .grayscale = 1, | |
87 | .nonstd = 1, | |
88 | .red = { 4, 3, 0 }, | |
89 | .green = { 0, 0, 0 }, | |
90 | .blue = { 0, 0, 0 }, | |
91 | .transp = { 0, 0, 0 }, | |
92 | }; | |
93 | ||
03c33a4f | 94 | /* the waveform structure that is coming from userspace firmware */ |
de7c6d15 JK |
95 | struct waveform_hdr { |
96 | u8 stuff[32]; | |
97 | ||
98 | u8 wmta[3]; | |
99 | u8 fvsn; | |
100 | ||
101 | u8 luts; | |
102 | u8 mc; | |
103 | u8 trc; | |
104 | u8 stuff3; | |
105 | ||
106 | u8 endb; | |
107 | u8 swtb; | |
108 | u8 stuff2a[2]; | |
109 | ||
110 | u8 stuff2b[3]; | |
111 | u8 wfm_cs; | |
112 | } __attribute__ ((packed)); | |
113 | ||
114 | /* main metronomefb functions */ | |
115 | static u8 calc_cksum(int start, int end, u8 *mem) | |
116 | { | |
117 | u8 tmp = 0; | |
118 | int i; | |
119 | ||
120 | for (i = start; i < end; i++) | |
121 | tmp += mem[i]; | |
122 | ||
123 | return tmp; | |
124 | } | |
125 | ||
126 | static u16 calc_img_cksum(u16 *start, int length) | |
127 | { | |
128 | u16 tmp = 0; | |
129 | ||
130 | while (length--) | |
131 | tmp += *start++; | |
132 | ||
133 | return tmp; | |
134 | } | |
135 | ||
136 | /* here we decode the incoming waveform file and populate metromem */ | |
137 | #define EXP_WFORM_SIZE 47001 | |
138 | static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, | |
139 | u8 *frame_count) | |
140 | { | |
141 | int tta; | |
142 | int wmta; | |
143 | int trn = 0; | |
144 | int i; | |
145 | unsigned char v; | |
146 | u8 cksum; | |
147 | int cksum_idx; | |
148 | int wfm_idx, owfm_idx; | |
149 | int mem_idx = 0; | |
150 | struct waveform_hdr *wfm_hdr; | |
151 | ||
152 | if (size != EXP_WFORM_SIZE) { | |
153 | printk(KERN_ERR "Error: unexpected size %d != %d\n", size, | |
154 | EXP_WFORM_SIZE); | |
155 | return -EINVAL; | |
156 | } | |
157 | ||
158 | wfm_hdr = (struct waveform_hdr *) mem; | |
159 | ||
160 | if (wfm_hdr->fvsn != 1) { | |
161 | printk(KERN_ERR "Error: bad fvsn %x\n", wfm_hdr->fvsn); | |
162 | return -EINVAL; | |
163 | } | |
164 | if (wfm_hdr->luts != 0) { | |
165 | printk(KERN_ERR "Error: bad luts %x\n", wfm_hdr->luts); | |
166 | return -EINVAL; | |
167 | } | |
168 | cksum = calc_cksum(32, 47, mem); | |
169 | if (cksum != wfm_hdr->wfm_cs) { | |
170 | printk(KERN_ERR "Error: bad cksum %x != %x\n", cksum, | |
171 | wfm_hdr->wfm_cs); | |
172 | return -EINVAL; | |
173 | } | |
174 | wfm_hdr->mc += 1; | |
175 | wfm_hdr->trc += 1; | |
176 | for (i = 0; i < 5; i++) { | |
177 | if (*(wfm_hdr->stuff2a + i) != 0) { | |
178 | printk(KERN_ERR "Error: unexpected value in padding\n"); | |
179 | return -EINVAL; | |
180 | } | |
181 | } | |
182 | ||
183 | /* calculating trn. trn is something used to index into | |
184 | the waveform. presumably selecting the right one for the | |
185 | desired temperature. it works out the offset of the first | |
186 | v that exceeds the specified temperature */ | |
187 | if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size) | |
188 | return -EINVAL; | |
189 | ||
190 | for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) { | |
191 | if (mem[i] > t) { | |
192 | trn = i - sizeof(*wfm_hdr) - 1; | |
193 | break; | |
194 | } | |
195 | } | |
196 | ||
197 | /* check temperature range table checksum */ | |
198 | cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1; | |
199 | if (cksum_idx > size) | |
200 | return -EINVAL; | |
201 | cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem); | |
202 | if (cksum != mem[cksum_idx]) { | |
203 | printk(KERN_ERR "Error: bad temperature range table cksum" | |
204 | " %x != %x\n", cksum, mem[cksum_idx]); | |
205 | return -EINVAL; | |
206 | } | |
207 | ||
208 | /* check waveform mode table address checksum */ | |
209 | wmta = le32_to_cpu(get_unaligned((__le32 *) wfm_hdr->wmta)); | |
210 | wmta &= 0x00FFFFFF; | |
211 | cksum_idx = wmta + m*4 + 3; | |
212 | if (cksum_idx > size) | |
213 | return -EINVAL; | |
214 | cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); | |
215 | if (cksum != mem[cksum_idx]) { | |
216 | printk(KERN_ERR "Error: bad mode table address cksum" | |
217 | " %x != %x\n", cksum, mem[cksum_idx]); | |
218 | return -EINVAL; | |
219 | } | |
220 | ||
221 | /* check waveform temperature table address checksum */ | |
222 | tta = le32_to_cpu(get_unaligned((int *) (mem + wmta + m*4))); | |
223 | tta &= 0x00FFFFFF; | |
224 | cksum_idx = tta + trn*4 + 3; | |
225 | if (cksum_idx > size) | |
226 | return -EINVAL; | |
227 | cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); | |
228 | if (cksum != mem[cksum_idx]) { | |
229 | printk(KERN_ERR "Error: bad temperature table address cksum" | |
230 | " %x != %x\n", cksum, mem[cksum_idx]); | |
231 | return -EINVAL; | |
232 | } | |
233 | ||
234 | /* here we do the real work of putting the waveform into the | |
235 | metromem buffer. this does runlength decoding of the waveform */ | |
236 | wfm_idx = le32_to_cpu(get_unaligned((__le32 *) (mem + tta + trn*4))); | |
237 | wfm_idx &= 0x00FFFFFF; | |
238 | owfm_idx = wfm_idx; | |
239 | if (wfm_idx > size) | |
240 | return -EINVAL; | |
241 | while (wfm_idx < size) { | |
242 | unsigned char rl; | |
243 | v = mem[wfm_idx++]; | |
244 | if (v == wfm_hdr->swtb) { | |
245 | while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) && | |
246 | wfm_idx < size) | |
247 | metromem[mem_idx++] = v; | |
248 | ||
249 | continue; | |
250 | } | |
251 | ||
252 | if (v == wfm_hdr->endb) | |
253 | break; | |
254 | ||
255 | rl = mem[wfm_idx++]; | |
256 | for (i = 0; i <= rl; i++) | |
257 | metromem[mem_idx++] = v; | |
258 | } | |
259 | ||
260 | cksum_idx = wfm_idx; | |
261 | if (cksum_idx > size) | |
262 | return -EINVAL; | |
263 | cksum = calc_cksum(owfm_idx, cksum_idx, mem); | |
264 | if (cksum != mem[cksum_idx]) { | |
265 | printk(KERN_ERR "Error: bad waveform data cksum" | |
266 | " %x != %x\n", cksum, mem[cksum_idx]); | |
267 | return -EINVAL; | |
268 | } | |
269 | *frame_count = (mem_idx/64); | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
de7c6d15 JK |
274 | static int metronome_display_cmd(struct metronomefb_par *par) |
275 | { | |
276 | int i; | |
277 | u16 cs; | |
278 | u16 opcode; | |
279 | static u8 borderval; | |
280 | u8 *ptr; | |
281 | ||
282 | /* setup display command | |
283 | we can't immediately set the opcode since the controller | |
284 | will try parse the command before we've set it all up | |
285 | so we just set cs here and set the opcode at the end */ | |
286 | ||
287 | ptr = par->metromem; | |
288 | ||
289 | if (par->metromem_cmd->opcode == 0xCC40) | |
290 | opcode = cs = 0xCC41; | |
291 | else | |
292 | opcode = cs = 0xCC40; | |
293 | ||
294 | /* set the args ( 2 bytes ) for display */ | |
295 | i = 0; | |
296 | par->metromem_cmd->args[i] = 1 << 3 /* border update */ | |
297 | | ((borderval++ % 4) & 0x0F) << 4 | |
298 | | (par->frame_count - 1) << 8; | |
299 | cs += par->metromem_cmd->args[i++]; | |
300 | ||
301 | /* the rest are 0 */ | |
302 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | |
303 | ||
304 | par->metromem_cmd->csum = cs; | |
305 | par->metromem_cmd->opcode = opcode; /* display cmd */ | |
306 | ||
03c33a4f | 307 | return par->board->met_wait_event_intr(par); |
de7c6d15 JK |
308 | } |
309 | ||
310 | static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) | |
311 | { | |
312 | int i; | |
313 | u16 cs; | |
314 | ||
315 | /* setup power up command */ | |
316 | par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */ | |
317 | cs = par->metromem_cmd->opcode; | |
318 | ||
319 | /* set pwr1,2,3 to 1024 */ | |
320 | for (i = 0; i < 3; i++) { | |
321 | par->metromem_cmd->args[i] = 1024; | |
322 | cs += par->metromem_cmd->args[i]; | |
323 | } | |
324 | ||
325 | /* the rest are 0 */ | |
326 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | |
327 | ||
328 | par->metromem_cmd->csum = cs; | |
329 | ||
330 | msleep(1); | |
03c33a4f | 331 | par->board->set_rst(par, 1); |
de7c6d15 JK |
332 | |
333 | msleep(1); | |
03c33a4f | 334 | par->board->set_stdby(par, 1); |
de7c6d15 | 335 | |
03c33a4f | 336 | return par->board->met_wait_event(par); |
de7c6d15 JK |
337 | } |
338 | ||
339 | static int __devinit metronome_config_cmd(struct metronomefb_par *par) | |
340 | { | |
341 | int i; | |
342 | u16 cs; | |
343 | ||
344 | /* setup config command | |
345 | we can't immediately set the opcode since the controller | |
346 | will try parse the command before we've set it all up | |
347 | so we just set cs here and set the opcode at the end */ | |
348 | ||
349 | cs = 0xCC10; | |
350 | ||
351 | /* set the 12 args ( 8 bytes ) for config. see spec for meanings */ | |
352 | i = 0; | |
353 | par->metromem_cmd->args[i] = 15 /* sdlew */ | |
354 | | 2 << 8 /* sdosz */ | |
355 | | 0 << 11 /* sdor */ | |
356 | | 0 << 12 /* sdces */ | |
357 | | 0 << 15; /* sdcer */ | |
358 | cs += par->metromem_cmd->args[i++]; | |
359 | ||
360 | par->metromem_cmd->args[i] = 42 /* gdspl */ | |
361 | | 1 << 8 /* gdr1 */ | |
362 | | 1 << 9 /* sdshr */ | |
363 | | 0 << 15; /* gdspp */ | |
364 | cs += par->metromem_cmd->args[i++]; | |
365 | ||
366 | par->metromem_cmd->args[i] = 18 /* gdspw */ | |
367 | | 0 << 15; /* dispc */ | |
368 | cs += par->metromem_cmd->args[i++]; | |
369 | ||
370 | par->metromem_cmd->args[i] = 599 /* vdlc */ | |
371 | | 0 << 11 /* dsi */ | |
372 | | 0 << 12; /* dsic */ | |
373 | cs += par->metromem_cmd->args[i++]; | |
374 | ||
375 | /* the rest are 0 */ | |
376 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | |
377 | ||
378 | par->metromem_cmd->csum = cs; | |
379 | par->metromem_cmd->opcode = 0xCC10; /* config cmd */ | |
380 | ||
03c33a4f | 381 | return par->board->met_wait_event(par); |
de7c6d15 JK |
382 | } |
383 | ||
384 | static int __devinit metronome_init_cmd(struct metronomefb_par *par) | |
385 | { | |
386 | int i; | |
387 | u16 cs; | |
388 | ||
389 | /* setup init command | |
390 | we can't immediately set the opcode since the controller | |
391 | will try parse the command before we've set it all up | |
392 | so we just set cs here and set the opcode at the end */ | |
393 | ||
394 | cs = 0xCC20; | |
395 | ||
396 | /* set the args ( 2 bytes ) for init */ | |
397 | i = 0; | |
398 | par->metromem_cmd->args[i] = 0; | |
399 | cs += par->metromem_cmd->args[i++]; | |
400 | ||
401 | /* the rest are 0 */ | |
402 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | |
403 | ||
404 | par->metromem_cmd->csum = cs; | |
405 | par->metromem_cmd->opcode = 0xCC20; /* init cmd */ | |
406 | ||
03c33a4f | 407 | return par->board->met_wait_event(par); |
de7c6d15 JK |
408 | } |
409 | ||
410 | static int __devinit metronome_init_regs(struct metronomefb_par *par) | |
411 | { | |
412 | int res; | |
413 | ||
03c33a4f JK |
414 | par->board->init_gpio_regs(par); |
415 | ||
416 | par->board->init_lcdc_regs(par); | |
417 | ||
418 | /* now that lcd is setup, setup dma descriptor */ | |
419 | par->board->post_dma_setup(par); | |
de7c6d15 JK |
420 | |
421 | res = metronome_powerup_cmd(par); | |
422 | if (res) | |
423 | return res; | |
424 | ||
425 | res = metronome_config_cmd(par); | |
426 | if (res) | |
427 | return res; | |
428 | ||
429 | res = metronome_init_cmd(par); | |
de7c6d15 JK |
430 | |
431 | return res; | |
432 | } | |
433 | ||
434 | static void metronomefb_dpy_update(struct metronomefb_par *par) | |
435 | { | |
436 | u16 cksum; | |
437 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; | |
438 | ||
439 | /* copy from vm to metromem */ | |
440 | memcpy(par->metromem_img, buf, DPY_W*DPY_H); | |
441 | ||
442 | cksum = calc_img_cksum((u16 *) par->metromem_img, | |
443 | (epd_frame_table[0].fw * DPY_H)/2); | |
03c33a4f | 444 | *((u16 *)(par->metromem_img) + |
de7c6d15 JK |
445 | (epd_frame_table[0].fw * DPY_H)/2) = cksum; |
446 | metronome_display_cmd(par); | |
447 | } | |
448 | ||
449 | static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index) | |
450 | { | |
451 | int i; | |
452 | u16 csum = 0; | |
03c33a4f JK |
453 | u16 *buf = (u16 __force *)(par->info->screen_base + index); |
454 | u16 *img = (u16 *)(par->metromem_img + index); | |
de7c6d15 JK |
455 | |
456 | /* swizzle from vm to metromem and recalc cksum at the same time*/ | |
457 | for (i = 0; i < PAGE_SIZE/2; i++) { | |
458 | *(img + i) = (buf[i] << 5) & 0xE0E0; | |
459 | csum += *(img + i); | |
460 | } | |
461 | return csum; | |
462 | } | |
463 | ||
464 | /* this is called back from the deferred io workqueue */ | |
465 | static void metronomefb_dpy_deferred_io(struct fb_info *info, | |
466 | struct list_head *pagelist) | |
467 | { | |
468 | u16 cksum; | |
469 | struct page *cur; | |
470 | struct fb_deferred_io *fbdefio = info->fbdefio; | |
471 | struct metronomefb_par *par = info->par; | |
472 | ||
473 | /* walk the written page list and swizzle the data */ | |
474 | list_for_each_entry(cur, &fbdefio->pagelist, lru) { | |
475 | cksum = metronomefb_dpy_update_page(par, | |
476 | (cur->index << PAGE_SHIFT)); | |
477 | par->metromem_img_csum -= par->csum_table[cur->index]; | |
478 | par->csum_table[cur->index] = cksum; | |
479 | par->metromem_img_csum += cksum; | |
480 | } | |
481 | ||
482 | metronome_display_cmd(par); | |
483 | } | |
484 | ||
485 | static void metronomefb_fillrect(struct fb_info *info, | |
486 | const struct fb_fillrect *rect) | |
487 | { | |
488 | struct metronomefb_par *par = info->par; | |
489 | ||
555514fa | 490 | sys_fillrect(info, rect); |
de7c6d15 JK |
491 | metronomefb_dpy_update(par); |
492 | } | |
493 | ||
494 | static void metronomefb_copyarea(struct fb_info *info, | |
495 | const struct fb_copyarea *area) | |
496 | { | |
497 | struct metronomefb_par *par = info->par; | |
498 | ||
555514fa | 499 | sys_copyarea(info, area); |
de7c6d15 JK |
500 | metronomefb_dpy_update(par); |
501 | } | |
502 | ||
503 | static void metronomefb_imageblit(struct fb_info *info, | |
504 | const struct fb_image *image) | |
505 | { | |
506 | struct metronomefb_par *par = info->par; | |
507 | ||
555514fa | 508 | sys_imageblit(info, image); |
de7c6d15 JK |
509 | metronomefb_dpy_update(par); |
510 | } | |
511 | ||
512 | /* | |
513 | * this is the slow path from userspace. they can seek and write to | |
514 | * the fb. it is based on fb_sys_write | |
515 | */ | |
516 | static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf, | |
517 | size_t count, loff_t *ppos) | |
518 | { | |
519 | struct metronomefb_par *par = info->par; | |
520 | unsigned long p = *ppos; | |
521 | void *dst; | |
522 | int err = 0; | |
523 | unsigned long total_size; | |
524 | ||
525 | if (info->state != FBINFO_STATE_RUNNING) | |
526 | return -EPERM; | |
527 | ||
528 | total_size = info->fix.smem_len; | |
529 | ||
530 | if (p > total_size) | |
531 | return -EFBIG; | |
532 | ||
533 | if (count > total_size) { | |
534 | err = -EFBIG; | |
535 | count = total_size; | |
536 | } | |
537 | ||
538 | if (count + p > total_size) { | |
539 | if (!err) | |
540 | err = -ENOSPC; | |
541 | ||
542 | count = total_size - p; | |
543 | } | |
544 | ||
03c33a4f | 545 | dst = (void __force *)(info->screen_base + p); |
de7c6d15 JK |
546 | |
547 | if (copy_from_user(dst, buf, count)) | |
548 | err = -EFAULT; | |
549 | ||
550 | if (!err) | |
551 | *ppos += count; | |
552 | ||
553 | metronomefb_dpy_update(par); | |
554 | ||
555 | return (err) ? err : count; | |
556 | } | |
557 | ||
558 | static struct fb_ops metronomefb_ops = { | |
559 | .owner = THIS_MODULE, | |
560 | .fb_write = metronomefb_write, | |
561 | .fb_fillrect = metronomefb_fillrect, | |
562 | .fb_copyarea = metronomefb_copyarea, | |
563 | .fb_imageblit = metronomefb_imageblit, | |
564 | }; | |
565 | ||
566 | static struct fb_deferred_io metronomefb_defio = { | |
567 | .delay = HZ, | |
568 | .deferred_io = metronomefb_dpy_deferred_io, | |
569 | }; | |
570 | ||
de7c6d15 JK |
571 | static int __devinit metronomefb_probe(struct platform_device *dev) |
572 | { | |
573 | struct fb_info *info; | |
03c33a4f | 574 | struct metronome_board *board; |
de7c6d15 JK |
575 | int retval = -ENOMEM; |
576 | int videomemorysize; | |
577 | unsigned char *videomemory; | |
578 | struct metronomefb_par *par; | |
579 | const struct firmware *fw_entry; | |
580 | int cmd_size, wfm_size, img_size, padding_size, totalsize; | |
581 | int i; | |
582 | ||
03c33a4f JK |
583 | /* pick up board specific routines */ |
584 | board = dev->dev.platform_data; | |
585 | if (!board) | |
586 | return -EINVAL; | |
587 | ||
588 | /* try to count device specific driver, if can't, platform recalls */ | |
589 | if (!try_module_get(board->owner)) | |
590 | return -ENODEV; | |
591 | ||
de7c6d15 JK |
592 | /* we have two blocks of memory. |
593 | info->screen_base which is vm, and is the fb used by apps. | |
594 | par->metromem which is physically contiguous memory and | |
595 | contains the display controller commands, waveform, | |
596 | processed image data and padding. this is the data pulled | |
03c33a4f | 597 | by the device's LCD controller and pushed to Metronome */ |
de7c6d15 JK |
598 | |
599 | videomemorysize = (DPY_W*DPY_H); | |
600 | videomemory = vmalloc(videomemorysize); | |
601 | if (!videomemory) | |
03c33a4f | 602 | return -ENOMEM; |
de7c6d15 JK |
603 | |
604 | memset(videomemory, 0, videomemorysize); | |
605 | ||
606 | info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); | |
607 | if (!info) | |
608 | goto err_vfree; | |
609 | ||
03c33a4f | 610 | info->screen_base = (char __force __iomem *)videomemory; |
de7c6d15 JK |
611 | info->fbops = &metronomefb_ops; |
612 | ||
613 | info->var = metronomefb_var; | |
614 | info->fix = metronomefb_fix; | |
615 | info->fix.smem_len = videomemorysize; | |
616 | par = info->par; | |
617 | par->info = info; | |
03c33a4f | 618 | par->board = board; |
de7c6d15 JK |
619 | init_waitqueue_head(&par->waitq); |
620 | ||
621 | /* this table caches per page csum values. */ | |
622 | par->csum_table = vmalloc(videomemorysize/PAGE_SIZE); | |
623 | if (!par->csum_table) | |
624 | goto err_csum_table; | |
625 | ||
626 | /* the metromem buffer is divided as follows: | |
627 | command | CRC | padding | |
628 | 16kb waveform data | CRC | padding | |
629 | image data | CRC | |
630 | and an extra 256 bytes for dma descriptors | |
631 | eg: IW=832 IH=622 WS=128 | |
632 | */ | |
633 | ||
634 | cmd_size = 1 * epd_frame_table[0].fw; | |
635 | wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1) | |
636 | / epd_frame_table[0].fw) * epd_frame_table[0].fw; | |
637 | img_size = epd_frame_table[0].fh * epd_frame_table[0].fw; | |
638 | padding_size = 4 * epd_frame_table[0].fw; | |
639 | totalsize = cmd_size + wfm_size + img_size + padding_size; | |
640 | par->metromemsize = PAGE_ALIGN(totalsize + 256); | |
641 | DPRINTK("desired memory size = %d\n", par->metromemsize); | |
642 | dev->dev.coherent_dma_mask = 0xffffffffull; | |
643 | par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize, | |
644 | &par->metromem_dma, GFP_KERNEL); | |
645 | if (!par->metromem) { | |
646 | printk(KERN_ERR | |
647 | "metronomefb: unable to allocate dma buffer\n"); | |
648 | goto err_vfree; | |
649 | } | |
650 | ||
651 | info->fix.smem_start = par->metromem_dma; | |
652 | par->metromem_cmd = (struct metromem_cmd *) par->metromem; | |
653 | par->metromem_wfm = par->metromem + cmd_size; | |
654 | par->metromem_img = par->metromem + cmd_size + wfm_size; | |
655 | par->metromem_img_csum = (u16 *) (par->metromem_img + | |
656 | (epd_frame_table[0].fw * DPY_H)); | |
657 | DPRINTK("img offset=0x%x\n", cmd_size + wfm_size); | |
658 | par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size | |
659 | + wfm_size + img_size + padding_size); | |
660 | par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size | |
661 | + img_size + padding_size; | |
662 | ||
03c33a4f JK |
663 | /* load the waveform in. assume mode 3, temp 31 for now |
664 | a) request the waveform file from userspace | |
de7c6d15 | 665 | b) process waveform and decode into metromem */ |
03c33a4f | 666 | retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); |
de7c6d15 JK |
667 | if (retval < 0) { |
668 | printk(KERN_ERR "metronomefb: couldn't get waveform\n"); | |
669 | goto err_dma_free; | |
670 | } | |
671 | ||
672 | retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, | |
673 | par->metromem_wfm, 3, 31, &par->frame_count); | |
2422fbba | 674 | release_firmware(fw_entry); |
de7c6d15 JK |
675 | if (retval < 0) { |
676 | printk(KERN_ERR "metronomefb: couldn't process waveform\n"); | |
2422fbba | 677 | goto err_dma_free; |
de7c6d15 | 678 | } |
de7c6d15 | 679 | |
03c33a4f | 680 | if (board->setup_irq(info)) |
2422fbba | 681 | goto err_dma_free; |
de7c6d15 JK |
682 | |
683 | retval = metronome_init_regs(par); | |
684 | if (retval < 0) | |
685 | goto err_free_irq; | |
686 | ||
687 | info->flags = FBINFO_FLAG_DEFAULT; | |
688 | ||
689 | info->fbdefio = &metronomefb_defio; | |
690 | fb_deferred_io_init(info); | |
691 | ||
692 | retval = fb_alloc_cmap(&info->cmap, 8, 0); | |
693 | if (retval < 0) { | |
694 | printk(KERN_ERR "Failed to allocate colormap\n"); | |
695 | goto err_fb_rel; | |
696 | } | |
697 | ||
698 | /* set cmap */ | |
699 | for (i = 0; i < 8; i++) | |
700 | info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16; | |
701 | memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8); | |
702 | memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8); | |
703 | ||
704 | retval = register_framebuffer(info); | |
705 | if (retval < 0) | |
706 | goto err_cmap; | |
707 | ||
708 | platform_set_drvdata(dev, info); | |
709 | ||
710 | printk(KERN_INFO | |
711 | "fb%d: Metronome frame buffer device, using %dK of video" | |
712 | " memory\n", info->node, videomemorysize >> 10); | |
713 | ||
714 | return 0; | |
715 | ||
716 | err_cmap: | |
717 | fb_dealloc_cmap(&info->cmap); | |
718 | err_fb_rel: | |
719 | framebuffer_release(info); | |
720 | err_free_irq: | |
03c33a4f | 721 | board->free_irq(info); |
de7c6d15 JK |
722 | err_dma_free: |
723 | dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem, | |
724 | par->metromem_dma); | |
725 | err_csum_table: | |
726 | vfree(par->csum_table); | |
727 | err_vfree: | |
728 | vfree(videomemory); | |
03c33a4f | 729 | module_put(board->owner); |
de7c6d15 JK |
730 | return retval; |
731 | } | |
732 | ||
733 | static int __devexit metronomefb_remove(struct platform_device *dev) | |
734 | { | |
735 | struct fb_info *info = platform_get_drvdata(dev); | |
736 | ||
737 | if (info) { | |
738 | struct metronomefb_par *par = info->par; | |
739 | fb_deferred_io_cleanup(info); | |
740 | dma_free_writecombine(&dev->dev, par->metromemsize, | |
741 | par->metromem, par->metromem_dma); | |
742 | fb_dealloc_cmap(&info->cmap); | |
743 | vfree(par->csum_table); | |
744 | unregister_framebuffer(info); | |
745 | vfree((void __force *)info->screen_base); | |
03c33a4f JK |
746 | par->board->free_irq(info); |
747 | module_put(par->board->owner); | |
de7c6d15 JK |
748 | framebuffer_release(info); |
749 | } | |
750 | return 0; | |
751 | } | |
752 | ||
753 | static struct platform_driver metronomefb_driver = { | |
754 | .probe = metronomefb_probe, | |
755 | .remove = metronomefb_remove, | |
756 | .driver = { | |
03c33a4f | 757 | .owner = THIS_MODULE, |
de7c6d15 JK |
758 | .name = "metronomefb", |
759 | }, | |
760 | }; | |
761 | ||
de7c6d15 JK |
762 | static int __init metronomefb_init(void) |
763 | { | |
03c33a4f | 764 | return platform_driver_register(&metronomefb_driver); |
de7c6d15 JK |
765 | } |
766 | ||
767 | static void __exit metronomefb_exit(void) | |
768 | { | |
de7c6d15 JK |
769 | platform_driver_unregister(&metronomefb_driver); |
770 | } | |
771 | ||
de7c6d15 JK |
772 | module_init(metronomefb_init); |
773 | module_exit(metronomefb_exit); | |
774 | ||
775 | MODULE_DESCRIPTION("fbdev driver for Metronome controller"); | |
776 | MODULE_AUTHOR("Jaya Kumar"); | |
777 | MODULE_LICENSE("GPL"); |