2 * broadsheetfb.c -- FB driver for E-Ink Broadsheet controller
4 * Copyright (C) 2008, Jaya Kumar
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
10 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
12 * This driver is written to be used with the Broadsheet display controller.
14 * It is intended to be architecture independent. A board specific driver
15 * must be used to perform all the physical IO interactions.
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/errno.h>
22 #include <linux/string.h>
24 #include <linux/slab.h>
25 #include <linux/vmalloc.h>
26 #include <linux/delay.h>
27 #include <linux/interrupt.h>
29 #include <linux/init.h>
30 #include <linux/platform_device.h>
31 #include <linux/list.h>
32 #include <linux/uaccess.h>
34 #include <video/broadsheetfb.h>
36 /* track panel specific parameters */
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 */
55 .sdcfg = (100 | (1 << 8) | (1 << 9)),
57 .lutfmt = (4 | (1 << 7)),
59 .fendfbegin = (10 << 8) | 4,
61 .lendlbegin = (100 << 8) | 4,
64 { /* custom 3.7" flexible on PET or steel */
67 .sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)),
69 .lutfmt = (4 | (1 << 7)),
71 .fendfbegin = (80 << 8) | 4,
73 .lendlbegin = (80 << 8) | 20,
76 { /* standard 9.7" on TFT backplane */
79 .sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)),
81 .lutfmt = (4 | (1 << 7)),
83 .fendfbegin = (4 << 8) | 4,
85 .lendlbegin = (60 << 8) | 10,
93 static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata = {
95 .type = FB_TYPE_PACKED_PIXELS,
96 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
100 .line_length = DPY_W,
101 .accel = FB_ACCEL_NONE,
104 static struct fb_var_screeninfo broadsheetfb_var __devinitdata = {
107 .xres_virtual = DPY_W,
108 .yres_virtual = DPY_H,
112 .green = { 0, 4, 0 },
114 .transp = { 0, 0, 0 },
117 /* main broadsheetfb functions */
118 static void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data)
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);
125 static void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data)
127 par->board->set_ctl(par, BS_DC, 0);
128 broadsheet_gpio_issue_data(par, data);
131 static void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data)
133 par->board->wait_for_rdy(par);
135 par->board->set_ctl(par, BS_CS, 0);
136 broadsheet_gpio_issue_cmd(par, data);
137 par->board->set_ctl(par, BS_DC, 1);
138 par->board->set_ctl(par, BS_CS, 1);
141 static void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
146 par->board->wait_for_rdy(par);
148 par->board->set_ctl(par, BS_CS, 0);
149 broadsheet_gpio_issue_cmd(par, cmd);
150 par->board->set_ctl(par, BS_DC, 1);
152 for (i = 0; i < argc; i++)
153 broadsheet_gpio_issue_data(par, argv[i]);
154 par->board->set_ctl(par, BS_CS, 1);
157 static void broadsheet_mmio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
162 par->board->mmio_write(par, BS_MMIO_CMD, cmd);
164 for (i = 0; i < argc; i++)
165 par->board->mmio_write(par, BS_MMIO_DATA, argv[i]);
168 static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
170 if (par->board->mmio_write)
171 par->board->mmio_write(par, BS_MMIO_CMD, data);
173 broadsheet_gpio_send_command(par, data);
176 static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
179 if (par->board->mmio_write)
180 broadsheet_mmio_send_cmdargs(par, cmd, argc, argv);
182 broadsheet_gpio_send_cmdargs(par, cmd, argc, argv);
185 static void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size,
191 par->board->set_ctl(par, BS_CS, 0);
192 par->board->set_ctl(par, BS_DC, 1);
194 for (i = 0; i < size; i++) {
195 par->board->set_ctl(par, BS_WR, 0);
196 tmp = (data[i] & 0x0F) << 4;
197 tmp |= (data[i] & 0x0F00) << 4;
198 par->board->set_hdb(par, tmp);
199 par->board->set_ctl(par, BS_WR, 1);
202 par->board->set_ctl(par, BS_CS, 1);
205 static void broadsheet_mmio_burst_write(struct broadsheetfb_par *par, int size,
211 for (i = 0; i < size; i++) {
212 tmp = (data[i] & 0x0F) << 4;
213 tmp |= (data[i] & 0x0F00) << 4;
214 par->board->mmio_write(par, BS_MMIO_DATA, tmp);
219 static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
222 if (par->board->mmio_write)
223 broadsheet_mmio_burst_write(par, size, data);
225 broadsheet_gpio_burst_write(par, size, data);
228 static u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par)
231 /* wait for ready to go hi. (lo is busy) */
232 par->board->wait_for_rdy(par);
234 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
235 par->board->set_ctl(par, BS_DC, 1);
236 par->board->set_ctl(par, BS_CS, 0);
237 par->board->set_ctl(par, BS_WR, 0);
239 res = par->board->get_hdb(par);
242 par->board->set_ctl(par, BS_WR, 1);
243 par->board->set_ctl(par, BS_CS, 1);
249 static u16 broadsheet_get_data(struct broadsheetfb_par *par)
251 if (par->board->mmio_read)
252 return par->board->mmio_read(par);
254 return broadsheet_gpio_get_data(par);
257 static void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg,
260 /* wait for ready to go hi. (lo is busy) */
261 par->board->wait_for_rdy(par);
263 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
264 par->board->set_ctl(par, BS_CS, 0);
266 broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG);
268 par->board->set_ctl(par, BS_DC, 1);
270 broadsheet_gpio_issue_data(par, reg);
271 broadsheet_gpio_issue_data(par, data);
273 par->board->set_ctl(par, BS_CS, 1);
276 static void broadsheet_mmio_write_reg(struct broadsheetfb_par *par, u16 reg,
279 par->board->mmio_write(par, BS_MMIO_CMD, BS_CMD_WR_REG);
280 par->board->mmio_write(par, BS_MMIO_DATA, reg);
281 par->board->mmio_write(par, BS_MMIO_DATA, data);
285 static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
288 if (par->board->mmio_write)
289 broadsheet_mmio_write_reg(par, reg, data);
291 broadsheet_gpio_write_reg(par, reg, data);
294 static void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg,
297 broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF);
298 broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF);
302 static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
304 broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, ®);
305 par->board->wait_for_rdy(par);
306 return broadsheet_get_data(par);
309 static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
312 int xres = par->info->var.xres;
313 int yres = par->info->var.yres;
315 args[0] = panel_table[par->panel_index].w;
316 args[1] = panel_table[par->panel_index].h;
317 args[2] = panel_table[par->panel_index].sdcfg;
318 args[3] = panel_table[par->panel_index].gdcfg;
319 args[4] = panel_table[par->panel_index].lutfmt;
320 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
322 /* did the controller really set it? */
323 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
325 args[0] = panel_table[par->panel_index].fsynclen;
326 args[1] = panel_table[par->panel_index].fendfbegin;
327 args[2] = panel_table[par->panel_index].lsynclen;
328 args[3] = panel_table[par->panel_index].lendlbegin;
329 args[4] = panel_table[par->panel_index].pixclk;
330 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
332 broadsheet_write_reg32(par, 0x310, xres*yres*2);
337 broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
339 broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
341 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
343 broadsheet_write_reg(par, 0x330, 0x84);
345 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
347 args[0] = (0x3 << 4);
348 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
351 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
353 broadsheet_burst_write(par, (panel_table[par->panel_index].w *
354 panel_table[par->panel_index].h)/2,
355 (u16 *) par->info->screen_base);
357 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
360 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
362 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
364 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
366 par->board->wait_for_rdy(par);
369 static void __devinit broadsheet_init(struct broadsheetfb_par *par)
371 broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
372 /* the controller needs a second */
374 broadsheet_init_display(par);
377 static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
381 unsigned char *buf = (unsigned char *)par->info->screen_base;
383 /* y1 must be a multiple of 4 so drop the lower bits */
385 /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
391 args[3] = cpu_to_le16(par->info->var.xres);
393 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
396 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
398 buf += y1 * par->info->var.xres;
399 broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
402 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
405 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
407 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
409 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
411 par->board->wait_for_rdy(par);
415 static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
420 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
423 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
424 broadsheet_burst_write(par, (panel_table[par->panel_index].w *
425 panel_table[par->panel_index].h)/2,
426 (u16 *) par->info->screen_base);
428 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
431 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
433 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
435 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
437 par->board->wait_for_rdy(par);
441 /* this is called back from the deferred io workqueue */
442 static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
443 struct list_head *pagelist)
448 struct fb_deferred_io *fbdefio = info->fbdefio;
450 u16 yres = info->var.yres;
451 u16 xres = info->var.xres;
453 /* height increment is fixed per page */
454 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
456 /* walk the written page list and swizzle the data */
457 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
458 if (prev_index < 0) {
459 /* just starting so assign first page */
460 y1 = (cur->index << PAGE_SHIFT) / xres;
462 } else if ((prev_index + 1) == cur->index) {
463 /* this page is consecutive so increase our height */
466 /* page not consecutive, issue previous update first */
467 broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
468 /* start over with our non consecutive page */
469 y1 = (cur->index << PAGE_SHIFT) / xres;
472 prev_index = cur->index;
475 /* if we still have any pages to update we do so now */
477 /* its a full screen update, just do it */
478 broadsheetfb_dpy_update(info->par);
480 broadsheetfb_dpy_update_pages(info->par, y1,
481 min((u16) (y1 + h), yres));
485 static void broadsheetfb_fillrect(struct fb_info *info,
486 const struct fb_fillrect *rect)
488 struct broadsheetfb_par *par = info->par;
490 sys_fillrect(info, rect);
492 broadsheetfb_dpy_update(par);
495 static void broadsheetfb_copyarea(struct fb_info *info,
496 const struct fb_copyarea *area)
498 struct broadsheetfb_par *par = info->par;
500 sys_copyarea(info, area);
502 broadsheetfb_dpy_update(par);
505 static void broadsheetfb_imageblit(struct fb_info *info,
506 const struct fb_image *image)
508 struct broadsheetfb_par *par = info->par;
510 sys_imageblit(info, image);
512 broadsheetfb_dpy_update(par);
516 * this is the slow path from userspace. they can seek and write to
517 * the fb. it's inefficient to do anything less than a full screen draw
519 static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
520 size_t count, loff_t *ppos)
522 struct broadsheetfb_par *par = info->par;
523 unsigned long p = *ppos;
526 unsigned long total_size;
528 if (info->state != FBINFO_STATE_RUNNING)
531 total_size = info->fix.smem_len;
536 if (count > total_size) {
541 if (count + p > total_size) {
545 count = total_size - p;
548 dst = (void *)(info->screen_base + p);
550 if (copy_from_user(dst, buf, count))
556 broadsheetfb_dpy_update(par);
558 return (err) ? err : count;
561 static struct fb_ops broadsheetfb_ops = {
562 .owner = THIS_MODULE,
563 .fb_read = fb_sys_read,
564 .fb_write = broadsheetfb_write,
565 .fb_fillrect = broadsheetfb_fillrect,
566 .fb_copyarea = broadsheetfb_copyarea,
567 .fb_imageblit = broadsheetfb_imageblit,
570 static struct fb_deferred_io broadsheetfb_defio = {
572 .deferred_io = broadsheetfb_dpy_deferred_io,
575 static int __devinit broadsheetfb_probe(struct platform_device *dev)
577 struct fb_info *info;
578 struct broadsheet_board *board;
579 int retval = -ENOMEM;
581 unsigned char *videomemory;
582 struct broadsheetfb_par *par;
587 /* pick up board specific routines */
588 board = dev->dev.platform_data;
592 /* try to count device specific driver, if can't, platform recalls */
593 if (!try_module_get(board->owner))
596 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
600 switch (board->get_panel_type()) {
613 dpyw = panel_table[panel_index].w;
614 dpyh = panel_table[panel_index].h;
616 videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);
618 videomemory = vmalloc(videomemorysize);
622 memset(videomemory, 0, videomemorysize);
624 info->screen_base = (char *)videomemory;
625 info->fbops = &broadsheetfb_ops;
627 broadsheetfb_var.xres = dpyw;
628 broadsheetfb_var.yres = dpyh;
629 broadsheetfb_var.xres_virtual = dpyw;
630 broadsheetfb_var.yres_virtual = dpyh;
631 info->var = broadsheetfb_var;
633 broadsheetfb_fix.line_length = dpyw;
634 info->fix = broadsheetfb_fix;
635 info->fix.smem_len = videomemorysize;
637 par->panel_index = panel_index;
640 par->write_reg = broadsheet_write_reg;
641 par->read_reg = broadsheet_read_reg;
642 init_waitqueue_head(&par->waitq);
644 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
646 info->fbdefio = &broadsheetfb_defio;
647 fb_deferred_io_init(info);
649 retval = fb_alloc_cmap(&info->cmap, 16, 0);
651 dev_err(&dev->dev, "Failed to allocate colormap\n");
656 for (i = 0; i < 16; i++)
657 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;
658 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);
659 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);
661 retval = par->board->setup_irq(info);
665 /* this inits the dpy */
666 retval = board->init(par);
670 broadsheet_init(par);
672 retval = register_framebuffer(info);
675 platform_set_drvdata(dev, info);
678 "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
679 info->node, videomemorysize >> 10);
687 fb_dealloc_cmap(&info->cmap);
691 framebuffer_release(info);
693 module_put(board->owner);
698 static int __devexit broadsheetfb_remove(struct platform_device *dev)
700 struct fb_info *info = platform_get_drvdata(dev);
703 struct broadsheetfb_par *par = info->par;
704 unregister_framebuffer(info);
705 fb_deferred_io_cleanup(info);
706 par->board->cleanup(par);
707 fb_dealloc_cmap(&info->cmap);
708 vfree((void *)info->screen_base);
709 module_put(par->board->owner);
710 framebuffer_release(info);
715 static struct platform_driver broadsheetfb_driver = {
716 .probe = broadsheetfb_probe,
717 .remove = broadsheetfb_remove,
719 .owner = THIS_MODULE,
720 .name = "broadsheetfb",
724 static int __init broadsheetfb_init(void)
726 return platform_driver_register(&broadsheetfb_driver);
729 static void __exit broadsheetfb_exit(void)
731 platform_driver_unregister(&broadsheetfb_driver);
734 module_init(broadsheetfb_init);
735 module_exit(broadsheetfb_exit);
737 MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
738 MODULE_AUTHOR("Jaya Kumar");
739 MODULE_LICENSE("GPL");