Merge branch 'ioat' into dmaengine
[linux-2.6-block.git] / drivers / video / broadsheetfb.c
CommitLineData
0d4ff4df
JK
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");