linux.conf.au 2009: Tuz
[linux-2.6] / drivers / video / hecubafb.c
1 /*
2  * linux/drivers/video/hecubafb.c -- FB driver for Hecuba/Apollo controller
3  *
4  * Copyright (C) 2006, Jaya Kumar
5  * This work was sponsored by CIS(M) Sdn Bhd
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License. See the file COPYING in the main directory of this archive for
9  * more details.
10  *
11  * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
12  * This work was possible because of apollo display code from E-Ink's website
13  * http://support.eink.com/community
14  * All information used to write this code is from public material made
15  * available by E-Ink on its support site. Some commands such as 0xA4
16  * were found by looping through cmd=0x00 thru 0xFF and supplying random
17  * values. There are other commands that the display is capable of,
18  * beyond the 5 used here but they are more complex.
19  *
20  * This driver is written to be used with the Hecuba display architecture.
21  * The actual display chip is called Apollo and the interface electronics
22  * it needs is called Hecuba.
23  *
24  * It is intended to be architecture independent. A board specific driver
25  * must be used to perform all the physical IO interactions. An example
26  * is provided as n411.c
27  *
28  */
29
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/errno.h>
33 #include <linux/string.h>
34 #include <linux/mm.h>
35 #include <linux/slab.h>
36 #include <linux/vmalloc.h>
37 #include <linux/delay.h>
38 #include <linux/interrupt.h>
39 #include <linux/fb.h>
40 #include <linux/init.h>
41 #include <linux/platform_device.h>
42 #include <linux/list.h>
43 #include <linux/uaccess.h>
44
45 #include <video/hecubafb.h>
46
47 /* Display specific information */
48 #define DPY_W 600
49 #define DPY_H 800
50
51 static struct fb_fix_screeninfo hecubafb_fix __devinitdata = {
52         .id =           "hecubafb",
53         .type =         FB_TYPE_PACKED_PIXELS,
54         .visual =       FB_VISUAL_MONO01,
55         .xpanstep =     0,
56         .ypanstep =     0,
57         .ywrapstep =    0,
58         .line_length =  DPY_W,
59         .accel =        FB_ACCEL_NONE,
60 };
61
62 static struct fb_var_screeninfo hecubafb_var __devinitdata = {
63         .xres           = DPY_W,
64         .yres           = DPY_H,
65         .xres_virtual   = DPY_W,
66         .yres_virtual   = DPY_H,
67         .bits_per_pixel = 1,
68         .nonstd         = 1,
69 };
70
71 /* main hecubafb functions */
72
73 static void apollo_send_data(struct hecubafb_par *par, unsigned char data)
74 {
75         /* set data */
76         par->board->set_data(par, data);
77
78         /* set DS low */
79         par->board->set_ctl(par, HCB_DS_BIT, 0);
80
81         /* wait for ack */
82         par->board->wait_for_ack(par, 0);
83
84         /* set DS hi */
85         par->board->set_ctl(par, HCB_DS_BIT, 1);
86
87         /* wait for ack to clear */
88         par->board->wait_for_ack(par, 1);
89 }
90
91 static void apollo_send_command(struct hecubafb_par *par, unsigned char data)
92 {
93         /* command so set CD to high */
94         par->board->set_ctl(par, HCB_CD_BIT, 1);
95
96         /* actually strobe with command */
97         apollo_send_data(par, data);
98
99         /* clear CD back to low */
100         par->board->set_ctl(par, HCB_CD_BIT, 0);
101 }
102
103 static void hecubafb_dpy_update(struct hecubafb_par *par)
104 {
105         int i;
106         unsigned char *buf = (unsigned char __force *)par->info->screen_base;
107
108         apollo_send_command(par, APOLLO_START_NEW_IMG);
109
110         for (i=0; i < (DPY_W*DPY_H/8); i++) {
111                 apollo_send_data(par, *(buf++));
112         }
113
114         apollo_send_command(par, APOLLO_STOP_IMG_DATA);
115         apollo_send_command(par, APOLLO_DISPLAY_IMG);
116 }
117
118 /* this is called back from the deferred io workqueue */
119 static void hecubafb_dpy_deferred_io(struct fb_info *info,
120                                 struct list_head *pagelist)
121 {
122         hecubafb_dpy_update(info->par);
123 }
124
125 static void hecubafb_fillrect(struct fb_info *info,
126                                    const struct fb_fillrect *rect)
127 {
128         struct hecubafb_par *par = info->par;
129
130         sys_fillrect(info, rect);
131
132         hecubafb_dpy_update(par);
133 }
134
135 static void hecubafb_copyarea(struct fb_info *info,
136                                    const struct fb_copyarea *area)
137 {
138         struct hecubafb_par *par = info->par;
139
140         sys_copyarea(info, area);
141
142         hecubafb_dpy_update(par);
143 }
144
145 static void hecubafb_imageblit(struct fb_info *info,
146                                 const struct fb_image *image)
147 {
148         struct hecubafb_par *par = info->par;
149
150         sys_imageblit(info, image);
151
152         hecubafb_dpy_update(par);
153 }
154
155 /*
156  * this is the slow path from userspace. they can seek and write to
157  * the fb. it's inefficient to do anything less than a full screen draw
158  */
159 static ssize_t hecubafb_write(struct fb_info *info, const char __user *buf,
160                                 size_t count, loff_t *ppos)
161 {
162         struct hecubafb_par *par = info->par;
163         unsigned long p = *ppos;
164         void *dst;
165         int err = 0;
166         unsigned long total_size;
167
168         if (info->state != FBINFO_STATE_RUNNING)
169                 return -EPERM;
170
171         total_size = info->fix.smem_len;
172
173         if (p > total_size)
174                 return -EFBIG;
175
176         if (count > total_size) {
177                 err = -EFBIG;
178                 count = total_size;
179         }
180
181         if (count + p > total_size) {
182                 if (!err)
183                         err = -ENOSPC;
184
185                 count = total_size - p;
186         }
187
188         dst = (void __force *) (info->screen_base + p);
189
190         if (copy_from_user(dst, buf, count))
191                 err = -EFAULT;
192
193         if  (!err)
194                 *ppos += count;
195
196         hecubafb_dpy_update(par);
197
198         return (err) ? err : count;
199 }
200
201 static struct fb_ops hecubafb_ops = {
202         .owner          = THIS_MODULE,
203         .fb_read        = fb_sys_read,
204         .fb_write       = hecubafb_write,
205         .fb_fillrect    = hecubafb_fillrect,
206         .fb_copyarea    = hecubafb_copyarea,
207         .fb_imageblit   = hecubafb_imageblit,
208 };
209
210 static struct fb_deferred_io hecubafb_defio = {
211         .delay          = HZ,
212         .deferred_io    = hecubafb_dpy_deferred_io,
213 };
214
215 static int __devinit hecubafb_probe(struct platform_device *dev)
216 {
217         struct fb_info *info;
218         struct hecuba_board *board;
219         int retval = -ENOMEM;
220         int videomemorysize;
221         unsigned char *videomemory;
222         struct hecubafb_par *par;
223
224         /* pick up board specific routines */
225         board = dev->dev.platform_data;
226         if (!board)
227                 return -EINVAL;
228
229         /* try to count device specific driver, if can't, platform recalls */
230         if (!try_module_get(board->owner))
231                 return -ENODEV;
232
233         videomemorysize = (DPY_W*DPY_H)/8;
234
235         if (!(videomemory = vmalloc(videomemorysize)))
236                 return retval;
237
238         memset(videomemory, 0, videomemorysize);
239
240         info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev);
241         if (!info)
242                 goto err_fballoc;
243
244         info->screen_base = (char __force __iomem *)videomemory;
245         info->fbops = &hecubafb_ops;
246
247         info->var = hecubafb_var;
248         info->fix = hecubafb_fix;
249         info->fix.smem_len = videomemorysize;
250         par = info->par;
251         par->info = info;
252         par->board = board;
253         par->send_command = apollo_send_command;
254         par->send_data = apollo_send_data;
255
256         info->flags = FBINFO_FLAG_DEFAULT;
257
258         info->fbdefio = &hecubafb_defio;
259         fb_deferred_io_init(info);
260
261         retval = register_framebuffer(info);
262         if (retval < 0)
263                 goto err_fbreg;
264         platform_set_drvdata(dev, info);
265
266         printk(KERN_INFO
267                "fb%d: Hecuba frame buffer device, using %dK of video memory\n",
268                info->node, videomemorysize >> 10);
269
270         /* this inits the dpy */
271         retval = par->board->init(par);
272         if (retval < 0)
273                 goto err_fbreg;
274
275         return 0;
276 err_fbreg:
277         framebuffer_release(info);
278 err_fballoc:
279         vfree(videomemory);
280         module_put(board->owner);
281         return retval;
282 }
283
284 static int __devexit hecubafb_remove(struct platform_device *dev)
285 {
286         struct fb_info *info = platform_get_drvdata(dev);
287
288         if (info) {
289                 struct hecubafb_par *par = info->par;
290                 fb_deferred_io_cleanup(info);
291                 unregister_framebuffer(info);
292                 vfree((void __force *)info->screen_base);
293                 if (par->board->remove)
294                         par->board->remove(par);
295                 module_put(par->board->owner);
296                 framebuffer_release(info);
297         }
298         return 0;
299 }
300
301 static struct platform_driver hecubafb_driver = {
302         .probe  = hecubafb_probe,
303         .remove = hecubafb_remove,
304         .driver = {
305                 .owner  = THIS_MODULE,
306                 .name   = "hecubafb",
307         },
308 };
309
310 static int __init hecubafb_init(void)
311 {
312         return platform_driver_register(&hecubafb_driver);
313 }
314
315 static void __exit hecubafb_exit(void)
316 {
317         platform_driver_unregister(&hecubafb_driver);
318 }
319
320 module_init(hecubafb_init);
321 module_exit(hecubafb_exit);
322
323 MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller");
324 MODULE_AUTHOR("Jaya Kumar");
325 MODULE_LICENSE("GPL");