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 /* Display specific information */
40 static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata
= {
42 .type
= FB_TYPE_PACKED_PIXELS
,
43 .visual
= FB_VISUAL_STATIC_PSEUDOCOLOR
,
48 .accel
= FB_ACCEL_NONE
,
51 static struct fb_var_screeninfo broadsheetfb_var __devinitdata
= {
54 .xres_virtual
= DPY_W
,
55 .yres_virtual
= DPY_H
,
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
,
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
,
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
)
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
);
138 par
->board
->set_ctl(par
, BS_WR
, 1);
139 par
->board
->set_ctl(par
, BS_CS
, 1);
144 static void broadsheet_write_reg(struct broadsheetfb_par
*par
, u16 reg
,
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
);
167 return broadsheet_get_data(par
);
170 static void __devinit
broadsheet_init_display(struct broadsheetfb_par
*par
)
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
);
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
);
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
);
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 */
230 broadsheet_init_display(par
);
233 static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par
*par
,
237 unsigned char *buf
= (unsigned char *)par
->info
->screen_base
;
239 /* y1 must be a multiple of 4 so drop the lower bits */
241 /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
247 args
[3] = cpu_to_le16(par
->info
->var
.xres
);
249 broadsheet_send_cmdargs(par
, BS_CMD_LD_IMG_AREA
, 5, args
);
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,
258 broadsheet_send_command(par
, BS_CMD_LD_IMG_END
);
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
)
276 broadsheet_send_cmdargs(par
, BS_CMD_LD_IMG
, 1, args
);
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
);
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
)
303 struct fb_deferred_io
*fbdefio
= info
->fbdefio
;
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
;
317 } else if ((prev_index
+ 1) == cur
->index
) {
318 /* this page is consecutive so increase our height */
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
;
327 prev_index
= cur
->index
;
330 /* if we still have any pages to update we do so now */
332 /* its a full screen update, just do it */
333 broadsheetfb_dpy_update(info
->par
);
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
;
381 unsigned long total_size
;
383 if (info
->state
!= FBINFO_STATE_RUNNING
)
386 total_size
= info
->fix
.smem_len
;
391 if (count
> total_size
) {
396 if (count
+ p
> total_size
) {
400 count
= total_size
- p
;
403 dst
= (void *)(info
->screen_base
+ p
);
405 if (copy_from_user(dst
, buf
, 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
= {
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
;
436 unsigned char *videomemory
;
437 struct broadsheetfb_par
*par
;
440 /* pick up board specific routines */
441 board
= dev
->dev
.platform_data
;
445 /* try to count device specific driver, if can't, platform recalls */
446 if (!try_module_get(board
->owner
))
449 info
= framebuffer_alloc(sizeof(struct broadsheetfb_par
), &dev
->dev
);
453 videomemorysize
= (DPY_W
*DPY_H
);
454 videomemory
= vmalloc(videomemorysize
);
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
;
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);
480 dev_err(&dev
->dev
, "Failed to allocate colormap\n");
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
);
494 /* this inits the dpy */
495 retval
= board
->init(par
);
499 broadsheet_init(par
);
501 retval
= register_framebuffer(info
);
504 platform_set_drvdata(dev
, info
);
507 "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
508 info
->node
, videomemorysize
>> 10);
516 fb_dealloc_cmap(&info
->cmap
);
520 framebuffer_release(info
);
522 module_put(board
->owner
);
527 static int __devexit
broadsheetfb_remove(struct platform_device
*dev
)
529 struct fb_info
*info
= platform_get_drvdata(dev
);
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
);
544 static struct platform_driver broadsheetfb_driver
= {
545 .probe
= broadsheetfb_probe
,
546 .remove
= broadsheetfb_remove
,
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");