valkyriefb: check fb_alloc_cmap return value and handle failure properly
[linux-2.6] / drivers / video / broadsheetfb.c
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
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,
49 };
50
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 },
62 };
63
64 /* main broadsheetfb functions */
65 static 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
72 static 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
78 static 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
88 static 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
104 static 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
124 static 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
144 static 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
163 static 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
170 static 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
225 static 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
233 static 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
271 static 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 */
297 static 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
340 static 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
350 static 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
360 static 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  */
374 static 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
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,
423 };
424
425 static struct fb_deferred_io broadsheetfb_defio = {
426         .delay          = HZ/4,
427         .deferred_io    = broadsheetfb_dpy_deferred_io,
428 };
429
430 static 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
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;
524
525 }
526
527 static 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
544 static 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
553 static int __init broadsheetfb_init(void)
554 {
555         return platform_driver_register(&broadsheetfb_driver);
556 }
557
558 static void __exit broadsheetfb_exit(void)
559 {
560         platform_driver_unregister(&broadsheetfb_driver);
561 }
562
563 module_init(broadsheetfb_init);
564 module_exit(broadsheetfb_exit);
565
566 MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
567 MODULE_AUTHOR("Jaya Kumar");
568 MODULE_LICENSE("GPL");