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(&par->metromem_cmd->args[i], 0,
358 (ARRAY_SIZE(par->metromem_cmd->args) - 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 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(&par->metromem_cmd->args[4], 0,
381 (ARRAY_SIZE(par->metromem_cmd->args) - 4) * 2);
382
383 par->metromem_cmd->csum = 0xCC10;
384 par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
385 par->metromem_cmd->opcode = 0xCC10;
386
387 return par->board->met_wait_event(par);
388}
389
390static int metronome_init_cmd(struct metronomefb_par *par)
391{
392 int i;
393 u16 cs;
394
395
396
397
398
399
400 cs = 0xCC20;
401
402
403 i = 0;
404 par->metromem_cmd->args[i] = 0;
405 cs += par->metromem_cmd->args[i++];
406
407
408 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
409
410 par->metromem_cmd->csum = cs;
411 par->metromem_cmd->opcode = 0xCC20;
412
413 return par->board->met_wait_event(par);
414}
415
416static int metronome_init_regs(struct metronomefb_par *par)
417{
418 int res;
419
420 res = par->board->setup_io(par);
421 if (res)
422 return res;
423
424 res = metronome_powerup_cmd(par);
425 if (res)
426 return res;
427
428 res = metronome_config_cmd(par);
429 if (res)
430 return res;
431
432 res = metronome_init_cmd(par);
433
434 return res;
435}
436
437static void metronomefb_dpy_update(struct metronomefb_par *par)
438{
439 int fbsize;
440 u16 cksum;
441 unsigned char *buf = (unsigned char __force *)par->info->screen_base;
442
443 fbsize = par->info->fix.smem_len;
444
445 memcpy(par->metromem_img, buf, fbsize);
446
447 cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
448 *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
449 metronome_display_cmd(par);
450}
451
452static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
453{
454 int i;
455 u16 csum = 0;
456 u16 *buf = (u16 __force *)(par->info->screen_base + index);
457 u16 *img = (u16 *)(par->metromem_img + index);
458
459
460 for (i = 0; i < PAGE_SIZE/2; i++) {
461 *(img + i) = (buf[i] << 5) & 0xE0E0;
462 csum += *(img + i);
463 }
464 return csum;
465}
466
467
468static void metronomefb_dpy_deferred_io(struct fb_info *info,
469 struct list_head *pagelist)
470{
471 u16 cksum;
472 struct page *cur;
473 struct fb_deferred_io *fbdefio = info->fbdefio;
474 struct metronomefb_par *par = info->par;
475
476
477 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
478 cksum = metronomefb_dpy_update_page(par,
479 (cur->index << PAGE_SHIFT));
480 par->metromem_img_csum -= par->csum_table[cur->index];
481 par->csum_table[cur->index] = cksum;
482 par->metromem_img_csum += cksum;
483 }
484
485 metronome_display_cmd(par);
486}
487
488static void metronomefb_fillrect(struct fb_info *info,
489 const struct fb_fillrect *rect)
490{
491 struct metronomefb_par *par = info->par;
492
493 sys_fillrect(info, rect);
494 metronomefb_dpy_update(par);
495}
496
497static void metronomefb_copyarea(struct fb_info *info,
498 const struct fb_copyarea *area)
499{
500 struct metronomefb_par *par = info->par;
501
502 sys_copyarea(info, area);
503 metronomefb_dpy_update(par);
504}
505
506static void metronomefb_imageblit(struct fb_info *info,
507 const struct fb_image *image)
508{
509 struct metronomefb_par *par = info->par;
510
511 sys_imageblit(info, image);
512 metronomefb_dpy_update(par);
513}
514
515
516
517
518
519static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
520 size_t count, loff_t *ppos)
521{
522 struct metronomefb_par *par = info->par;
523 unsigned long p = *ppos;
524 void *dst;
525 int err = 0;
526 unsigned long total_size;
527
528 if (info->state != FBINFO_STATE_RUNNING)
529 return -EPERM;
530
531 total_size = info->fix.smem_len;
532
533 if (p > total_size)
534 return -EFBIG;
535
536 if (count > total_size) {
537 err = -EFBIG;
538 count = total_size;
539 }
540
541 if (count + p > total_size) {
542 if (!err)
543 err = -ENOSPC;
544
545 count = total_size - p;
546 }
547
548 dst = (void __force *)(info->screen_base + p);
549
550 if (copy_from_user(dst, buf, count))
551 err = -EFAULT;
552
553 if (!err)
554 *ppos += count;
555
556 metronomefb_dpy_update(par);
557
558 return (err) ? err : count;
559}
560
561static struct fb_ops metronomefb_ops = {
562 .owner = THIS_MODULE,
563 .fb_write = metronomefb_write,
564 .fb_fillrect = metronomefb_fillrect,
565 .fb_copyarea = metronomefb_copyarea,
566 .fb_imageblit = metronomefb_imageblit,
567};
568
569static struct fb_deferred_io metronomefb_defio = {
570 .delay = HZ,
571 .deferred_io = metronomefb_dpy_deferred_io,
572};
573
574static int metronomefb_probe(struct platform_device *dev)
575{
576 struct fb_info *info;
577 struct metronome_board *board;
578 int retval = -ENOMEM;
579 int videomemorysize;
580 unsigned char *videomemory;
581 struct metronomefb_par *par;
582 const struct firmware *fw_entry;
583 int i;
584 int panel_type;
585 int fw, fh;
586 int epd_dt_index;
587
588
589 board = dev->dev.platform_data;
590 if (!board)
591 return -EINVAL;
592
593
594 if (!try_module_get(board->owner))
595 return -ENODEV;
596
597 info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
598 if (!info)
599 goto err;
600
601
602
603
604
605
606
607
608
609
610 panel_type = board->get_panel_type();
611 switch (panel_type) {
612 case 6:
613 epd_dt_index = 0;
614 break;
615 case 8:
616 epd_dt_index = 1;
617 break;
618 case 97:
619 epd_dt_index = 2;
620 break;
621 default:
622 dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
623 epd_dt_index = 0;
624 break;
625 }
626
627 fw = epd_frame_table[epd_dt_index].fw;
628 fh = epd_frame_table[epd_dt_index].fh;
629
630
631
632 videomemorysize = PAGE_SIZE + (fw * fh);
633 videomemory = vzalloc(videomemorysize);
634 if (!videomemory)
635 goto err_fb_rel;
636
637 info->screen_base = (char __force __iomem *)videomemory;
638 info->fbops = &metronomefb_ops;
639
640 metronomefb_fix.line_length = fw;
641 metronomefb_var.xres = fw;
642 metronomefb_var.yres = fh;
643 metronomefb_var.xres_virtual = fw;
644 metronomefb_var.yres_virtual = fh;
645 info->var = metronomefb_var;
646 info->fix = metronomefb_fix;
647 info->fix.smem_len = videomemorysize;
648 par = info->par;
649 par->info = info;
650 par->board = board;
651 par->dt = epd_dt_index;
652 init_waitqueue_head(&par->waitq);
653
654
655 par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
656 if (!par->csum_table)
657 goto err_vfree;
658
659
660
661
662 retval = board->setup_fb(par);
663 if (retval) {
664 dev_err(&dev->dev, "Failed to setup fb\n");
665 goto err_csum_table;
666 }
667
668
669 if ((!par->metromem_wfm) || (!par->metromem_img) ||
670 (!par->metromem_dma)) {
671 dev_err(&dev->dev, "fb access failure\n");
672 retval = -EINVAL;
673 goto err_csum_table;
674 }
675
676 info->fix.smem_start = par->metromem_dma;
677
678
679
680
681 retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
682 if (retval < 0) {
683 dev_err(&dev->dev, "Failed to get waveform\n");
684 goto err_csum_table;
685 }
686
687 retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
688 par);
689 release_firmware(fw_entry);
690 if (retval < 0) {
691 dev_err(&dev->dev, "Failed processing waveform\n");
692 goto err_csum_table;
693 }
694
695 retval = board->setup_irq(info);
696 if (retval)
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 | FBINFO_VIRTFB;
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 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 .name = "metronomefb",
772 },
773};
774module_platform_driver(metronomefb_driver);
775
776module_param(user_wfm_size, uint, 0);
777MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
778
779MODULE_DESCRIPTION("fbdev driver for Metronome controller");
780MODULE_AUTHOR("Jaya Kumar");
781MODULE_LICENSE("GPL");
782