broadsheetfb: add multiple panel type support
[linux-2.6.git] / drivers / video / broadsheetfb.c
1 /*
2  * broadsheetfb.c -- FB driver for E-Ink Broadsheet 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 driver is written to be used with the Broadsheet display controller.
13  *
14  * It is intended to be architecture independent. A board specific driver
15  * must be used to perform all the physical IO interactions.
16  *
17  */
18
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/errno.h>
22 #include <linux/string.h>
23 #include <linux/mm.h>
24 #include <linux/slab.h>
25 #include <linux/vmalloc.h>
26 #include <linux/delay.h>
27 #include <linux/interrupt.h>
28 #include <linux/fb.h>
29 #include <linux/init.h>
30 #include <linux/platform_device.h>
31 #include <linux/list.h>
32 #include <linux/uaccess.h>
33
34 #include <video/broadsheetfb.h>
35
36 /* track panel specific parameters */
37 struct panel_info {
38         int w;
39         int h;
40         u16 sdcfg;
41         u16 gdcfg;
42         u16 lutfmt;
43         u16 fsynclen;
44         u16 fendfbegin;
45         u16 lsynclen;
46         u16 lendlbegin;
47         u16 pixclk;
48 };
49
50 /* table of panel specific parameters to be indexed into by the board drivers */
51 static struct panel_info panel_table[] = {
52         {       /* standard 6" on TFT backplane */
53                 .w = 800,
54                 .h = 600,
55                 .sdcfg = (100 | (1 << 8) | (1 << 9)),
56                 .gdcfg = 2,
57                 .lutfmt = (4 | (1 << 7)),
58                 .fsynclen = 4,
59                 .fendfbegin = (10 << 8) | 4,
60                 .lsynclen = 10,
61                 .lendlbegin = (100 << 8) | 4,
62                 .pixclk = 6,
63         },
64         {       /* custom 3.7" flexible on PET or steel */
65                 .w = 320,
66                 .h = 240,
67                 .sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)),
68                 .gdcfg = 3,
69                 .lutfmt = (4 | (1 << 7)),
70                 .fsynclen = 0,
71                 .fendfbegin = (80 << 8) | 4,
72                 .lsynclen = 10,
73                 .lendlbegin = (80 << 8) | 20,
74                 .pixclk = 14,
75         },
76         {       /* standard 9.7" on TFT backplane */
77                 .w = 1200,
78                 .h = 825,
79                 .sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)),
80                 .gdcfg = 2,
81                 .lutfmt = (4 | (1 << 7)),
82                 .fsynclen = 0,
83                 .fendfbegin = (4 << 8) | 4,
84                 .lsynclen = 4,
85                 .lendlbegin = (60 << 8) | 10,
86                 .pixclk = 3,
87         },
88 };
89
90 #define DPY_W 800
91 #define DPY_H 600
92
93 static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata = {
94         .id =           "broadsheetfb",
95         .type =         FB_TYPE_PACKED_PIXELS,
96         .visual =       FB_VISUAL_STATIC_PSEUDOCOLOR,
97         .xpanstep =     0,
98         .ypanstep =     0,
99         .ywrapstep =    0,
100         .line_length =  DPY_W,
101         .accel =        FB_ACCEL_NONE,
102 };
103
104 static struct fb_var_screeninfo broadsheetfb_var __devinitdata = {
105         .xres           = DPY_W,
106         .yres           = DPY_H,
107         .xres_virtual   = DPY_W,
108         .yres_virtual   = DPY_H,
109         .bits_per_pixel = 8,
110         .grayscale      = 1,
111         .red =          { 0, 4, 0 },
112         .green =        { 0, 4, 0 },
113         .blue =         { 0, 4, 0 },
114         .transp =       { 0, 0, 0 },
115 };
116
117 /* main broadsheetfb functions */
118 static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data)
119 {
120         par->board->set_ctl(par, BS_WR, 0);
121         par->board->set_hdb(par, data);
122         par->board->set_ctl(par, BS_WR, 1);
123 }
124
125 static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data)
126 {
127         par->board->set_ctl(par, BS_DC, 0);
128         broadsheet_issue_data(par, data);
129 }
130
131 static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
132 {
133         par->board->wait_for_rdy(par);
134
135         par->board->set_ctl(par, BS_CS, 0);
136         broadsheet_issue_cmd(par, data);
137         par->board->set_ctl(par, BS_DC, 1);
138         par->board->set_ctl(par, BS_CS, 1);
139 }
140
141 static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
142                                         int argc, u16 *argv)
143 {
144         int i;
145
146         par->board->wait_for_rdy(par);
147
148         par->board->set_ctl(par, BS_CS, 0);
149         broadsheet_issue_cmd(par, cmd);
150         par->board->set_ctl(par, BS_DC, 1);
151
152         for (i = 0; i < argc; i++)
153                 broadsheet_issue_data(par, argv[i]);
154         par->board->set_ctl(par, BS_CS, 1);
155 }
156
157 static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
158                                         u16 *data)
159 {
160         int i;
161         u16 tmp;
162
163         par->board->set_ctl(par, BS_CS, 0);
164         par->board->set_ctl(par, BS_DC, 1);
165
166         for (i = 0; i < size; i++) {
167                 par->board->set_ctl(par, BS_WR, 0);
168                 tmp = (data[i] & 0x0F) << 4;
169                 tmp |= (data[i] & 0x0F00) << 4;
170                 par->board->set_hdb(par, tmp);
171                 par->board->set_ctl(par, BS_WR, 1);
172         }
173
174         par->board->set_ctl(par, BS_CS, 1);
175 }
176
177 static u16 broadsheet_get_data(struct broadsheetfb_par *par)
178 {
179         u16 res;
180         /* wait for ready to go hi. (lo is busy) */
181         par->board->wait_for_rdy(par);
182
183         /* cs lo, dc lo for cmd, we lo for each data, db as usual */
184         par->board->set_ctl(par, BS_DC, 1);
185         par->board->set_ctl(par, BS_CS, 0);
186         par->board->set_ctl(par, BS_WR, 0);
187
188         res = par->board->get_hdb(par);
189
190         /* strobe wr */
191         par->board->set_ctl(par, BS_WR, 1);
192         par->board->set_ctl(par, BS_CS, 1);
193
194         return res;
195 }
196
197 static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
198                                         u16 data)
199 {
200         /* wait for ready to go hi. (lo is busy) */
201         par->board->wait_for_rdy(par);
202
203         /* cs lo, dc lo for cmd, we lo for each data, db as usual */
204         par->board->set_ctl(par, BS_CS, 0);
205
206         broadsheet_issue_cmd(par, BS_CMD_WR_REG);
207
208         par->board->set_ctl(par, BS_DC, 1);
209
210         broadsheet_issue_data(par, reg);
211         broadsheet_issue_data(par, data);
212
213         par->board->set_ctl(par, BS_CS, 1);
214 }
215
216 static void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg,
217                                         u32 data)
218 {
219         broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF);
220         broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF);
221 }
222
223
224 static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
225 {
226         broadsheet_send_command(par, reg);
227         msleep(100);
228         return broadsheet_get_data(par);
229 }
230
231 static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
232 {
233         u16 args[5];
234         int xres = par->info->var.xres;
235         int yres = par->info->var.yres;
236
237         args[0] = panel_table[par->panel_index].w;
238         args[1] = panel_table[par->panel_index].h;
239         args[2] = panel_table[par->panel_index].sdcfg;
240         args[3] = panel_table[par->panel_index].gdcfg;
241         args[4] = panel_table[par->panel_index].lutfmt;
242         broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
243
244         /* did the controller really set it? */
245         broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
246
247         args[0] = panel_table[par->panel_index].fsynclen;
248         args[1] = panel_table[par->panel_index].fendfbegin;
249         args[2] = panel_table[par->panel_index].lsynclen;
250         args[3] = panel_table[par->panel_index].lendlbegin;
251         args[4] = panel_table[par->panel_index].pixclk;
252         broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
253
254         broadsheet_write_reg32(par, 0x310, xres*yres*2);
255
256         /* setup waveform */
257         args[0] = 0x886;
258         args[1] = 0;
259         broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
260
261         broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
262
263         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
264
265         broadsheet_write_reg(par, 0x330, 0x84);
266
267         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
268
269         args[0] = (0x3 << 4);
270         broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
271
272         args[0] = 0x154;
273         broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
274
275         broadsheet_burst_write(par, (panel_table[par->panel_index].w *
276                                         panel_table[par->panel_index].h)/2,
277                                         (u16 *) par->info->screen_base);
278
279         broadsheet_send_command(par, BS_CMD_LD_IMG_END);
280
281         args[0] = 0x4300;
282         broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
283
284         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
285
286         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
287
288         par->board->wait_for_rdy(par);
289 }
290
291 static void __devinit broadsheet_init(struct broadsheetfb_par *par)
292 {
293         broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
294         /* the controller needs a second */
295         msleep(1000);
296         broadsheet_init_display(par);
297 }
298
299 static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
300                                                 u16 y1, u16 y2)
301 {
302         u16 args[5];
303         unsigned char *buf = (unsigned char *)par->info->screen_base;
304
305         /* y1 must be a multiple of 4 so drop the lower bits */
306         y1 &= 0xFFFC;
307         /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
308         y2 |= 0x0003;
309
310         args[0] = 0x3 << 4;
311         args[1] = 0;
312         args[2] = y1;
313         args[3] = cpu_to_le16(par->info->var.xres);
314         args[4] = y2;
315         broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
316
317         args[0] = 0x154;
318         broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
319
320         buf += y1 * par->info->var.xres;
321         broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
322                                 (u16 *) buf);
323
324         broadsheet_send_command(par, BS_CMD_LD_IMG_END);
325
326         args[0] = 0x4300;
327         broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
328
329         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
330
331         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
332
333         par->board->wait_for_rdy(par);
334
335 }
336
337 static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
338 {
339         u16 args[5];
340
341         args[0] = 0x3 << 4;
342         broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
343
344         args[0] = 0x154;
345         broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
346         broadsheet_burst_write(par, (panel_table[par->panel_index].w *
347                                         panel_table[par->panel_index].h)/2,
348                                         (u16 *) par->info->screen_base);
349
350         broadsheet_send_command(par, BS_CMD_LD_IMG_END);
351
352         args[0] = 0x4300;
353         broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
354
355         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
356
357         broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
358
359         par->board->wait_for_rdy(par);
360
361 }
362
363 /* this is called back from the deferred io workqueue */
364 static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
365                                 struct list_head *pagelist)
366 {
367         u16 y1 = 0, h = 0;
368         int prev_index = -1;
369         struct page *cur;
370         struct fb_deferred_io *fbdefio = info->fbdefio;
371         int h_inc;
372         u16 yres = info->var.yres;
373         u16 xres = info->var.xres;
374
375         /* height increment is fixed per page */
376         h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
377
378         /* walk the written page list and swizzle the data */
379         list_for_each_entry(cur, &fbdefio->pagelist, lru) {
380                 if (prev_index < 0) {
381                         /* just starting so assign first page */
382                         y1 = (cur->index << PAGE_SHIFT) / xres;
383                         h = h_inc;
384                 } else if ((prev_index + 1) == cur->index) {
385                         /* this page is consecutive so increase our height */
386                         h += h_inc;
387                 } else {
388                         /* page not consecutive, issue previous update first */
389                         broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
390                         /* start over with our non consecutive page */
391                         y1 = (cur->index << PAGE_SHIFT) / xres;
392                         h = h_inc;
393                 }
394                 prev_index = cur->index;
395         }
396
397         /* if we still have any pages to update we do so now */
398         if (h >= yres) {
399                 /* its a full screen update, just do it */
400                 broadsheetfb_dpy_update(info->par);
401         } else {
402                 broadsheetfb_dpy_update_pages(info->par, y1,
403                                                 min((u16) (y1 + h), yres));
404         }
405 }
406
407 static void broadsheetfb_fillrect(struct fb_info *info,
408                                    const struct fb_fillrect *rect)
409 {
410         struct broadsheetfb_par *par = info->par;
411
412         sys_fillrect(info, rect);
413
414         broadsheetfb_dpy_update(par);
415 }
416
417 static void broadsheetfb_copyarea(struct fb_info *info,
418                                    const struct fb_copyarea *area)
419 {
420         struct broadsheetfb_par *par = info->par;
421
422         sys_copyarea(info, area);
423
424         broadsheetfb_dpy_update(par);
425 }
426
427 static void broadsheetfb_imageblit(struct fb_info *info,
428                                 const struct fb_image *image)
429 {
430         struct broadsheetfb_par *par = info->par;
431
432         sys_imageblit(info, image);
433
434         broadsheetfb_dpy_update(par);
435 }
436
437 /*
438  * this is the slow path from userspace. they can seek and write to
439  * the fb. it's inefficient to do anything less than a full screen draw
440  */
441 static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
442                                 size_t count, loff_t *ppos)
443 {
444         struct broadsheetfb_par *par = info->par;
445         unsigned long p = *ppos;
446         void *dst;
447         int err = 0;
448         unsigned long total_size;
449
450         if (info->state != FBINFO_STATE_RUNNING)
451                 return -EPERM;
452
453         total_size = info->fix.smem_len;
454
455         if (p > total_size)
456                 return -EFBIG;
457
458         if (count > total_size) {
459                 err = -EFBIG;
460                 count = total_size;
461         }
462
463         if (count + p > total_size) {
464                 if (!err)
465                         err = -ENOSPC;
466
467                 count = total_size - p;
468         }
469
470         dst = (void *)(info->screen_base + p);
471
472         if (copy_from_user(dst, buf, count))
473                 err = -EFAULT;
474
475         if  (!err)
476                 *ppos += count;
477
478         broadsheetfb_dpy_update(par);
479
480         return (err) ? err : count;
481 }
482
483 static struct fb_ops broadsheetfb_ops = {
484         .owner          = THIS_MODULE,
485         .fb_read        = fb_sys_read,
486         .fb_write       = broadsheetfb_write,
487         .fb_fillrect    = broadsheetfb_fillrect,
488         .fb_copyarea    = broadsheetfb_copyarea,
489         .fb_imageblit   = broadsheetfb_imageblit,
490 };
491
492 static struct fb_deferred_io broadsheetfb_defio = {
493         .delay          = HZ/4,
494         .deferred_io    = broadsheetfb_dpy_deferred_io,
495 };
496
497 static int __devinit broadsheetfb_probe(struct platform_device *dev)
498 {
499         struct fb_info *info;
500         struct broadsheet_board *board;
501         int retval = -ENOMEM;
502         int videomemorysize;
503         unsigned char *videomemory;
504         struct broadsheetfb_par *par;
505         int i;
506         int dpyw, dpyh;
507         int panel_index;
508
509         /* pick up board specific routines */
510         board = dev->dev.platform_data;
511         if (!board)
512                 return -EINVAL;
513
514         /* try to count device specific driver, if can't, platform recalls */
515         if (!try_module_get(board->owner))
516                 return -ENODEV;
517
518         info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
519         if (!info)
520                 goto err;
521
522         switch (board->get_panel_type()) {
523         case 37:
524                 panel_index = 1;
525                 break;
526         case 97:
527                 panel_index = 2;
528                 break;
529         case 6:
530         default:
531                 panel_index = 0;
532                 break;
533         }
534
535         dpyw = panel_table[panel_index].w;
536         dpyh = panel_table[panel_index].h;
537
538         videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);
539
540         videomemory = vmalloc(videomemorysize);
541         if (!videomemory)
542                 goto err_fb_rel;
543
544         memset(videomemory, 0, videomemorysize);
545
546         info->screen_base = (char *)videomemory;
547         info->fbops = &broadsheetfb_ops;
548
549         broadsheetfb_var.xres = dpyw;
550         broadsheetfb_var.yres = dpyh;
551         broadsheetfb_var.xres_virtual = dpyw;
552         broadsheetfb_var.yres_virtual = dpyh;
553         info->var = broadsheetfb_var;
554
555         broadsheetfb_fix.line_length = dpyw;
556         info->fix = broadsheetfb_fix;
557         info->fix.smem_len = videomemorysize;
558         par = info->par;
559         par->panel_index = panel_index;
560         par->info = info;
561         par->board = board;
562         par->write_reg = broadsheet_write_reg;
563         par->read_reg = broadsheet_read_reg;
564         init_waitqueue_head(&par->waitq);
565
566         info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
567
568         info->fbdefio = &broadsheetfb_defio;
569         fb_deferred_io_init(info);
570
571         retval = fb_alloc_cmap(&info->cmap, 16, 0);
572         if (retval < 0) {
573                 dev_err(&dev->dev, "Failed to allocate colormap\n");
574                 goto err_vfree;
575         }
576
577         /* set cmap */
578         for (i = 0; i < 16; i++)
579                 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;
580         memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);
581         memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);
582
583         retval = par->board->setup_irq(info);
584         if (retval < 0)
585                 goto err_cmap;
586
587         /* this inits the dpy */
588         retval = board->init(par);
589         if (retval < 0)
590                 goto err_free_irq;
591
592         broadsheet_init(par);
593
594         retval = register_framebuffer(info);
595         if (retval < 0)
596                 goto err_free_irq;
597         platform_set_drvdata(dev, info);
598
599         printk(KERN_INFO
600                "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
601                info->node, videomemorysize >> 10);
602
603
604         return 0;
605
606 err_free_irq:
607         board->cleanup(par);
608 err_cmap:
609         fb_dealloc_cmap(&info->cmap);
610 err_vfree:
611         vfree(videomemory);
612 err_fb_rel:
613         framebuffer_release(info);
614 err:
615         module_put(board->owner);
616         return retval;
617
618 }
619
620 static int __devexit broadsheetfb_remove(struct platform_device *dev)
621 {
622         struct fb_info *info = platform_get_drvdata(dev);
623
624         if (info) {
625                 struct broadsheetfb_par *par = info->par;
626                 unregister_framebuffer(info);
627                 fb_deferred_io_cleanup(info);
628                 par->board->cleanup(par);
629                 fb_dealloc_cmap(&info->cmap);
630                 vfree((void *)info->screen_base);
631                 module_put(par->board->owner);
632                 framebuffer_release(info);
633         }
634         return 0;
635 }
636
637 static struct platform_driver broadsheetfb_driver = {
638         .probe  = broadsheetfb_probe,
639         .remove = broadsheetfb_remove,
640         .driver = {
641                 .owner  = THIS_MODULE,
642                 .name   = "broadsheetfb",
643         },
644 };
645
646 static int __init broadsheetfb_init(void)
647 {
648         return platform_driver_register(&broadsheetfb_driver);
649 }
650
651 static void __exit broadsheetfb_exit(void)
652 {
653         platform_driver_unregister(&broadsheetfb_driver);
654 }
655
656 module_init(broadsheetfb_init);
657 module_exit(broadsheetfb_exit);
658
659 MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
660 MODULE_AUTHOR("Jaya Kumar");
661 MODULE_LICENSE("GPL");