1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
39#include <video/metronomefb.h>
40
41#include <asm/unaligned.h>
42
43
44#define DPY_W 832
45#define DPY_H 622
46
47static int user_wfm_size;
48
49
50struct epd_frame {
51 int fw;
52 int fh;
53 u16 config[4];
54 int wfm_size;
55};
56
57static struct epd_frame epd_frame_table[] = {
58 {
59 .fw = 832,
60 .fh = 622,
61 .config = {
62 15
63 | 2 << 8
64 | 0 << 11
65 | 0 << 12
66 | 0 << 15,
67 42
68 | 1 << 8
69 | 1 << 9
70 | 0 << 15,
71 18
72 | 0 << 15,
73 599
74 | 0 << 11
75 | 0 << 12,
76 },
77 .wfm_size = 47001,
78 },
79 {
80 .fw = 1088,
81 .fh = 791,
82 .config = {
83 0x0104,
84 0x031f,
85 0x0088,
86 0x02ff,
87 },
88 .wfm_size = 46770,
89 },
90 {
91 .fw = 1200,
92 .fh = 842,
93 .config = {
94 0x0101,
95 0x030e,
96 0x0012,
97 0x0280,
98 },
99 .wfm_size = 46770,
100 },
101};
102
103static struct fb_fix_screeninfo metronomefb_fix __devinitdata = {
104 .id = "metronomefb",
105 .type = FB_TYPE_PACKED_PIXELS,
106 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
107 .xpanstep = 0,
108 .ypanstep = 0,
109 .ywrapstep = 0,
110 .line_length = DPY_W,
111 .accel = FB_ACCEL_NONE,
112};
113
114static struct fb_var_screeninfo metronomefb_var __devinitdata = {
115 .xres = DPY_W,
116 .yres = DPY_H,
117 .xres_virtual = DPY_W,
118 .yres_virtual = DPY_H,
119 .bits_per_pixel = 8,
120 .grayscale = 1,
121 .nonstd = 1,
122 .red = { 4, 3, 0 },
123 .green = { 0, 0, 0 },
124 .blue = { 0, 0, 0 },
125 .transp = { 0, 0, 0 },
126};
127
128
129struct waveform_hdr {
130 u8 stuff[32];
131
132 u8 wmta[3];
133 u8 fvsn;
134
135 u8 luts;
136 u8 mc;
137 u8 trc;
138 u8 stuff3;
139
140 u8 endb;
141 u8 swtb;
142 u8 stuff2a[2];
143
144 u8 stuff2b[3];
145 u8 wfm_cs;
146} __attribute__ ((packed));
147
148
149static u8 calc_cksum(int start, int end, u8 *mem)
150{
151 u8 tmp = 0;
152 int i;
153
154 for (i = start; i < end; i++)
155 tmp += mem[i];
156
157 return tmp;
158}
159
160static u16 calc_img_cksum(u16 *start, int length)
161{
162 u16 tmp = 0;
163
164 while (length--)
165 tmp += *start++;
166
167 return tmp;
168}
169
170
171static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
172 struct metronomefb_par *par)
173{
174 int tta;
175 int wmta;
176 int trn = 0;
177 int i;
178 unsigned char v;
179 u8 cksum;
180 int cksum_idx;
181 int wfm_idx, owfm_idx;
182 int mem_idx = 0;
183 struct waveform_hdr *wfm_hdr;
184 u8 *metromem = par->metromem_wfm;
185 struct device *dev = par->info->dev;
186
187 if (user_wfm_size)
188 epd_frame_table[par->dt].wfm_size = user_wfm_size;
189
190 if (size != epd_frame_table[par->dt].wfm_size) {
191 dev_err(dev, "Error: unexpected size %Zd != %d\n", size,
192 epd_frame_table[par->dt].wfm_size);
193 return -EINVAL;
194 }
195
196 wfm_hdr = (struct waveform_hdr *) mem;
197
198 if (wfm_hdr->fvsn != 1) {
199 dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
200 return -EINVAL;
201 }
202 if (wfm_hdr->luts != 0) {
203 dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
204 return -EINVAL;
205 }
206 cksum = calc_cksum(32, 47, mem);
207 if (cksum != wfm_hdr->wfm_cs) {
208 dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
209 wfm_hdr->wfm_cs);
210 return -EINVAL;
211 }
212 wfm_hdr->mc += 1;
213 wfm_hdr->trc += 1;
214 for (i = 0; i < 5; i++) {
215 if (*(wfm_hdr->stuff2a + i) != 0) {
216 dev_err(dev, "Error: unexpected value in padding\n");
217 return -EINVAL;
218 }
219 }
220
221
222
223
224
225 if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
226 return -EINVAL;
227
228 for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
229 if (mem[i] > t) {
230 trn = i - sizeof(*wfm_hdr) - 1;
231 break;
232 }
233 }
234
235
236 cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
237 if (cksum_idx > size)
238 return -EINVAL;
239 cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
240 if (cksum != mem[cksum_idx]) {
241 dev_err(dev, "Error: bad temperature range table cksum"
242 " %x != %x\n", cksum, mem[cksum_idx]);
243 return -EINVAL;
244 }
245
246
247 wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
248 cksum_idx = wmta + m*4 + 3;
249 if (cksum_idx > size)
250 return -EINVAL;
251 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
252 if (cksum != mem[cksum_idx]) {
253 dev_err(dev, "Error: bad mode table address cksum"
254 " %x != %x\n", cksum, mem[cksum_idx]);
255 return -EINVAL;
256 }
257
258
259 tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
260 cksum_idx = tta + trn*4 + 3;
261 if (cksum_idx > size)
262 return -EINVAL;
263 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
264 if (cksum != mem[cksum_idx]) {
265 dev_err(dev, "Error: bad temperature table address cksum"
266 " %x != %x\n", cksum, mem[cksum_idx]);
267 return -EINVAL;
268 }
269
270
271
272 wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
273 owfm_idx = wfm_idx;
274 if (wfm_idx > size)
275 return -EINVAL;
276 while (wfm_idx < size) {
277 unsigned char rl;
278 v = mem[wfm_idx++];
279 if (v == wfm_hdr->swtb) {
280 while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
281 wfm_idx < size)
282 metromem[mem_idx++] = v;
283
284 continue;
285 }
286
287 if (v == wfm_hdr->endb)
288 break;
289
290 rl = mem[wfm_idx++];
291 for (i = 0; i <= rl; i++)
292 metromem[mem_idx++] = v;
293 }
294
295 cksum_idx = wfm_idx;
296 if (cksum_idx > size)
297 return -EINVAL;
298 cksum = calc_cksum(owfm_idx, cksum_idx, mem);
299 if (cksum != mem[cksum_idx]) {
300 dev_err(dev, "Error: bad waveform data cksum"
301 " %x != %x\n", cksum, mem[cksum_idx]);
302 return -EINVAL;
303 }
304 par->frame_count = (mem_idx/64);
305
306 return 0;
307}
308
309static int metronome_display_cmd(struct metronomefb_par *par)
310{
311 int i;
312 u16 cs;
313 u16 opcode;
314 static u8 borderval;
315
316
317
318
319
320
321 if (par->metromem_cmd->opcode == 0xCC40)
322 opcode = cs = 0xCC41;
323 else
324 opcode = cs = 0xCC40;
325
326
327 i = 0;
328 par->metromem_cmd->args[i] = 1 << 3
329 | ((borderval++ % 4) & 0x0F) << 4
330 | (par->frame_count - 1) << 8;
331 cs += par->metromem_cmd->args[i++];
332
333
334 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
335
336 par->metromem_cmd->csum = cs;
337 par->metromem_cmd->opcode = opcode;
338
339 return par->board->met_wait_event_intr(par);
340}
341
342static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
343{
344 int i;
345 u16 cs;
346
347
348 par->metromem_cmd->opcode = 0x1234;
349 cs = par->metromem_cmd->opcode;
350
351
352 for (i = 0; i < 3; i++) {
353 par->metromem_cmd->args[i] = 1024;
354 cs += par->metromem_cmd->args[i];
355 }
356
357
358 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
359
360 par->metromem_cmd->csum = cs;
361
362 msleep(1);
363 par->board->set_rst(par, 1);
364
365 msleep(1);
366 par->board->set_stdby(par, 1);
367
368 return par->board->met_wait_event(par);
369}
370
371static int __devinit metronome_config_cmd(struct metronomefb_par *par)
372{
373
374
375
376
377 memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
378 sizeof(epd_frame_table[par->dt].config));
379
380 memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);
381
382 par->metromem_cmd->csum = 0xCC10;
383 par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
384 par->metromem_cmd->opcode = 0xCC10;
385
386 return par->board->met_wait_event(par);
387}
388
389static int __devinit metronome_init_cmd(struct metronomefb_par *par)
390{
391 int i;
392 u16 cs;
393
394
395
396
397
398
399 cs = 0xCC20;
400
401
402 i = 0;
403 par->metromem_cmd->args[i] = 0;
404 cs += par->metromem_cmd->args[i++];
405
406
407 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
408
409 par->metromem_cmd->csum = cs;
410 par->metromem_cmd->opcode = 0xCC20;
411
412 return par->board->met_wait_event(par);
413}
414
415static int __devinit metronome_init_regs(struct metronomefb_par *par)
416{
417 int res;
418
419 res = par->board->setup_io(par);
420 if (res)
421 return res;
422
423 res = metronome_powerup_cmd(par);
424 if (res)
425 return res;
426
427 res = metronome_config_cmd(par);
428 if (res)
429 return res;
430
431 res = metronome_init_cmd(par);
432
433 return res;
434}
435
436static void metronomefb_dpy_update(struct metronomefb_par *par)
437{
438 int fbsize;
439 u16 cksum;
440 unsigned char *buf = (unsigned char __force *)par->info->screen_base;
441
442 fbsize = par->info->fix.smem_len;
443
444 memcpy(par->metromem_img, buf, fbsize);
445
446 cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
447 *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
448 metronome_display_cmd(par);
449}
450
451static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
452{
453 int i;
454 u16 csum = 0;
455 u16 *buf = (u16 __force *)(par->info->screen_base + index);
456 u16 *img = (u16 *)(par->metromem_img + index);
457
458
459 for (i = 0; i < PAGE_SIZE/2; i++) {
460 *(img + i) = (buf[i] << 5) & 0xE0E0;
461 csum += *(img + i);
462 }
463 return csum;
464}
465
466
467static void metronomefb_dpy_deferred_io(struct fb_info *info,
468 struct list_head *pagelist)
469{
470 u16 cksum;
471 struct page *cur;
472 struct fb_deferred_io *fbdefio = info->fbdefio;
473 struct metronomefb_par *par = info->par;
474
475
476 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
477 cksum = metronomefb_dpy_update_page(par,
478 (cur->index << PAGE_SHIFT));
479 par->metromem_img_csum -= par->csum_table[cur->index];
480 par->csum_table[cur->index] = cksum;
481 par->metromem_img_csum += cksum;
482 }
483
484 metronome_display_cmd(par);
485}
486
487static void metronomefb_fillrect(struct fb_info *info,
488 const struct fb_fillrect *rect)
489{
490 struct metronomefb_par *par = info->par;
491
492 sys_fillrect(info, rect);
493 metronomefb_dpy_update(par);
494}
495
496static void metronomefb_copyarea(struct fb_info *info,
497 const struct fb_copyarea *area)
498{
499 struct metronomefb_par *par = info->par;
500
501 sys_copyarea(info, area);
502 metronomefb_dpy_update(par);
503}
504
505static void metronomefb_imageblit(struct fb_info *info,
506 const struct fb_image *image)
507{
508 struct metronomefb_par *par = info->par;
509
510 sys_imageblit(info, image);
511 metronomefb_dpy_update(par);
512}
513
514
515
516
517
518static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
519 size_t count, loff_t *ppos)
520{
521 struct metronomefb_par *par = info->par;
522 unsigned long p = *ppos;
523 void *dst;
524 int err = 0;
525 unsigned long total_size;
526
527 if (info->state != FBINFO_STATE_RUNNING)
528 return -EPERM;
529
530 total_size = info->fix.smem_len;
531
532 if (p > total_size)
533 return -EFBIG;
534
535 if (count > total_size) {
536 err = -EFBIG;
537 count = total_size;
538 }
539
540 if (count + p > total_size) {
541 if (!err)
542 err = -ENOSPC;
543
544 count = total_size - p;
545 }
546
547 dst = (void __force *)(info->screen_base + p);
548
549 if (copy_from_user(dst, buf, count))
550 err = -EFAULT;
551
552 if (!err)
553 *ppos += count;
554
555 metronomefb_dpy_update(par);
556
557 return (err) ? err : count;
558}
559
560static struct fb_ops metronomefb_ops = {
561 .owner = THIS_MODULE,
562 .fb_write = metronomefb_write,
563 .fb_fillrect = metronomefb_fillrect,
564 .fb_copyarea = metronomefb_copyarea,
565 .fb_imageblit = metronomefb_imageblit,
566};
567
568static struct fb_deferred_io metronomefb_defio = {
569 .delay = HZ,
570 .deferred_io = metronomefb_dpy_deferred_io,
571};
572
573static int __devinit metronomefb_probe(struct platform_device *dev)
574{
575 struct fb_info *info;
576 struct metronome_board *board;
577 int retval = -ENOMEM;
578 int videomemorysize;
579 unsigned char *videomemory;
580 struct metronomefb_par *par;
581 const struct firmware *fw_entry;
582 int i;
583 int panel_type;
584 int fw, fh;
585 int epd_dt_index;
586
587
588 board = dev->dev.platform_data;
589 if (!board)
590 return -EINVAL;
591
592
593 if (!try_module_get(board->owner))
594 return -ENODEV;
595
596 info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
597 if (!info)
598 goto err;
599
600
601
602
603
604
605
606
607
608
609 panel_type = board->get_panel_type();
610 switch (panel_type) {
611 case 6:
612 epd_dt_index = 0;
613 break;
614 case 8:
615 epd_dt_index = 1;
616 break;
617 case 97:
618 epd_dt_index = 2;
619 break;
620 default:
621 dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
622 epd_dt_index = 0;
623 break;
624 }
625
626 fw = epd_frame_table[epd_dt_index].fw;
627 fh = epd_frame_table[epd_dt_index].fh;
628
629
630
631 videomemorysize = PAGE_SIZE + (fw * fh);
632 videomemory = vmalloc(videomemorysize);
633 if (!videomemory)
634 goto err_fb_rel;
635
636 memset(videomemory, 0, videomemorysize);
637
638 info->screen_base = (char __force __iomem *)videomemory;
639 info->fbops = &metronomefb_ops;
640
641 metronomefb_fix.line_length = fw;
642 metronomefb_var.xres = fw;
643 metronomefb_var.yres = fh;
644 metronomefb_var.xres_virtual = fw;
645 metronomefb_var.yres_virtual = fh;
646 info->var = metronomefb_var;
647 info->fix = metronomefb_fix;
648 info->fix.smem_len = videomemorysize;
649 par = info->par;
650 par->info = info;
651 par->board = board;
652 par->dt = epd_dt_index;
653 init_waitqueue_head(&par->waitq);
654
655
656 par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
657 if (!par->csum_table)
658 goto err_vfree;
659
660
661
662
663 retval = board->setup_fb(par);
664 if (retval) {
665 dev_err(&dev->dev, "Failed to setup fb\n");
666 goto err_csum_table;
667 }
668
669
670 if ((!par->metromem_wfm) || (!par->metromem_img) ||
671 (!par->metromem_dma)) {
672 dev_err(&dev->dev, "fb access failure\n");
673 retval = -EINVAL;
674 goto err_csum_table;
675 }
676
677 info->fix.smem_start = par->metromem_dma;
678
679
680
681
682 retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
683 if (retval < 0) {
684 dev_err(&dev->dev, "Failed to get waveform\n");
685 goto err_csum_table;
686 }
687
688 retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
689 par);
690 release_firmware(fw_entry);
691 if (retval < 0) {
692 dev_err(&dev->dev, "Failed processing waveform\n");
693 goto err_csum_table;
694 }
695
696 if (board->setup_irq(info))
697 goto err_csum_table;
698
699 retval = metronome_init_regs(par);
700 if (retval < 0)
701 goto err_free_irq;
702
703 info->flags = FBINFO_FLAG_DEFAULT;
704
705 info->fbdefio = &metronomefb_defio;
706 fb_deferred_io_init(info);
707
708 retval = fb_alloc_cmap(&info->cmap, 8, 0);
709 if (retval < 0) {
710 dev_err(&dev->dev, "Failed to allocate colormap\n");
711 goto err_free_irq;
712 }
713
714
715 for (i = 0; i < 8; i++)
716 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
717 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
718 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
719
720 retval = register_framebuffer(info);
721 if (retval < 0)
722 goto err_cmap;
723
724 platform_set_drvdata(dev, info);
725
726 dev_dbg(&dev->dev,
727 "fb%d: Metronome frame buffer device, using %dK of video"
728 " memory\n", info->node, videomemorysize >> 10);
729
730 return 0;
731
732err_cmap:
733 fb_dealloc_cmap(&info->cmap);
734err_free_irq:
735 board->cleanup(par);
736err_csum_table:
737 vfree(par->csum_table);
738err_vfree:
739 vfree(videomemory);
740err_fb_rel:
741 framebuffer_release(info);
742err:
743 module_put(board->owner);
744 return retval;
745}
746
747static int __devexit metronomefb_remove(struct platform_device *dev)
748{
749 struct fb_info *info = platform_get_drvdata(dev);
750
751 if (info) {
752 struct metronomefb_par *par = info->par;
753
754 unregister_framebuffer(info);
755 fb_deferred_io_cleanup(info);
756 fb_dealloc_cmap(&info->cmap);
757 par->board->cleanup(par);
758 vfree(par->csum_table);
759 vfree((void __force *)info->screen_base);
760 module_put(par->board->owner);
761 dev_dbg(&dev->dev, "calling release\n");
762 framebuffer_release(info);
763 }
764 return 0;
765}
766
767static struct platform_driver metronomefb_driver = {
768 .probe = metronomefb_probe,
769 .remove = metronomefb_remove,
770 .driver = {
771 .owner = THIS_MODULE,
772 .name = "metronomefb",
773 },
774};
775
776static int __init metronomefb_init(void)
777{
778 return platform_driver_register(&metronomefb_driver);
779}
780
781static void __exit metronomefb_exit(void)
782{
783 platform_driver_unregister(&metronomefb_driver);
784}
785
786module_param(user_wfm_size, uint, 0);
787MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
788
789module_init(metronomefb_init);
790module_exit(metronomefb_exit);
791
792MODULE_DESCRIPTION("fbdev driver for Metronome controller");
793MODULE_AUTHOR("Jaya Kumar");
794MODULE_LICENSE("GPL");
795