blob: 8f011062dec5ad6fc3d03d5081fe4b0575e71d72 [file] [log] [blame]
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001/*
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
Jaya Kumarc1c341a2010-03-10 15:21:24 -080036/* track panel specific parameters */
37struct panel_info {
38 int w;
39 int h;
40 u16 sdcfg;
41 u16 gdcfg;
42 u16 lutfmt;
43 u16 fsynclen;
44 u16 fendfbegin;
45 u16 lsynclen;
46 u16 lendlbegin;
47 u16 pixclk;
48};
49
50/* table of panel specific parameters to be indexed into by the board drivers */
51static struct panel_info panel_table[] = {
52 { /* standard 6" on TFT backplane */
53 .w = 800,
54 .h = 600,
55 .sdcfg = (100 | (1 << 8) | (1 << 9)),
56 .gdcfg = 2,
57 .lutfmt = (4 | (1 << 7)),
58 .fsynclen = 4,
59 .fendfbegin = (10 << 8) | 4,
60 .lsynclen = 10,
61 .lendlbegin = (100 << 8) | 4,
62 .pixclk = 6,
63 },
64 { /* custom 3.7" flexible on PET or steel */
65 .w = 320,
66 .h = 240,
67 .sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)),
68 .gdcfg = 3,
69 .lutfmt = (4 | (1 << 7)),
70 .fsynclen = 0,
71 .fendfbegin = (80 << 8) | 4,
72 .lsynclen = 10,
73 .lendlbegin = (80 << 8) | 20,
74 .pixclk = 14,
75 },
76 { /* standard 9.7" on TFT backplane */
77 .w = 1200,
78 .h = 825,
79 .sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)),
80 .gdcfg = 2,
81 .lutfmt = (4 | (1 << 7)),
82 .fsynclen = 0,
83 .fendfbegin = (4 << 8) | 4,
84 .lsynclen = 4,
85 .lendlbegin = (60 << 8) | 10,
86 .pixclk = 3,
87 },
88};
89
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +010090#define DPY_W 800
91#define DPY_H 600
92
93static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata = {
94 .id = "broadsheetfb",
95 .type = FB_TYPE_PACKED_PIXELS,
96 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
97 .xpanstep = 0,
98 .ypanstep = 0,
99 .ywrapstep = 0,
100 .line_length = DPY_W,
101 .accel = FB_ACCEL_NONE,
102};
103
104static struct fb_var_screeninfo broadsheetfb_var __devinitdata = {
105 .xres = DPY_W,
106 .yres = DPY_H,
107 .xres_virtual = DPY_W,
108 .yres_virtual = DPY_H,
109 .bits_per_pixel = 8,
110 .grayscale = 1,
111 .red = { 0, 4, 0 },
112 .green = { 0, 4, 0 },
113 .blue = { 0, 4, 0 },
114 .transp = { 0, 0, 0 },
115};
116
117/* main broadsheetfb functions */
118static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data)
119{
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);
123}
124
125static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data)
126{
127 par->board->set_ctl(par, BS_DC, 0);
128 broadsheet_issue_data(par, data);
129}
130
131static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
132{
133 par->board->wait_for_rdy(par);
134
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);
139}
140
141static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
142 int argc, u16 *argv)
143{
144 int i;
145
146 par->board->wait_for_rdy(par);
147
148 par->board->set_ctl(par, BS_CS, 0);
149 broadsheet_issue_cmd(par, cmd);
150 par->board->set_ctl(par, BS_DC, 1);
151
152 for (i = 0; i < argc; i++)
153 broadsheet_issue_data(par, argv[i]);
154 par->board->set_ctl(par, BS_CS, 1);
155}
156
157static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
158 u16 *data)
159{
160 int i;
161 u16 tmp;
162
163 par->board->set_ctl(par, BS_CS, 0);
164 par->board->set_ctl(par, BS_DC, 1);
165
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);
172 }
173
174 par->board->set_ctl(par, BS_CS, 1);
175}
176
177static u16 broadsheet_get_data(struct broadsheetfb_par *par)
178{
179 u16 res;
180 /* wait for ready to go hi. (lo is busy) */
181 par->board->wait_for_rdy(par);
182
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);
187
188 res = par->board->get_hdb(par);
189
190 /* strobe wr */
191 par->board->set_ctl(par, BS_WR, 1);
192 par->board->set_ctl(par, BS_CS, 1);
193
194 return res;
195}
196
197static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
198 u16 data)
199{
200 /* wait for ready to go hi. (lo is busy) */
201 par->board->wait_for_rdy(par);
202
203 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
204 par->board->set_ctl(par, BS_CS, 0);
205
206 broadsheet_issue_cmd(par, BS_CMD_WR_REG);
207
208 par->board->set_ctl(par, BS_DC, 1);
209
210 broadsheet_issue_data(par, reg);
211 broadsheet_issue_data(par, data);
212
213 par->board->set_ctl(par, BS_CS, 1);
214}
215
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800216static void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg,
217 u32 data)
218{
219 broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF);
220 broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF);
221}
222
223
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100224static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
225{
226 broadsheet_send_command(par, reg);
227 msleep(100);
228 return broadsheet_get_data(par);
229}
230
231static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
232{
233 u16 args[5];
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800234 int xres = par->info->var.xres;
235 int yres = par->info->var.yres;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100236
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800237 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;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100242 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
243
244 /* did the controller really set it? */
245 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
246
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800247 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;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100252 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
253
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800254 broadsheet_write_reg32(par, 0x310, xres*yres*2);
255
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100256 /* setup waveform */
257 args[0] = 0x886;
258 args[1] = 0;
259 broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
260
261 broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
262
263 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
264
265 broadsheet_write_reg(par, 0x330, 0x84);
266
267 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
268
269 args[0] = (0x3 << 4);
270 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
271
272 args[0] = 0x154;
273 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
274
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800275 broadsheet_burst_write(par, (panel_table[par->panel_index].w *
276 panel_table[par->panel_index].h)/2,
277 (u16 *) par->info->screen_base);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100278
279 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
280
281 args[0] = 0x4300;
282 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
283
284 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
285
286 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
287
288 par->board->wait_for_rdy(par);
289}
290
291static void __devinit broadsheet_init(struct broadsheetfb_par *par)
292{
293 broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
294 /* the controller needs a second */
295 msleep(1000);
296 broadsheet_init_display(par);
297}
298
299static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
300 u16 y1, u16 y2)
301{
302 u16 args[5];
303 unsigned char *buf = (unsigned char *)par->info->screen_base;
304
305 /* y1 must be a multiple of 4 so drop the lower bits */
306 y1 &= 0xFFFC;
307 /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
308 y2 |= 0x0003;
309
310 args[0] = 0x3 << 4;
311 args[1] = 0;
312 args[2] = y1;
313 args[3] = cpu_to_le16(par->info->var.xres);
314 args[4] = y2;
315 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
316
317 args[0] = 0x154;
318 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
319
320 buf += y1 * par->info->var.xres;
321 broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
322 (u16 *) buf);
323
324 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
325
326 args[0] = 0x4300;
327 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
328
329 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
330
331 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
332
333 par->board->wait_for_rdy(par);
334
335}
336
337static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
338{
339 u16 args[5];
340
341 args[0] = 0x3 << 4;
342 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
343
344 args[0] = 0x154;
345 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800346 broadsheet_burst_write(par, (panel_table[par->panel_index].w *
347 panel_table[par->panel_index].h)/2,
348 (u16 *) par->info->screen_base);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100349
350 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
351
352 args[0] = 0x4300;
353 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
354
355 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
356
357 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
358
359 par->board->wait_for_rdy(par);
360
361}
362
363/* this is called back from the deferred io workqueue */
364static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
365 struct list_head *pagelist)
366{
367 u16 y1 = 0, h = 0;
368 int prev_index = -1;
369 struct page *cur;
370 struct fb_deferred_io *fbdefio = info->fbdefio;
371 int h_inc;
372 u16 yres = info->var.yres;
373 u16 xres = info->var.xres;
374
375 /* height increment is fixed per page */
376 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
377
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;
383 h = h_inc;
384 } else if ((prev_index + 1) == cur->index) {
385 /* this page is consecutive so increase our height */
386 h += h_inc;
387 } else {
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;
392 h = h_inc;
393 }
394 prev_index = cur->index;
395 }
396
397 /* if we still have any pages to update we do so now */
398 if (h >= yres) {
399 /* its a full screen update, just do it */
400 broadsheetfb_dpy_update(info->par);
401 } else {
402 broadsheetfb_dpy_update_pages(info->par, y1,
403 min((u16) (y1 + h), yres));
404 }
405}
406
407static void broadsheetfb_fillrect(struct fb_info *info,
408 const struct fb_fillrect *rect)
409{
410 struct broadsheetfb_par *par = info->par;
411
412 sys_fillrect(info, rect);
413
414 broadsheetfb_dpy_update(par);
415}
416
417static void broadsheetfb_copyarea(struct fb_info *info,
418 const struct fb_copyarea *area)
419{
420 struct broadsheetfb_par *par = info->par;
421
422 sys_copyarea(info, area);
423
424 broadsheetfb_dpy_update(par);
425}
426
427static void broadsheetfb_imageblit(struct fb_info *info,
428 const struct fb_image *image)
429{
430 struct broadsheetfb_par *par = info->par;
431
432 sys_imageblit(info, image);
433
434 broadsheetfb_dpy_update(par);
435}
436
437/*
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
440 */
441static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
442 size_t count, loff_t *ppos)
443{
444 struct broadsheetfb_par *par = info->par;
445 unsigned long p = *ppos;
446 void *dst;
447 int err = 0;
448 unsigned long total_size;
449
450 if (info->state != FBINFO_STATE_RUNNING)
451 return -EPERM;
452
453 total_size = info->fix.smem_len;
454
455 if (p > total_size)
456 return -EFBIG;
457
458 if (count > total_size) {
459 err = -EFBIG;
460 count = total_size;
461 }
462
463 if (count + p > total_size) {
464 if (!err)
465 err = -ENOSPC;
466
467 count = total_size - p;
468 }
469
470 dst = (void *)(info->screen_base + p);
471
472 if (copy_from_user(dst, buf, count))
473 err = -EFAULT;
474
475 if (!err)
476 *ppos += count;
477
478 broadsheetfb_dpy_update(par);
479
480 return (err) ? err : count;
481}
482
483static 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,
490};
491
492static struct fb_deferred_io broadsheetfb_defio = {
493 .delay = HZ/4,
494 .deferred_io = broadsheetfb_dpy_deferred_io,
495};
496
497static int __devinit broadsheetfb_probe(struct platform_device *dev)
498{
499 struct fb_info *info;
500 struct broadsheet_board *board;
501 int retval = -ENOMEM;
502 int videomemorysize;
503 unsigned char *videomemory;
504 struct broadsheetfb_par *par;
505 int i;
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800506 int dpyw, dpyh;
507 int panel_index;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100508
509 /* pick up board specific routines */
510 board = dev->dev.platform_data;
511 if (!board)
512 return -EINVAL;
513
514 /* try to count device specific driver, if can't, platform recalls */
515 if (!try_module_get(board->owner))
516 return -ENODEV;
517
518 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
519 if (!info)
520 goto err;
521
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800522 switch (board->get_panel_type()) {
523 case 37:
524 panel_index = 1;
525 break;
526 case 97:
527 panel_index = 2;
528 break;
529 case 6:
530 default:
531 panel_index = 0;
532 break;
533 }
534
535 dpyw = panel_table[panel_index].w;
536 dpyh = panel_table[panel_index].h;
537
538 videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);
539
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100540 videomemory = vmalloc(videomemorysize);
541 if (!videomemory)
542 goto err_fb_rel;
543
544 memset(videomemory, 0, videomemorysize);
545
546 info->screen_base = (char *)videomemory;
547 info->fbops = &broadsheetfb_ops;
548
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800549 broadsheetfb_var.xres = dpyw;
550 broadsheetfb_var.yres = dpyh;
551 broadsheetfb_var.xres_virtual = dpyw;
552 broadsheetfb_var.yres_virtual = dpyh;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100553 info->var = broadsheetfb_var;
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800554
555 broadsheetfb_fix.line_length = dpyw;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100556 info->fix = broadsheetfb_fix;
557 info->fix.smem_len = videomemorysize;
558 par = info->par;
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800559 par->panel_index = panel_index;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100560 par->info = info;
561 par->board = board;
562 par->write_reg = broadsheet_write_reg;
563 par->read_reg = broadsheet_read_reg;
564 init_waitqueue_head(&par->waitq);
565
Konrad Rzeszutek Wilka9b5ff92009-12-03 10:31:58 -0500566 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100567
568 info->fbdefio = &broadsheetfb_defio;
569 fb_deferred_io_init(info);
570
571 retval = fb_alloc_cmap(&info->cmap, 16, 0);
572 if (retval < 0) {
573 dev_err(&dev->dev, "Failed to allocate colormap\n");
574 goto err_vfree;
575 }
576
577 /* set cmap */
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);
582
583 retval = par->board->setup_irq(info);
584 if (retval < 0)
585 goto err_cmap;
586
587 /* this inits the dpy */
588 retval = board->init(par);
589 if (retval < 0)
590 goto err_free_irq;
591
592 broadsheet_init(par);
593
594 retval = register_framebuffer(info);
595 if (retval < 0)
596 goto err_free_irq;
597 platform_set_drvdata(dev, info);
598
599 printk(KERN_INFO
600 "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
601 info->node, videomemorysize >> 10);
602
603
604 return 0;
605
606err_free_irq:
607 board->cleanup(par);
608err_cmap:
609 fb_dealloc_cmap(&info->cmap);
610err_vfree:
611 vfree(videomemory);
612err_fb_rel:
613 framebuffer_release(info);
614err:
615 module_put(board->owner);
616 return retval;
617
618}
619
620static int __devexit broadsheetfb_remove(struct platform_device *dev)
621{
622 struct fb_info *info = platform_get_drvdata(dev);
623
624 if (info) {
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);
633 }
634 return 0;
635}
636
637static struct platform_driver broadsheetfb_driver = {
638 .probe = broadsheetfb_probe,
639 .remove = broadsheetfb_remove,
640 .driver = {
641 .owner = THIS_MODULE,
642 .name = "broadsheetfb",
643 },
644};
645
646static int __init broadsheetfb_init(void)
647{
648 return platform_driver_register(&broadsheetfb_driver);
649}
650
651static void __exit broadsheetfb_exit(void)
652{
653 platform_driver_unregister(&broadsheetfb_driver);
654}
655
656module_init(broadsheetfb_init);
657module_exit(broadsheetfb_exit);
658
659MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
660MODULE_AUTHOR("Jaya Kumar");
661MODULE_LICENSE("GPL");