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