Full support for Ginger Console
[linux-ginger.git] / drivers / video / broadsheetfb.c
blob509cb92e873105b8874ea08cdacca171636c4651
1 /*
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
8 * more details.
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>
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>
34 #include <video/broadsheetfb.h>
36 /* Display specific information */
37 #define DPY_W 800
38 #define DPY_H 600
40 static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata = {
41 .id = "broadsheetfb",
42 .type = FB_TYPE_PACKED_PIXELS,
43 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
44 .xpanstep = 0,
45 .ypanstep = 0,
46 .ywrapstep = 0,
47 .line_length = DPY_W,
48 .accel = FB_ACCEL_NONE,
51 static struct fb_var_screeninfo broadsheetfb_var __devinitdata = {
52 .xres = DPY_W,
53 .yres = DPY_H,
54 .xres_virtual = DPY_W,
55 .yres_virtual = DPY_H,
56 .bits_per_pixel = 8,
57 .grayscale = 1,
58 .red = { 0, 4, 0 },
59 .green = { 0, 4, 0 },
60 .blue = { 0, 4, 0 },
61 .transp = { 0, 0, 0 },
64 /* main broadsheetfb functions */
65 static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data)
67 par->board->set_ctl(par, BS_WR, 0);
68 par->board->set_hdb(par, data);
69 par->board->set_ctl(par, BS_WR, 1);
72 static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data)
74 par->board->set_ctl(par, BS_DC, 0);
75 broadsheet_issue_data(par, data);
78 static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
80 par->board->wait_for_rdy(par);
82 par->board->set_ctl(par, BS_CS, 0);
83 broadsheet_issue_cmd(par, data);
84 par->board->set_ctl(par, BS_DC, 1);
85 par->board->set_ctl(par, BS_CS, 1);
88 static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
89 int argc, u16 *argv)
91 int i;
93 par->board->wait_for_rdy(par);
95 par->board->set_ctl(par, BS_CS, 0);
96 broadsheet_issue_cmd(par, cmd);
97 par->board->set_ctl(par, BS_DC, 1);
99 for (i = 0; i < argc; i++)
100 broadsheet_issue_data(par, argv[i]);
101 par->board->set_ctl(par, BS_CS, 1);
104 static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
105 u16 *data)
107 int i;
108 u16 tmp;
110 par->board->set_ctl(par, BS_CS, 0);
111 par->board->set_ctl(par, BS_DC, 1);
113 for (i = 0; i < size; i++) {
114 par->board->set_ctl(par, BS_WR, 0);
115 tmp = (data[i] & 0x0F) << 4;
116 tmp |= (data[i] & 0x0F00) << 4;
117 par->board->set_hdb(par, tmp);
118 par->board->set_ctl(par, BS_WR, 1);
121 par->board->set_ctl(par, BS_CS, 1);
124 static u16 broadsheet_get_data(struct broadsheetfb_par *par)
126 u16 res;
127 /* wait for ready to go hi. (lo is busy) */
128 par->board->wait_for_rdy(par);
130 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
131 par->board->set_ctl(par, BS_DC, 1);
132 par->board->set_ctl(par, BS_CS, 0);
133 par->board->set_ctl(par, BS_WR, 0);
135 res = par->board->get_hdb(par);
137 /* strobe wr */
138 par->board->set_ctl(par, BS_WR, 1);
139 par->board->set_ctl(par, BS_CS, 1);
141 return res;
144 static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
145 u16 data)
147 /* wait for ready to go hi. (lo is busy) */
148 par->board->wait_for_rdy(par);
150 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
151 par->board->set_ctl(par, BS_CS, 0);
153 broadsheet_issue_cmd(par, BS_CMD_WR_REG);
155 par->board->set_ctl(par, BS_DC, 1);
157 broadsheet_issue_data(par, reg);
158 broadsheet_issue_data(par, data);
160 par->board->set_ctl(par, BS_CS, 1);
163 static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
165 broadsheet_send_command(par, reg);
166 msleep(100);
167 return broadsheet_get_data(par);
170 static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
172 u16 args[5];
174 args[0] = DPY_W;
175 args[1] = DPY_H;
176 args[2] = (100 | (1 << 8) | (1 << 9)); /* sdcfg */
177 args[3] = 2; /* gdrv cfg */
178 args[4] = (4 | (1 << 7)); /* lut index format */
179 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
181 /* did the controller really set it? */
182 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
184 args[0] = 4; /* fsync len */
185 args[1] = (10 << 8) | 4; /* fend/fbegin len */
186 args[2] = 10; /* line sync len */
187 args[3] = (100 << 8) | 4; /* line end/begin len */
188 args[4] = 6; /* pixel clock cfg */
189 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
191 /* setup waveform */
192 args[0] = 0x886;
193 args[1] = 0;
194 broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
196 broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
198 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
200 broadsheet_write_reg(par, 0x330, 0x84);
202 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
204 args[0] = (0x3 << 4);
205 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
207 args[0] = 0x154;
208 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
210 broadsheet_burst_write(par, DPY_W*DPY_H/2,
211 (u16 *) par->info->screen_base);
213 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
215 args[0] = 0x4300;
216 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
218 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
220 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
222 par->board->wait_for_rdy(par);
225 static void __devinit broadsheet_init(struct broadsheetfb_par *par)
227 broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
228 /* the controller needs a second */
229 msleep(1000);
230 broadsheet_init_display(par);
233 static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
234 u16 y1, u16 y2)
236 u16 args[5];
237 unsigned char *buf = (unsigned char *)par->info->screen_base;
239 /* y1 must be a multiple of 4 so drop the lower bits */
240 y1 &= 0xFFFC;
241 /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
242 y2 |= 0x0003;
244 args[0] = 0x3 << 4;
245 args[1] = 0;
246 args[2] = y1;
247 args[3] = cpu_to_le16(par->info->var.xres);
248 args[4] = y2;
249 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
251 args[0] = 0x154;
252 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
254 buf += y1 * par->info->var.xres;
255 broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
256 (u16 *) buf);
258 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
260 args[0] = 0x4300;
261 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
263 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
265 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
267 par->board->wait_for_rdy(par);
271 static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
273 u16 args[5];
275 args[0] = 0x3 << 4;
276 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
278 args[0] = 0x154;
279 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
280 broadsheet_burst_write(par, DPY_W*DPY_H/2,
281 (u16 *) par->info->screen_base);
283 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
285 args[0] = 0x4300;
286 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
288 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
290 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
292 par->board->wait_for_rdy(par);
296 /* this is called back from the deferred io workqueue */
297 static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
298 struct list_head *pagelist)
300 u16 y1 = 0, h = 0;
301 int prev_index = -1;
302 struct page *cur;
303 struct fb_deferred_io *fbdefio = info->fbdefio;
304 int h_inc;
305 u16 yres = info->var.yres;
306 u16 xres = info->var.xres;
308 /* height increment is fixed per page */
309 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
311 /* walk the written page list and swizzle the data */
312 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
313 if (prev_index < 0) {
314 /* just starting so assign first page */
315 y1 = (cur->index << PAGE_SHIFT) / xres;
316 h = h_inc;
317 } else if ((prev_index + 1) == cur->index) {
318 /* this page is consecutive so increase our height */
319 h += h_inc;
320 } else {
321 /* page not consecutive, issue previous update first */
322 broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
323 /* start over with our non consecutive page */
324 y1 = (cur->index << PAGE_SHIFT) / xres;
325 h = h_inc;
327 prev_index = cur->index;
330 /* if we still have any pages to update we do so now */
331 if (h >= yres) {
332 /* its a full screen update, just do it */
333 broadsheetfb_dpy_update(info->par);
334 } else {
335 broadsheetfb_dpy_update_pages(info->par, y1,
336 min((u16) (y1 + h), yres));
340 static void broadsheetfb_fillrect(struct fb_info *info,
341 const struct fb_fillrect *rect)
343 struct broadsheetfb_par *par = info->par;
345 sys_fillrect(info, rect);
347 broadsheetfb_dpy_update(par);
350 static void broadsheetfb_copyarea(struct fb_info *info,
351 const struct fb_copyarea *area)
353 struct broadsheetfb_par *par = info->par;
355 sys_copyarea(info, area);
357 broadsheetfb_dpy_update(par);
360 static void broadsheetfb_imageblit(struct fb_info *info,
361 const struct fb_image *image)
363 struct broadsheetfb_par *par = info->par;
365 sys_imageblit(info, image);
367 broadsheetfb_dpy_update(par);
371 * this is the slow path from userspace. they can seek and write to
372 * the fb. it's inefficient to do anything less than a full screen draw
374 static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
375 size_t count, loff_t *ppos)
377 struct broadsheetfb_par *par = info->par;
378 unsigned long p = *ppos;
379 void *dst;
380 int err = 0;
381 unsigned long total_size;
383 if (info->state != FBINFO_STATE_RUNNING)
384 return -EPERM;
386 total_size = info->fix.smem_len;
388 if (p > total_size)
389 return -EFBIG;
391 if (count > total_size) {
392 err = -EFBIG;
393 count = total_size;
396 if (count + p > total_size) {
397 if (!err)
398 err = -ENOSPC;
400 count = total_size - p;
403 dst = (void *)(info->screen_base + p);
405 if (copy_from_user(dst, buf, count))
406 err = -EFAULT;
408 if (!err)
409 *ppos += count;
411 broadsheetfb_dpy_update(par);
413 return (err) ? err : count;
416 static struct fb_ops broadsheetfb_ops = {
417 .owner = THIS_MODULE,
418 .fb_read = fb_sys_read,
419 .fb_write = broadsheetfb_write,
420 .fb_fillrect = broadsheetfb_fillrect,
421 .fb_copyarea = broadsheetfb_copyarea,
422 .fb_imageblit = broadsheetfb_imageblit,
425 static struct fb_deferred_io broadsheetfb_defio = {
426 .delay = HZ/4,
427 .deferred_io = broadsheetfb_dpy_deferred_io,
430 static int __devinit broadsheetfb_probe(struct platform_device *dev)
432 struct fb_info *info;
433 struct broadsheet_board *board;
434 int retval = -ENOMEM;
435 int videomemorysize;
436 unsigned char *videomemory;
437 struct broadsheetfb_par *par;
438 int i;
440 /* pick up board specific routines */
441 board = dev->dev.platform_data;
442 if (!board)
443 return -EINVAL;
445 /* try to count device specific driver, if can't, platform recalls */
446 if (!try_module_get(board->owner))
447 return -ENODEV;
449 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
450 if (!info)
451 goto err;
453 videomemorysize = (DPY_W*DPY_H);
454 videomemory = vmalloc(videomemorysize);
455 if (!videomemory)
456 goto err_fb_rel;
458 memset(videomemory, 0, videomemorysize);
460 info->screen_base = (char *)videomemory;
461 info->fbops = &broadsheetfb_ops;
463 info->var = broadsheetfb_var;
464 info->fix = broadsheetfb_fix;
465 info->fix.smem_len = videomemorysize;
466 par = info->par;
467 par->info = info;
468 par->board = board;
469 par->write_reg = broadsheet_write_reg;
470 par->read_reg = broadsheet_read_reg;
471 init_waitqueue_head(&par->waitq);
473 info->flags = FBINFO_FLAG_DEFAULT;
475 info->fbdefio = &broadsheetfb_defio;
476 fb_deferred_io_init(info);
478 retval = fb_alloc_cmap(&info->cmap, 16, 0);
479 if (retval < 0) {
480 dev_err(&dev->dev, "Failed to allocate colormap\n");
481 goto err_vfree;
484 /* set cmap */
485 for (i = 0; i < 16; i++)
486 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;
487 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);
488 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);
490 retval = par->board->setup_irq(info);
491 if (retval < 0)
492 goto err_cmap;
494 /* this inits the dpy */
495 retval = board->init(par);
496 if (retval < 0)
497 goto err_free_irq;
499 broadsheet_init(par);
501 retval = register_framebuffer(info);
502 if (retval < 0)
503 goto err_free_irq;
504 platform_set_drvdata(dev, info);
506 printk(KERN_INFO
507 "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
508 info->node, videomemorysize >> 10);
511 return 0;
513 err_free_irq:
514 board->cleanup(par);
515 err_cmap:
516 fb_dealloc_cmap(&info->cmap);
517 err_vfree:
518 vfree(videomemory);
519 err_fb_rel:
520 framebuffer_release(info);
521 err:
522 module_put(board->owner);
523 return retval;
527 static int __devexit broadsheetfb_remove(struct platform_device *dev)
529 struct fb_info *info = platform_get_drvdata(dev);
531 if (info) {
532 struct broadsheetfb_par *par = info->par;
533 unregister_framebuffer(info);
534 fb_deferred_io_cleanup(info);
535 par->board->cleanup(par);
536 fb_dealloc_cmap(&info->cmap);
537 vfree((void *)info->screen_base);
538 module_put(par->board->owner);
539 framebuffer_release(info);
541 return 0;
544 static struct platform_driver broadsheetfb_driver = {
545 .probe = broadsheetfb_probe,
546 .remove = broadsheetfb_remove,
547 .driver = {
548 .owner = THIS_MODULE,
549 .name = "broadsheetfb",
553 static int __init broadsheetfb_init(void)
555 return platform_driver_register(&broadsheetfb_driver);
558 static void __exit broadsheetfb_exit(void)
560 platform_driver_unregister(&broadsheetfb_driver);
563 module_init(broadsheetfb_init);
564 module_exit(broadsheetfb_exit);
566 MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
567 MODULE_AUTHOR("Jaya Kumar");
568 MODULE_LICENSE("GPL");