Merge tag 'drm-misc-next-2023-07-13' of git://anongit.freedesktop.org/drm/drm-misc...
[sfrench/cifs-2.6.git] / drivers / video / fbdev / metronomefb.c
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. https://www.eink.com/
14  *
15  * This driver is written to be used with the Metronome display controller.
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
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 /* Display specific information */
43 #define DPY_W 832
44 #define DPY_H 622
45
46 static int user_wfm_size;
47
48 /* frame differs from image. frame includes non-visible pixels */
49 struct epd_frame {
50         int fw; /* frame width */
51         int fh; /* frame height */
52         u16 config[4];
53         int wfm_size;
54 };
55
56 static struct epd_frame epd_frame_table[] = {
57         {
58                 .fw = 832,
59                 .fh = 622,
60                 .config = {
61                         15 /* sdlew */
62                         | 2 << 8 /* sdosz */
63                         | 0 << 11 /* sdor */
64                         | 0 << 12 /* sdces */
65                         | 0 << 15, /* sdcer */
66                         42 /* gdspl */
67                         | 1 << 8 /* gdr1 */
68                         | 1 << 9 /* sdshr */
69                         | 0 << 15, /* gdspp */
70                         18 /* gdspw */
71                         | 0 << 15, /* dispc */
72                         599 /* vdlc */
73                         | 0 << 11 /* dsi */
74                         | 0 << 12, /* dsic */
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
102 static 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
113 static 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 /* the waveform structure that is coming from userspace firmware */
128 struct 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 /* main metronomefb functions */
148 static 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
159 static 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 /* here we decode the incoming waveform file and populate metromem */
170 static 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->device;
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         /* calculating trn. trn is something used to index into
221         the waveform. presumably selecting the right one for the
222         desired temperature. it works out the offset of the first
223         v that exceeds the specified temperature */
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         /* check temperature range table checksum */
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         /* check waveform mode table address checksum */
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         /* check waveform temperature table address checksum */
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         /* here we do the real work of putting the waveform into the
270         metromem buffer. this does runlength decoding of the waveform */
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
308 static int metronome_display_cmd(struct metronomefb_par *par)
309 {
310         int i;
311         u16 cs;
312         u16 opcode;
313         static u8 borderval;
314
315         /* setup display command
316         we can't immediately set the opcode since the controller
317         will try parse the command before we've set it all up
318         so we just set cs here and set the opcode at the end */
319
320         if (par->metromem_cmd->opcode == 0xCC40)
321                 opcode = cs = 0xCC41;
322         else
323                 opcode = cs = 0xCC40;
324
325         /* set the args ( 2 bytes ) for display */
326         i = 0;
327         par->metromem_cmd->args[i] =    1 << 3 /* border update */
328                                         | ((borderval++ % 4) & 0x0F) << 4
329                                         | (par->frame_count - 1) << 8;
330         cs += par->metromem_cmd->args[i++];
331
332         /* the rest are 0 */
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; /* display cmd */
337
338         return par->board->met_wait_event_intr(par);
339 }
340
341 static int metronome_powerup_cmd(struct metronomefb_par *par)
342 {
343         int i;
344         u16 cs;
345
346         /* setup power up command */
347         par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
348         cs = par->metromem_cmd->opcode;
349
350         /* set pwr1,2,3 to 1024 */
351         for (i = 0; i < 3; i++) {
352                 par->metromem_cmd->args[i] = 1024;
353                 cs += par->metromem_cmd->args[i];
354         }
355
356         /* the rest are 0 */
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
371 static int metronome_config_cmd(struct metronomefb_par *par)
372 {
373         /* setup config command
374         we can't immediately set the opcode since the controller
375         will try parse the command before we've set it all up */
376
377         memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
378                 sizeof(epd_frame_table[par->dt].config));
379         /* the rest are 0 */
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; /* config cmd */
386
387         return par->board->met_wait_event(par);
388 }
389
390 static int metronome_init_cmd(struct metronomefb_par *par)
391 {
392         int i;
393         u16 cs;
394
395         /* setup init command
396         we can't immediately set the opcode since the controller
397         will try parse the command before we've set it all up
398         so we just set cs here and set the opcode at the end */
399
400         cs = 0xCC20;
401
402         /* set the args ( 2 bytes ) for init */
403         i = 0;
404         par->metromem_cmd->args[i] = 0;
405         cs += par->metromem_cmd->args[i++];
406
407         /* the rest are 0 */
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; /* init cmd */
412
413         return par->board->met_wait_event(par);
414 }
415
416 static 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
437 static void metronomefb_dpy_update(struct metronomefb_par *par)
438 {
439         int fbsize;
440         u16 cksum;
441         unsigned char *buf = par->info->screen_buffer;
442
443         fbsize = par->info->fix.smem_len;
444         /* copy from vm to metromem */
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
452 static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
453 {
454         int i;
455         u16 csum = 0;
456         u16 *buf = (u16 *)(par->info->screen_buffer + index);
457         u16 *img = (u16 *)(par->metromem_img + index);
458
459         /* swizzle from vm to metromem and recalc cksum at the same time*/
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 /* this is called back from the deferred io workqueue */
468 static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
469 {
470         u16 cksum;
471         struct fb_deferred_io_pageref *pageref;
472         struct metronomefb_par *par = info->par;
473
474         /* walk the written page list and swizzle the data */
475         list_for_each_entry(pageref, pagereflist, list) {
476                 unsigned long pgoffset = pageref->offset >> PAGE_SHIFT;
477                 cksum = metronomefb_dpy_update_page(par, pageref->offset);
478                 par->metromem_img_csum -= par->csum_table[pgoffset];
479                 par->csum_table[pgoffset] = cksum;
480                 par->metromem_img_csum += cksum;
481         }
482
483         metronome_display_cmd(par);
484 }
485
486 static void metronomefb_defio_damage_range(struct fb_info *info, off_t off, size_t len)
487 {
488         struct metronomefb_par *par = info->par;
489
490         metronomefb_dpy_update(par);
491 }
492
493 static void metronomefb_defio_damage_area(struct fb_info *info, u32 x, u32 y,
494                                           u32 width, u32 height)
495 {
496         struct metronomefb_par *par = info->par;
497
498         metronomefb_dpy_update(par);
499 }
500
501 FB_GEN_DEFAULT_DEFERRED_SYS_OPS(metronomefb,
502                                 metronomefb_defio_damage_range,
503                                 metronomefb_defio_damage_area)
504
505 static const struct fb_ops metronomefb_ops = {
506         .owner  = THIS_MODULE,
507         FB_DEFAULT_DEFERRED_OPS(metronomefb),
508 };
509
510 static struct fb_deferred_io metronomefb_defio = {
511         .delay                  = HZ,
512         .sort_pagereflist       = true,
513         .deferred_io            = metronomefb_dpy_deferred_io,
514 };
515
516 static int metronomefb_probe(struct platform_device *dev)
517 {
518         struct fb_info *info;
519         struct metronome_board *board;
520         int retval = -ENOMEM;
521         int videomemorysize;
522         unsigned char *videomemory;
523         struct metronomefb_par *par;
524         const struct firmware *fw_entry;
525         int i;
526         int panel_type;
527         int fw, fh;
528         int epd_dt_index;
529
530         /* pick up board specific routines */
531         board = dev->dev.platform_data;
532         if (!board)
533                 return -EINVAL;
534
535         /* try to count device specific driver, if can't, platform recalls */
536         if (!try_module_get(board->owner))
537                 return -ENODEV;
538
539         info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
540         if (!info)
541                 goto err;
542
543         /* we have two blocks of memory.
544         info->screen_buffer which is vm, and is the fb used by apps.
545         par->metromem which is physically contiguous memory and
546         contains the display controller commands, waveform,
547         processed image data and padding. this is the data pulled
548         by the device's LCD controller and pushed to Metronome.
549         the metromem memory is allocated by the board driver and
550         is provided to us */
551
552         panel_type = board->get_panel_type();
553         switch (panel_type) {
554         case 6:
555                 epd_dt_index = 0;
556                 break;
557         case 8:
558                 epd_dt_index = 1;
559                 break;
560         case 97:
561                 epd_dt_index = 2;
562                 break;
563         default:
564                 dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
565                 epd_dt_index = 0;
566                 break;
567         }
568
569         fw = epd_frame_table[epd_dt_index].fw;
570         fh = epd_frame_table[epd_dt_index].fh;
571
572         /* we need to add a spare page because our csum caching scheme walks
573          * to the end of the page */
574         videomemorysize = PAGE_SIZE + (fw * fh);
575         videomemory = vzalloc(videomemorysize);
576         if (!videomemory)
577                 goto err_fb_rel;
578
579         info->screen_buffer = videomemory;
580         info->fbops = &metronomefb_ops;
581
582         metronomefb_fix.line_length = fw;
583         metronomefb_var.xres = fw;
584         metronomefb_var.yres = fh;
585         metronomefb_var.xres_virtual = fw;
586         metronomefb_var.yres_virtual = fh;
587         info->var = metronomefb_var;
588         info->fix = metronomefb_fix;
589         info->fix.smem_len = videomemorysize;
590         par = info->par;
591         par->info = info;
592         par->board = board;
593         par->dt = epd_dt_index;
594         init_waitqueue_head(&par->waitq);
595
596         /* this table caches per page csum values. */
597         par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
598         if (!par->csum_table)
599                 goto err_vfree;
600
601         /* the physical framebuffer that we use is setup by
602          * the platform device driver. It will provide us
603          * with cmd, wfm and image memory in a contiguous area. */
604         retval = board->setup_fb(par);
605         if (retval) {
606                 dev_err(&dev->dev, "Failed to setup fb\n");
607                 goto err_csum_table;
608         }
609
610         /* after this point we should have a framebuffer */
611         if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
612                 (!par->metromem_dma)) {
613                 dev_err(&dev->dev, "fb access failure\n");
614                 retval = -EINVAL;
615                 goto err_csum_table;
616         }
617
618         info->fix.smem_start = par->metromem_dma;
619
620         /* load the waveform in. assume mode 3, temp 31 for now
621                 a) request the waveform file from userspace
622                 b) process waveform and decode into metromem */
623         retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
624         if (retval < 0) {
625                 dev_err(&dev->dev, "Failed to get waveform\n");
626                 goto err_csum_table;
627         }
628
629         retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
630                                 par);
631         release_firmware(fw_entry);
632         if (retval < 0) {
633                 dev_err(&dev->dev, "Failed processing waveform\n");
634                 goto err_csum_table;
635         }
636
637         retval = board->setup_irq(info);
638         if (retval)
639                 goto err_csum_table;
640
641         retval = metronome_init_regs(par);
642         if (retval < 0)
643                 goto err_free_irq;
644
645         info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
646
647         info->fbdefio = &metronomefb_defio;
648         fb_deferred_io_init(info);
649
650         retval = fb_alloc_cmap(&info->cmap, 8, 0);
651         if (retval < 0) {
652                 dev_err(&dev->dev, "Failed to allocate colormap\n");
653                 goto err_free_irq;
654         }
655
656         /* set cmap */
657         for (i = 0; i < 8; i++)
658                 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
659         memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
660         memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
661
662         retval = register_framebuffer(info);
663         if (retval < 0)
664                 goto err_cmap;
665
666         platform_set_drvdata(dev, info);
667
668         dev_dbg(&dev->dev,
669                 "fb%d: Metronome frame buffer device, using %dK of video"
670                 " memory\n", info->node, videomemorysize >> 10);
671
672         return 0;
673
674 err_cmap:
675         fb_dealloc_cmap(&info->cmap);
676 err_free_irq:
677         board->cleanup(par);
678 err_csum_table:
679         vfree(par->csum_table);
680 err_vfree:
681         vfree(videomemory);
682 err_fb_rel:
683         framebuffer_release(info);
684 err:
685         module_put(board->owner);
686         return retval;
687 }
688
689 static void metronomefb_remove(struct platform_device *dev)
690 {
691         struct fb_info *info = platform_get_drvdata(dev);
692
693         if (info) {
694                 struct metronomefb_par *par = info->par;
695
696                 unregister_framebuffer(info);
697                 fb_deferred_io_cleanup(info);
698                 fb_dealloc_cmap(&info->cmap);
699                 par->board->cleanup(par);
700                 vfree(par->csum_table);
701                 vfree(info->screen_buffer);
702                 module_put(par->board->owner);
703                 dev_dbg(&dev->dev, "calling release\n");
704                 framebuffer_release(info);
705         }
706 }
707
708 static struct platform_driver metronomefb_driver = {
709         .probe  = metronomefb_probe,
710         .remove_new = metronomefb_remove,
711         .driver = {
712                 .name   = "metronomefb",
713         },
714 };
715 module_platform_driver(metronomefb_driver);
716
717 module_param(user_wfm_size, uint, 0);
718 MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
719
720 MODULE_DESCRIPTION("fbdev driver for Metronome controller");
721 MODULE_AUTHOR("Jaya Kumar");
722 MODULE_LICENSE("GPL");
723
724 MODULE_FIRMWARE("metronome.wbf");