aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJaya Kumar <jayakumar.lkml@gmail.com>2009-01-01 11:49:19 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-02-10 06:27:59 -0500
commit0d4ff4df341208b1b75e01feca27301c0dcbf490 (patch)
tree53cbfa3058c15e04ec1c7758ac4343cc06f06ed0 /drivers
parentb4e411294a193e18c41912bc3df03f5b9cd7c823 (diff)
[ARM] 5353/1: fbdev: add E-Ink Broadsheet controller support v3
This patch adds support for the E-Ink Broadsheet display controller. Cc: Eric Miao <eric.miao@marvell.com> Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/Kconfig14
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/broadsheetfb.c568
3 files changed, 583 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index f0267706cb45..5aa099bc7690 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2135,6 +2135,20 @@ config FB_MX3
2135 far only synchronous displays are supported. If you plan to use 2135 far only synchronous displays are supported. If you plan to use
2136 an LCD display with your i.MX31 system, say Y here. 2136 an LCD display with your i.MX31 system, say Y here.
2137 2137
2138config FB_BROADSHEET
2139 tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
2140 depends on FB
2141 select FB_SYS_FILLRECT
2142 select FB_SYS_COPYAREA
2143 select FB_SYS_IMAGEBLIT
2144 select FB_SYS_FOPS
2145 select FB_DEFERRED_IO
2146 help
2147 This driver implements support for the E-Ink Broadsheet
2148 controller. The release name for this device was Epson S1D13521
2149 and could also have been called by other names when coupled with
2150 a bridge adapter.
2151
2138source "drivers/video/omap/Kconfig" 2152source "drivers/video/omap/Kconfig"
2139 2153
2140source "drivers/video/backlight/Kconfig" 2154source "drivers/video/backlight/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 2a998ca6181d..edd5a85c1eb5 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -106,6 +106,7 @@ obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o
106obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o 106obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o
107obj-$(CONFIG_FB_MAXINE) += maxinefb.o 107obj-$(CONFIG_FB_MAXINE) += maxinefb.o
108obj-$(CONFIG_FB_METRONOME) += metronomefb.o 108obj-$(CONFIG_FB_METRONOME) += metronomefb.o
109obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o
109obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o 110obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o
110obj-$(CONFIG_FB_SH7760) += sh7760fb.o 111obj-$(CONFIG_FB_SH7760) += sh7760fb.o
111obj-$(CONFIG_FB_IMX) += imxfb.o 112obj-$(CONFIG_FB_IMX) += imxfb.o
diff --git a/drivers/video/broadsheetfb.c b/drivers/video/broadsheetfb.c
new file mode 100644
index 000000000000..509cb92e8731
--- /dev/null
+++ b/drivers/video/broadsheetfb.c
@@ -0,0 +1,568 @@
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/* Display specific information */
37#define DPY_W 800
38#define DPY_H 600
39
40static 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,
49};
50
51static 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 },
62};
63
64/* main broadsheetfb functions */
65static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data)
66{
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);
70}
71
72static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data)
73{
74 par->board->set_ctl(par, BS_DC, 0);
75 broadsheet_issue_data(par, data);
76}
77
78static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
79{
80 par->board->wait_for_rdy(par);
81
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);
86}
87
88static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
89 int argc, u16 *argv)
90{
91 int i;
92
93 par->board->wait_for_rdy(par);
94
95 par->board->set_ctl(par, BS_CS, 0);
96 broadsheet_issue_cmd(par, cmd);
97 par->board->set_ctl(par, BS_DC, 1);
98
99 for (i = 0; i < argc; i++)
100 broadsheet_issue_data(par, argv[i]);
101 par->board->set_ctl(par, BS_CS, 1);
102}
103
104static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
105 u16 *data)
106{
107 int i;
108 u16 tmp;
109
110 par->board->set_ctl(par, BS_CS, 0);
111 par->board->set_ctl(par, BS_DC, 1);
112
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);
119 }
120
121 par->board->set_ctl(par, BS_CS, 1);
122}
123
124static u16 broadsheet_get_data(struct broadsheetfb_par *par)
125{
126 u16 res;
127 /* wait for ready to go hi. (lo is busy) */
128 par->board->wait_for_rdy(par);
129
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);
134
135 res = par->board->get_hdb(par);
136
137 /* strobe wr */
138 par->board->set_ctl(par, BS_WR, 1);
139 par->board->set_ctl(par, BS_CS, 1);
140
141 return res;
142}
143
144static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
145 u16 data)
146{
147 /* wait for ready to go hi. (lo is busy) */
148 par->board->wait_for_rdy(par);
149
150 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
151 par->board->set_ctl(par, BS_CS, 0);
152
153 broadsheet_issue_cmd(par, BS_CMD_WR_REG);
154
155 par->board->set_ctl(par, BS_DC, 1);
156
157 broadsheet_issue_data(par, reg);
158 broadsheet_issue_data(par, data);
159
160 par->board->set_ctl(par, BS_CS, 1);
161}
162
163static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
164{
165 broadsheet_send_command(par, reg);
166 msleep(100);
167 return broadsheet_get_data(par);
168}
169
170static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
171{
172 u16 args[5];
173
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);
180
181 /* did the controller really set it? */
182 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
183
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);
190
191 /* setup waveform */
192 args[0] = 0x886;
193 args[1] = 0;
194 broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
195
196 broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
197
198 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
199
200 broadsheet_write_reg(par, 0x330, 0x84);
201
202 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
203
204 args[0] = (0x3 << 4);
205 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
206
207 args[0] = 0x154;
208 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
209
210 broadsheet_burst_write(par, DPY_W*DPY_H/2,
211 (u16 *) par->info->screen_base);
212
213 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
214
215 args[0] = 0x4300;
216 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
217
218 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
219
220 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
221
222 par->board->wait_for_rdy(par);
223}
224
225static void __devinit broadsheet_init(struct broadsheetfb_par *par)
226{
227 broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
228 /* the controller needs a second */
229 msleep(1000);
230 broadsheet_init_display(par);
231}
232
233static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
234 u16 y1, u16 y2)
235{
236 u16 args[5];
237 unsigned char *buf = (unsigned char *)par->info->screen_base;
238
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;
243
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);
250
251 args[0] = 0x154;
252 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
253
254 buf += y1 * par->info->var.xres;
255 broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
256 (u16 *) buf);
257
258 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
259
260 args[0] = 0x4300;
261 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
262
263 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
264
265 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
266
267 par->board->wait_for_rdy(par);
268
269}
270
271static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
272{
273 u16 args[5];
274
275 args[0] = 0x3 << 4;
276 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
277
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);
282
283 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
284
285 args[0] = 0x4300;
286 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
287
288 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
289
290 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
291
292 par->board->wait_for_rdy(par);
293
294}
295
296/* this is called back from the deferred io workqueue */
297static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
298 struct list_head *pagelist)
299{
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;
307
308 /* height increment is fixed per page */
309 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
310
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;
326 }
327 prev_index = cur->index;
328 }
329
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));
337 }
338}
339
340static void broadsheetfb_fillrect(struct fb_info *info,
341 const struct fb_fillrect *rect)
342{
343 struct broadsheetfb_par *par = info->par;
344
345 sys_fillrect(info, rect);
346
347 broadsheetfb_dpy_update(par);
348}
349
350static void broadsheetfb_copyarea(struct fb_info *info,
351 const struct fb_copyarea *area)
352{
353 struct broadsheetfb_par *par = info->par;
354
355 sys_copyarea(info, area);
356
357 broadsheetfb_dpy_update(par);
358}
359
360static void broadsheetfb_imageblit(struct fb_info *info,
361 const struct fb_image *image)
362{
363 struct broadsheetfb_par *par = info->par;
364
365 sys_imageblit(info, image);
366
367 broadsheetfb_dpy_update(par);
368}
369
370/*
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
373 */
374static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
375 size_t count, loff_t *ppos)
376{
377 struct broadsheetfb_par *par = info->par;
378 unsigned long p = *ppos;
379 void *dst;
380 int err = 0;
381 unsigned long total_size;
382
383 if (info->state != FBINFO_STATE_RUNNING)
384 return -EPERM;
385
386 total_size = info->fix.smem_len;
387
388 if (p > total_size)
389 return -EFBIG;
390
391 if (count > total_size) {
392 err = -EFBIG;
393 count = total_size;
394 }
395
396 if (count + p > total_size) {
397 if (!err)
398 err = -ENOSPC;
399
400 count = total_size - p;
401 }
402
403 dst = (void *)(info->screen_base + p);
404
405 if (copy_from_user(dst, buf, count))
406 err = -EFAULT;
407
408 if (!err)
409 *ppos += count;
410
411 broadsheetfb_dpy_update(par);
412
413 return (err) ? err : count;
414}
415
416static 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,
423};
424
425static struct fb_deferred_io broadsheetfb_defio = {
426 .delay = HZ/4,
427 .deferred_io = broadsheetfb_dpy_deferred_io,
428};
429
430static int __devinit broadsheetfb_probe(struct platform_device *dev)
431{
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;
439
440 /* pick up board specific routines */
441 board = dev->dev.platform_data;
442 if (!board)
443 return -EINVAL;
444
445 /* try to count device specific driver, if can't, platform recalls */
446 if (!try_module_get(board->owner))
447 return -ENODEV;
448
449 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
450 if (!info)
451 goto err;
452
453 videomemorysize = (DPY_W*DPY_H);
454 videomemory = vmalloc(videomemorysize);
455 if (!videomemory)
456 goto err_fb_rel;
457
458 memset(videomemory, 0, videomemorysize);
459
460 info->screen_base = (char *)videomemory;
461 info->fbops = &broadsheetfb_ops;
462
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);
472
473 info->flags = FBINFO_FLAG_DEFAULT;
474
475 info->fbdefio = &broadsheetfb_defio;
476 fb_deferred_io_init(info);
477
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;
482 }
483
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);
489
490 retval = par->board->setup_irq(info);
491 if (retval < 0)
492 goto err_cmap;
493
494 /* this inits the dpy */
495 retval = board->init(par);
496 if (retval < 0)
497 goto err_free_irq;
498
499 broadsheet_init(par);
500
501 retval = register_framebuffer(info);
502 if (retval < 0)
503 goto err_free_irq;
504 platform_set_drvdata(dev, info);
505
506 printk(KERN_INFO
507 "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
508 info->node, videomemorysize >> 10);
509
510
511 return 0;
512
513err_free_irq:
514 board->cleanup(par);
515err_cmap:
516 fb_dealloc_cmap(&info->cmap);
517err_vfree:
518 vfree(videomemory);
519err_fb_rel:
520 framebuffer_release(info);
521err:
522 module_put(board->owner);
523 return retval;
524
525}
526
527static int __devexit broadsheetfb_remove(struct platform_device *dev)
528{
529 struct fb_info *info = platform_get_drvdata(dev);
530
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);
540 }
541 return 0;
542}
543
544static struct platform_driver broadsheetfb_driver = {
545 .probe = broadsheetfb_probe,
546 .remove = broadsheetfb_remove,
547 .driver = {
548 .owner = THIS_MODULE,
549 .name = "broadsheetfb",
550 },
551};
552
553static int __init broadsheetfb_init(void)
554{
555 return platform_driver_register(&broadsheetfb_driver);
556}
557
558static void __exit broadsheetfb_exit(void)
559{
560 platform_driver_unregister(&broadsheetfb_driver);
561}
562
563module_init(broadsheetfb_init);
564module_exit(broadsheetfb_exit);
565
566MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
567MODULE_AUTHOR("Jaya Kumar");
568MODULE_LICENSE("GPL");