blob: 7cda4f80af52be850afd9389b4dfeb28bd856150 [file] [log] [blame]
Jaya Kumarde7c6d12008-03-19 17:01:10 -07001/*
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.
Jaya Kumar03c33a42008-04-28 02:15:38 -070016 * 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
Jaya Kumarde7c6d12008-03-19 17:01:10 -070019 *
Jaya Kumarde7c6d12008-03-19 17:01:10 -070020 */
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
Jaya Kumar03c33a42008-04-28 02:15:38 -070039#include <video/metronomefb.h>
40
Jaya Kumarde7c6d12008-03-19 17:01:10 -070041#include <asm/unaligned.h>
42
Jaya Kumarde7c6d12008-03-19 17:01:10 -070043/* Display specific information */
44#define DPY_W 832
45#define DPY_H 622
46
Jaya Kumarde7c6d12008-03-19 17:01:10 -070047/* frame differs from image. frame includes non-visible pixels */
48struct epd_frame {
49 int fw; /* frame width */
50 int fh; /* frame height */
51};
52
53static struct epd_frame epd_frame_table[] = {
54 {
55 .fw = 832,
56 .fh = 622
57 },
58};
59
60static struct fb_fix_screeninfo metronomefb_fix __devinitdata = {
61 .id = "metronomefb",
62 .type = FB_TYPE_PACKED_PIXELS,
63 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
64 .xpanstep = 0,
65 .ypanstep = 0,
66 .ywrapstep = 0,
67 .line_length = DPY_W,
68 .accel = FB_ACCEL_NONE,
69};
70
71static struct fb_var_screeninfo metronomefb_var __devinitdata = {
72 .xres = DPY_W,
73 .yres = DPY_H,
74 .xres_virtual = DPY_W,
75 .yres_virtual = DPY_H,
76 .bits_per_pixel = 8,
77 .grayscale = 1,
78 .nonstd = 1,
79 .red = { 4, 3, 0 },
80 .green = { 0, 0, 0 },
81 .blue = { 0, 0, 0 },
82 .transp = { 0, 0, 0 },
83};
84
Jaya Kumar03c33a42008-04-28 02:15:38 -070085/* the waveform structure that is coming from userspace firmware */
Jaya Kumarde7c6d12008-03-19 17:01:10 -070086struct waveform_hdr {
87 u8 stuff[32];
88
89 u8 wmta[3];
90 u8 fvsn;
91
92 u8 luts;
93 u8 mc;
94 u8 trc;
95 u8 stuff3;
96
97 u8 endb;
98 u8 swtb;
99 u8 stuff2a[2];
100
101 u8 stuff2b[3];
102 u8 wfm_cs;
103} __attribute__ ((packed));
104
105/* main metronomefb functions */
106static u8 calc_cksum(int start, int end, u8 *mem)
107{
108 u8 tmp = 0;
109 int i;
110
111 for (i = start; i < end; i++)
112 tmp += mem[i];
113
114 return tmp;
115}
116
117static u16 calc_img_cksum(u16 *start, int length)
118{
119 u16 tmp = 0;
120
121 while (length--)
122 tmp += *start++;
123
124 return tmp;
125}
126
127/* here we decode the incoming waveform file and populate metromem */
128#define EXP_WFORM_SIZE 47001
Jaya Kumar28501332008-08-05 13:52:14 +0100129static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
130 struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700131{
132 int tta;
133 int wmta;
134 int trn = 0;
135 int i;
136 unsigned char v;
137 u8 cksum;
138 int cksum_idx;
139 int wfm_idx, owfm_idx;
140 int mem_idx = 0;
141 struct waveform_hdr *wfm_hdr;
Jaya Kumar28501332008-08-05 13:52:14 +0100142 u8 *metromem = par->metromem_wfm;
143 struct device *dev = par->info->dev;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700144
145 if (size != EXP_WFORM_SIZE) {
Jaya Kumar28501332008-08-05 13:52:14 +0100146 dev_err(dev, "Error: unexpected size %d != %d\n", size,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700147 EXP_WFORM_SIZE);
148 return -EINVAL;
149 }
150
151 wfm_hdr = (struct waveform_hdr *) mem;
152
153 if (wfm_hdr->fvsn != 1) {
Jaya Kumar28501332008-08-05 13:52:14 +0100154 dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700155 return -EINVAL;
156 }
157 if (wfm_hdr->luts != 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100158 dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700159 return -EINVAL;
160 }
161 cksum = calc_cksum(32, 47, mem);
162 if (cksum != wfm_hdr->wfm_cs) {
Jaya Kumar28501332008-08-05 13:52:14 +0100163 dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700164 wfm_hdr->wfm_cs);
165 return -EINVAL;
166 }
167 wfm_hdr->mc += 1;
168 wfm_hdr->trc += 1;
169 for (i = 0; i < 5; i++) {
170 if (*(wfm_hdr->stuff2a + i) != 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100171 dev_err(dev, "Error: unexpected value in padding\n");
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700172 return -EINVAL;
173 }
174 }
175
176 /* calculating trn. trn is something used to index into
177 the waveform. presumably selecting the right one for the
178 desired temperature. it works out the offset of the first
179 v that exceeds the specified temperature */
180 if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
181 return -EINVAL;
182
183 for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
184 if (mem[i] > t) {
185 trn = i - sizeof(*wfm_hdr) - 1;
186 break;
187 }
188 }
189
190 /* check temperature range table checksum */
191 cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
192 if (cksum_idx > size)
193 return -EINVAL;
194 cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
195 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100196 dev_err(dev, "Error: bad temperature range table cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700197 " %x != %x\n", cksum, mem[cksum_idx]);
198 return -EINVAL;
199 }
200
201 /* check waveform mode table address checksum */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700202 wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700203 cksum_idx = wmta + m*4 + 3;
204 if (cksum_idx > size)
205 return -EINVAL;
206 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
207 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100208 dev_err(dev, "Error: bad mode table address cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700209 " %x != %x\n", cksum, mem[cksum_idx]);
210 return -EINVAL;
211 }
212
213 /* check waveform temperature table address checksum */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700214 tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700215 cksum_idx = tta + trn*4 + 3;
216 if (cksum_idx > size)
217 return -EINVAL;
218 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
219 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100220 dev_err(dev, "Error: bad temperature table address cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700221 " %x != %x\n", cksum, mem[cksum_idx]);
222 return -EINVAL;
223 }
224
225 /* here we do the real work of putting the waveform into the
226 metromem buffer. this does runlength decoding of the waveform */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700227 wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700228 owfm_idx = wfm_idx;
229 if (wfm_idx > size)
230 return -EINVAL;
231 while (wfm_idx < size) {
232 unsigned char rl;
233 v = mem[wfm_idx++];
234 if (v == wfm_hdr->swtb) {
235 while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
236 wfm_idx < size)
237 metromem[mem_idx++] = v;
238
239 continue;
240 }
241
242 if (v == wfm_hdr->endb)
243 break;
244
245 rl = mem[wfm_idx++];
246 for (i = 0; i <= rl; i++)
247 metromem[mem_idx++] = v;
248 }
249
250 cksum_idx = wfm_idx;
251 if (cksum_idx > size)
252 return -EINVAL;
253 cksum = calc_cksum(owfm_idx, cksum_idx, mem);
254 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100255 dev_err(dev, "Error: bad waveform data cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700256 " %x != %x\n", cksum, mem[cksum_idx]);
257 return -EINVAL;
258 }
Jaya Kumar28501332008-08-05 13:52:14 +0100259 par->frame_count = (mem_idx/64);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700260
261 return 0;
262}
263
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700264static int metronome_display_cmd(struct metronomefb_par *par)
265{
266 int i;
267 u16 cs;
268 u16 opcode;
269 static u8 borderval;
270 u8 *ptr;
271
272 /* setup display command
273 we can't immediately set the opcode since the controller
274 will try parse the command before we've set it all up
275 so we just set cs here and set the opcode at the end */
276
277 ptr = par->metromem;
278
279 if (par->metromem_cmd->opcode == 0xCC40)
280 opcode = cs = 0xCC41;
281 else
282 opcode = cs = 0xCC40;
283
284 /* set the args ( 2 bytes ) for display */
285 i = 0;
286 par->metromem_cmd->args[i] = 1 << 3 /* border update */
287 | ((borderval++ % 4) & 0x0F) << 4
288 | (par->frame_count - 1) << 8;
289 cs += par->metromem_cmd->args[i++];
290
291 /* the rest are 0 */
292 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
293
294 par->metromem_cmd->csum = cs;
295 par->metromem_cmd->opcode = opcode; /* display cmd */
296
Jaya Kumar03c33a42008-04-28 02:15:38 -0700297 return par->board->met_wait_event_intr(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700298}
299
300static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
301{
302 int i;
303 u16 cs;
304
305 /* setup power up command */
306 par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
307 cs = par->metromem_cmd->opcode;
308
309 /* set pwr1,2,3 to 1024 */
310 for (i = 0; i < 3; i++) {
311 par->metromem_cmd->args[i] = 1024;
312 cs += par->metromem_cmd->args[i];
313 }
314
315 /* the rest are 0 */
316 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
317
318 par->metromem_cmd->csum = cs;
319
320 msleep(1);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700321 par->board->set_rst(par, 1);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700322
323 msleep(1);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700324 par->board->set_stdby(par, 1);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700325
Jaya Kumar03c33a42008-04-28 02:15:38 -0700326 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700327}
328
329static int __devinit metronome_config_cmd(struct metronomefb_par *par)
330{
331 int i;
332 u16 cs;
333
334 /* setup config command
335 we can't immediately set the opcode since the controller
336 will try parse the command before we've set it all up
337 so we just set cs here and set the opcode at the end */
338
339 cs = 0xCC10;
340
341 /* set the 12 args ( 8 bytes ) for config. see spec for meanings */
342 i = 0;
343 par->metromem_cmd->args[i] = 15 /* sdlew */
344 | 2 << 8 /* sdosz */
345 | 0 << 11 /* sdor */
346 | 0 << 12 /* sdces */
347 | 0 << 15; /* sdcer */
348 cs += par->metromem_cmd->args[i++];
349
350 par->metromem_cmd->args[i] = 42 /* gdspl */
351 | 1 << 8 /* gdr1 */
352 | 1 << 9 /* sdshr */
353 | 0 << 15; /* gdspp */
354 cs += par->metromem_cmd->args[i++];
355
356 par->metromem_cmd->args[i] = 18 /* gdspw */
357 | 0 << 15; /* dispc */
358 cs += par->metromem_cmd->args[i++];
359
360 par->metromem_cmd->args[i] = 599 /* vdlc */
361 | 0 << 11 /* dsi */
362 | 0 << 12; /* dsic */
363 cs += par->metromem_cmd->args[i++];
364
365 /* the rest are 0 */
366 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
367
368 par->metromem_cmd->csum = cs;
369 par->metromem_cmd->opcode = 0xCC10; /* config cmd */
370
Jaya Kumar03c33a42008-04-28 02:15:38 -0700371 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700372}
373
374static int __devinit metronome_init_cmd(struct metronomefb_par *par)
375{
376 int i;
377 u16 cs;
378
379 /* setup init command
380 we can't immediately set the opcode since the controller
381 will try parse the command before we've set it all up
382 so we just set cs here and set the opcode at the end */
383
384 cs = 0xCC20;
385
386 /* set the args ( 2 bytes ) for init */
387 i = 0;
388 par->metromem_cmd->args[i] = 0;
389 cs += par->metromem_cmd->args[i++];
390
391 /* the rest are 0 */
392 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
393
394 par->metromem_cmd->csum = cs;
395 par->metromem_cmd->opcode = 0xCC20; /* init cmd */
396
Jaya Kumar03c33a42008-04-28 02:15:38 -0700397 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700398}
399
400static int __devinit metronome_init_regs(struct metronomefb_par *par)
401{
402 int res;
403
Jaya Kumar03c33a42008-04-28 02:15:38 -0700404 par->board->init_gpio_regs(par);
405
406 par->board->init_lcdc_regs(par);
407
408 /* now that lcd is setup, setup dma descriptor */
409 par->board->post_dma_setup(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700410
411 res = metronome_powerup_cmd(par);
412 if (res)
413 return res;
414
415 res = metronome_config_cmd(par);
416 if (res)
417 return res;
418
419 res = metronome_init_cmd(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700420
421 return res;
422}
423
424static void metronomefb_dpy_update(struct metronomefb_par *par)
425{
426 u16 cksum;
427 unsigned char *buf = (unsigned char __force *)par->info->screen_base;
428
429 /* copy from vm to metromem */
430 memcpy(par->metromem_img, buf, DPY_W*DPY_H);
431
432 cksum = calc_img_cksum((u16 *) par->metromem_img,
433 (epd_frame_table[0].fw * DPY_H)/2);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700434 *((u16 *)(par->metromem_img) +
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700435 (epd_frame_table[0].fw * DPY_H)/2) = cksum;
436 metronome_display_cmd(par);
437}
438
439static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
440{
441 int i;
442 u16 csum = 0;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700443 u16 *buf = (u16 __force *)(par->info->screen_base + index);
444 u16 *img = (u16 *)(par->metromem_img + index);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700445
446 /* swizzle from vm to metromem and recalc cksum at the same time*/
447 for (i = 0; i < PAGE_SIZE/2; i++) {
448 *(img + i) = (buf[i] << 5) & 0xE0E0;
449 csum += *(img + i);
450 }
451 return csum;
452}
453
454/* this is called back from the deferred io workqueue */
455static void metronomefb_dpy_deferred_io(struct fb_info *info,
456 struct list_head *pagelist)
457{
458 u16 cksum;
459 struct page *cur;
460 struct fb_deferred_io *fbdefio = info->fbdefio;
461 struct metronomefb_par *par = info->par;
462
463 /* walk the written page list and swizzle the data */
464 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
465 cksum = metronomefb_dpy_update_page(par,
466 (cur->index << PAGE_SHIFT));
467 par->metromem_img_csum -= par->csum_table[cur->index];
468 par->csum_table[cur->index] = cksum;
469 par->metromem_img_csum += cksum;
470 }
471
472 metronome_display_cmd(par);
473}
474
475static void metronomefb_fillrect(struct fb_info *info,
476 const struct fb_fillrect *rect)
477{
478 struct metronomefb_par *par = info->par;
479
Jaya Kumar555514f2008-04-28 02:15:36 -0700480 sys_fillrect(info, rect);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700481 metronomefb_dpy_update(par);
482}
483
484static void metronomefb_copyarea(struct fb_info *info,
485 const struct fb_copyarea *area)
486{
487 struct metronomefb_par *par = info->par;
488
Jaya Kumar555514f2008-04-28 02:15:36 -0700489 sys_copyarea(info, area);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700490 metronomefb_dpy_update(par);
491}
492
493static void metronomefb_imageblit(struct fb_info *info,
494 const struct fb_image *image)
495{
496 struct metronomefb_par *par = info->par;
497
Jaya Kumar555514f2008-04-28 02:15:36 -0700498 sys_imageblit(info, image);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700499 metronomefb_dpy_update(par);
500}
501
502/*
503 * this is the slow path from userspace. they can seek and write to
504 * the fb. it is based on fb_sys_write
505 */
506static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
507 size_t count, loff_t *ppos)
508{
509 struct metronomefb_par *par = info->par;
510 unsigned long p = *ppos;
511 void *dst;
512 int err = 0;
513 unsigned long total_size;
514
515 if (info->state != FBINFO_STATE_RUNNING)
516 return -EPERM;
517
518 total_size = info->fix.smem_len;
519
520 if (p > total_size)
521 return -EFBIG;
522
523 if (count > total_size) {
524 err = -EFBIG;
525 count = total_size;
526 }
527
528 if (count + p > total_size) {
529 if (!err)
530 err = -ENOSPC;
531
532 count = total_size - p;
533 }
534
Jaya Kumar03c33a42008-04-28 02:15:38 -0700535 dst = (void __force *)(info->screen_base + p);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700536
537 if (copy_from_user(dst, buf, count))
538 err = -EFAULT;
539
540 if (!err)
541 *ppos += count;
542
543 metronomefb_dpy_update(par);
544
545 return (err) ? err : count;
546}
547
548static struct fb_ops metronomefb_ops = {
549 .owner = THIS_MODULE,
550 .fb_write = metronomefb_write,
551 .fb_fillrect = metronomefb_fillrect,
552 .fb_copyarea = metronomefb_copyarea,
553 .fb_imageblit = metronomefb_imageblit,
554};
555
556static struct fb_deferred_io metronomefb_defio = {
557 .delay = HZ,
558 .deferred_io = metronomefb_dpy_deferred_io,
559};
560
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700561static int __devinit metronomefb_probe(struct platform_device *dev)
562{
563 struct fb_info *info;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700564 struct metronome_board *board;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700565 int retval = -ENOMEM;
566 int videomemorysize;
567 unsigned char *videomemory;
568 struct metronomefb_par *par;
569 const struct firmware *fw_entry;
570 int cmd_size, wfm_size, img_size, padding_size, totalsize;
571 int i;
572
Jaya Kumar03c33a42008-04-28 02:15:38 -0700573 /* pick up board specific routines */
574 board = dev->dev.platform_data;
575 if (!board)
576 return -EINVAL;
577
578 /* try to count device specific driver, if can't, platform recalls */
579 if (!try_module_get(board->owner))
580 return -ENODEV;
581
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700582 /* we have two blocks of memory.
583 info->screen_base which is vm, and is the fb used by apps.
584 par->metromem which is physically contiguous memory and
585 contains the display controller commands, waveform,
586 processed image data and padding. this is the data pulled
Jaya Kumar03c33a42008-04-28 02:15:38 -0700587 by the device's LCD controller and pushed to Metronome */
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700588
589 videomemorysize = (DPY_W*DPY_H);
590 videomemory = vmalloc(videomemorysize);
591 if (!videomemory)
Jaya Kumar03c33a42008-04-28 02:15:38 -0700592 return -ENOMEM;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700593
594 memset(videomemory, 0, videomemorysize);
595
596 info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
597 if (!info)
598 goto err_vfree;
599
Jaya Kumar03c33a42008-04-28 02:15:38 -0700600 info->screen_base = (char __force __iomem *)videomemory;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700601 info->fbops = &metronomefb_ops;
602
603 info->var = metronomefb_var;
604 info->fix = metronomefb_fix;
605 info->fix.smem_len = videomemorysize;
606 par = info->par;
607 par->info = info;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700608 par->board = board;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700609 init_waitqueue_head(&par->waitq);
610
611 /* this table caches per page csum values. */
612 par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
613 if (!par->csum_table)
614 goto err_csum_table;
615
616 /* the metromem buffer is divided as follows:
617 command | CRC | padding
618 16kb waveform data | CRC | padding
619 image data | CRC
620 and an extra 256 bytes for dma descriptors
621 eg: IW=832 IH=622 WS=128
622 */
623
624 cmd_size = 1 * epd_frame_table[0].fw;
625 wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1)
626 / epd_frame_table[0].fw) * epd_frame_table[0].fw;
627 img_size = epd_frame_table[0].fh * epd_frame_table[0].fw;
628 padding_size = 4 * epd_frame_table[0].fw;
629 totalsize = cmd_size + wfm_size + img_size + padding_size;
630 par->metromemsize = PAGE_ALIGN(totalsize + 256);
631 DPRINTK("desired memory size = %d\n", par->metromemsize);
632 dev->dev.coherent_dma_mask = 0xffffffffull;
633 par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize,
634 &par->metromem_dma, GFP_KERNEL);
635 if (!par->metromem) {
636 printk(KERN_ERR
637 "metronomefb: unable to allocate dma buffer\n");
638 goto err_vfree;
639 }
640
641 info->fix.smem_start = par->metromem_dma;
642 par->metromem_cmd = (struct metromem_cmd *) par->metromem;
643 par->metromem_wfm = par->metromem + cmd_size;
644 par->metromem_img = par->metromem + cmd_size + wfm_size;
645 par->metromem_img_csum = (u16 *) (par->metromem_img +
646 (epd_frame_table[0].fw * DPY_H));
647 DPRINTK("img offset=0x%x\n", cmd_size + wfm_size);
648 par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size
649 + wfm_size + img_size + padding_size);
650 par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size
651 + img_size + padding_size;
652
Jaya Kumar03c33a42008-04-28 02:15:38 -0700653 /* load the waveform in. assume mode 3, temp 31 for now
654 a) request the waveform file from userspace
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700655 b) process waveform and decode into metromem */
Jaya Kumar03c33a42008-04-28 02:15:38 -0700656 retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700657 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100658 dev_err(&dev->dev, "Failed to get waveform\n");
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700659 goto err_dma_free;
660 }
661
Jaya Kumar28501332008-08-05 13:52:14 +0100662 retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
663 par);
Sebastian Siewior2422fbb2008-04-28 02:15:39 -0700664 release_firmware(fw_entry);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700665 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100666 dev_err(&dev->dev, "Failed processing waveform\n");
Sebastian Siewior2422fbb2008-04-28 02:15:39 -0700667 goto err_dma_free;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700668 }
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700669
Jaya Kumar03c33a42008-04-28 02:15:38 -0700670 if (board->setup_irq(info))
Sebastian Siewior2422fbb2008-04-28 02:15:39 -0700671 goto err_dma_free;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700672
673 retval = metronome_init_regs(par);
674 if (retval < 0)
675 goto err_free_irq;
676
677 info->flags = FBINFO_FLAG_DEFAULT;
678
679 info->fbdefio = &metronomefb_defio;
680 fb_deferred_io_init(info);
681
682 retval = fb_alloc_cmap(&info->cmap, 8, 0);
683 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100684 dev_err(&dev->dev, "Failed to allocate colormap\n");
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700685 goto err_fb_rel;
686 }
687
688 /* set cmap */
689 for (i = 0; i < 8; i++)
690 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
691 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
692 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
693
694 retval = register_framebuffer(info);
695 if (retval < 0)
696 goto err_cmap;
697
698 platform_set_drvdata(dev, info);
699
Jaya Kumar28501332008-08-05 13:52:14 +0100700 dev_dbg(&dev->dev,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700701 "fb%d: Metronome frame buffer device, using %dK of video"
702 " memory\n", info->node, videomemorysize >> 10);
703
704 return 0;
705
706err_cmap:
707 fb_dealloc_cmap(&info->cmap);
708err_fb_rel:
709 framebuffer_release(info);
710err_free_irq:
Jaya Kumar03c33a42008-04-28 02:15:38 -0700711 board->free_irq(info);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700712err_dma_free:
713 dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem,
714 par->metromem_dma);
715err_csum_table:
716 vfree(par->csum_table);
717err_vfree:
718 vfree(videomemory);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700719 module_put(board->owner);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700720 return retval;
721}
722
723static int __devexit metronomefb_remove(struct platform_device *dev)
724{
725 struct fb_info *info = platform_get_drvdata(dev);
726
727 if (info) {
728 struct metronomefb_par *par = info->par;
729 fb_deferred_io_cleanup(info);
730 dma_free_writecombine(&dev->dev, par->metromemsize,
731 par->metromem, par->metromem_dma);
732 fb_dealloc_cmap(&info->cmap);
733 vfree(par->csum_table);
734 unregister_framebuffer(info);
735 vfree((void __force *)info->screen_base);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700736 par->board->free_irq(info);
737 module_put(par->board->owner);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700738 framebuffer_release(info);
739 }
740 return 0;
741}
742
743static struct platform_driver metronomefb_driver = {
744 .probe = metronomefb_probe,
745 .remove = metronomefb_remove,
746 .driver = {
Jaya Kumar03c33a42008-04-28 02:15:38 -0700747 .owner = THIS_MODULE,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700748 .name = "metronomefb",
749 },
750};
751
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700752static int __init metronomefb_init(void)
753{
Jaya Kumar03c33a42008-04-28 02:15:38 -0700754 return platform_driver_register(&metronomefb_driver);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700755}
756
757static void __exit metronomefb_exit(void)
758{
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700759 platform_driver_unregister(&metronomefb_driver);
760}
761
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700762module_init(metronomefb_init);
763module_exit(metronomefb_exit);
764
765MODULE_DESCRIPTION("fbdev driver for Metronome controller");
766MODULE_AUTHOR("Jaya Kumar");
767MODULE_LICENSE("GPL");