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_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_issue_cmd(struct broadsheetfb_par *par, u16 data)
127 par->board->set_ctl(par, BS_DC, 0);
128 broadsheet_issue_data(par, data);
131 static void broadsheet_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_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_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_issue_cmd(par, cmd);
150 par->board->set_ctl(par, BS_DC, 1);
152 for (i = 0; i < argc; i++)
153 broadsheet_issue_data(par, argv[i]);
154 par->board->set_ctl(par, BS_CS, 1);
157 static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
163 par->board->set_ctl(par, BS_CS, 0);
164 par->board->set_ctl(par, BS_DC, 1);
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);
174 par->board->set_ctl(par, BS_CS, 1);
177 static u16 broadsheet_get_data(struct broadsheetfb_par *par)
180 /* wait for ready to go hi. (lo is busy) */
181 par->board->wait_for_rdy(par);
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);
188 res = par->board->get_hdb(par);
191 par->board->set_ctl(par, BS_WR, 1);
192 par->board->set_ctl(par, BS_CS, 1);
197 static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
200 /* wait for ready to go hi. (lo is busy) */
201 par->board->wait_for_rdy(par);
203 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
204 par->board->set_ctl(par, BS_CS, 0);
206 broadsheet_issue_cmd(par, BS_CMD_WR_REG);
208 par->board->set_ctl(par, BS_DC, 1);
210 broadsheet_issue_data(par, reg);
211 broadsheet_issue_data(par, data);
213 par->board->set_ctl(par, BS_CS, 1);
216 static void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg,
219 broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF);
220 broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF);
224 static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
226 broadsheet_send_command(par, reg);
228 return broadsheet_get_data(par);
231 static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
234 int xres = par->info->var.xres;
235 int yres = par->info->var.yres;
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);
244 /* did the controller really set it? */
245 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
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);
254 broadsheet_write_reg32(par, 0x310, xres*yres*2);
259 broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
261 broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
263 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
265 broadsheet_write_reg(par, 0x330, 0x84);
267 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
269 args[0] = (0x3 << 4);
270 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
273 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
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);
279 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
282 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
284 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
286 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
288 par->board->wait_for_rdy(par);
291 static void __devinit broadsheet_init(struct broadsheetfb_par *par)
293 broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
294 /* the controller needs a second */
296 broadsheet_init_display(par);
299 static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
303 unsigned char *buf = (unsigned char *)par->info->screen_base;
305 /* y1 must be a multiple of 4 so drop the lower bits */
307 /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
313 args[3] = cpu_to_le16(par->info->var.xres);
315 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
318 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
320 buf += y1 * par->info->var.xres;
321 broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
324 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
327 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
329 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
331 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
333 par->board->wait_for_rdy(par);
337 static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
342 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
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);
350 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
353 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
355 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
357 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
359 par->board->wait_for_rdy(par);
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)
370 struct fb_deferred_io *fbdefio = info->fbdefio;
372 u16 yres = info->var.yres;
373 u16 xres = info->var.xres;
375 /* height increment is fixed per page */
376 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
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;
384 } else if ((prev_index + 1) == cur->index) {
385 /* this page is consecutive so increase our height */
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;
394 prev_index = cur->index;
397 /* if we still have any pages to update we do so now */
399 /* its a full screen update, just do it */
400 broadsheetfb_dpy_update(info->par);
402 broadsheetfb_dpy_update_pages(info->par, y1,
403 min((u16) (y1 + h), yres));
407 static void broadsheetfb_fillrect(struct fb_info *info,
408 const struct fb_fillrect *rect)
410 struct broadsheetfb_par *par = info->par;
412 sys_fillrect(info, rect);
414 broadsheetfb_dpy_update(par);
417 static void broadsheetfb_copyarea(struct fb_info *info,
418 const struct fb_copyarea *area)
420 struct broadsheetfb_par *par = info->par;
422 sys_copyarea(info, area);
424 broadsheetfb_dpy_update(par);
427 static void broadsheetfb_imageblit(struct fb_info *info,
428 const struct fb_image *image)
430 struct broadsheetfb_par *par = info->par;
432 sys_imageblit(info, image);
434 broadsheetfb_dpy_update(par);
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
441 static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
442 size_t count, loff_t *ppos)
444 struct broadsheetfb_par *par = info->par;
445 unsigned long p = *ppos;
448 unsigned long total_size;
450 if (info->state != FBINFO_STATE_RUNNING)
453 total_size = info->fix.smem_len;
458 if (count > total_size) {
463 if (count + p > total_size) {
467 count = total_size - p;
470 dst = (void *)(info->screen_base + p);
472 if (copy_from_user(dst, buf, count))
478 broadsheetfb_dpy_update(par);
480 return (err) ? err : count;
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,
492 static struct fb_deferred_io broadsheetfb_defio = {
494 .deferred_io = broadsheetfb_dpy_deferred_io,
497 static int __devinit broadsheetfb_probe(struct platform_device *dev)
499 struct fb_info *info;
500 struct broadsheet_board *board;
501 int retval = -ENOMEM;
503 unsigned char *videomemory;
504 struct broadsheetfb_par *par;
509 /* pick up board specific routines */
510 board = dev->dev.platform_data;
514 /* try to count device specific driver, if can't, platform recalls */
515 if (!try_module_get(board->owner))
518 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
522 switch (board->get_panel_type()) {
535 dpyw = panel_table[panel_index].w;
536 dpyh = panel_table[panel_index].h;
538 videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);
540 videomemory = vmalloc(videomemorysize);
544 memset(videomemory, 0, videomemorysize);
546 info->screen_base = (char *)videomemory;
547 info->fbops = &broadsheetfb_ops;
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;
555 broadsheetfb_fix.line_length = dpyw;
556 info->fix = broadsheetfb_fix;
557 info->fix.smem_len = videomemorysize;
559 par->panel_index = panel_index;
562 par->write_reg = broadsheet_write_reg;
563 par->read_reg = broadsheet_read_reg;
564 init_waitqueue_head(&par->waitq);
566 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
568 info->fbdefio = &broadsheetfb_defio;
569 fb_deferred_io_init(info);
571 retval = fb_alloc_cmap(&info->cmap, 16, 0);
573 dev_err(&dev->dev, "Failed to allocate colormap\n");
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);
583 retval = par->board->setup_irq(info);
587 /* this inits the dpy */
588 retval = board->init(par);
592 broadsheet_init(par);
594 retval = register_framebuffer(info);
597 platform_set_drvdata(dev, info);
600 "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
601 info->node, videomemorysize >> 10);
609 fb_dealloc_cmap(&info->cmap);
613 framebuffer_release(info);
615 module_put(board->owner);
620 static int __devexit broadsheetfb_remove(struct platform_device *dev)
622 struct fb_info *info = platform_get_drvdata(dev);
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);
637 static struct platform_driver broadsheetfb_driver = {
638 .probe = broadsheetfb_probe,
639 .remove = broadsheetfb_remove,
641 .owner = THIS_MODULE,
642 .name = "broadsheetfb",
646 static int __init broadsheetfb_init(void)
648 return platform_driver_register(&broadsheetfb_driver);
651 static void __exit broadsheetfb_exit(void)
653 platform_driver_unregister(&broadsheetfb_driver);
656 module_init(broadsheetfb_init);
657 module_exit(broadsheetfb_exit);
659 MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
660 MODULE_AUTHOR("Jaya Kumar");
661 MODULE_LICENSE("GPL");