Commit | Line | Data |
---|---|---|
aad09e51 JK |
1 | /* |
2 | * linux/drivers/video/hecubafb.c -- FB driver for Hecuba 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 controller | |
21 | * board, and tested with the EInk 800x600 display in 1 bit mode. | |
22 | * The interface between Hecuba and the host is TTL based GPIO. The | |
23 | * GPIO requirements are 8 writable data lines and 6 lines for control. | |
24 | * Only 4 of the controls are actually used here but 6 for future use. | |
25 | * The driver requires the IO addresses for data and control GPIO at | |
26 | * load time. It is also possible to use this display with a standard | |
27 | * PC parallel port. | |
28 | * | |
29 | * General notes: | |
30 | * - User must set hecubafb_enable=1 to enable it | |
31 | * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR c2io_addr=0xIOADDR | |
32 | * | |
33 | */ | |
34 | ||
35 | #include <linux/module.h> | |
36 | #include <linux/kernel.h> | |
37 | #include <linux/errno.h> | |
38 | #include <linux/string.h> | |
39 | #include <linux/mm.h> | |
40 | #include <linux/slab.h> | |
41 | #include <linux/vmalloc.h> | |
42 | #include <linux/delay.h> | |
43 | #include <linux/interrupt.h> | |
44 | #include <linux/fb.h> | |
45 | #include <linux/init.h> | |
46 | #include <linux/platform_device.h> | |
47 | #include <linux/list.h> | |
48 | #include <asm/uaccess.h> | |
49 | ||
50 | /* Apollo controller specific defines */ | |
51 | #define APOLLO_START_NEW_IMG 0xA0 | |
52 | #define APOLLO_STOP_IMG_DATA 0xA1 | |
53 | #define APOLLO_DISPLAY_IMG 0xA2 | |
54 | #define APOLLO_ERASE_DISPLAY 0xA3 | |
55 | #define APOLLO_INIT_DISPLAY 0xA4 | |
56 | ||
57 | /* Hecuba interface specific defines */ | |
58 | /* WUP is inverted, CD is inverted, DS is inverted */ | |
59 | #define HCB_NWUP_BIT 0x01 | |
60 | #define HCB_NDS_BIT 0x02 | |
61 | #define HCB_RW_BIT 0x04 | |
62 | #define HCB_NCD_BIT 0x08 | |
63 | #define HCB_ACK_BIT 0x80 | |
64 | ||
65 | /* Display specific information */ | |
66 | #define DPY_W 600 | |
67 | #define DPY_H 800 | |
68 | ||
69 | struct hecubafb_par { | |
70 | unsigned long dio_addr; | |
71 | unsigned long cio_addr; | |
72 | unsigned long c2io_addr; | |
73 | unsigned char ctl; | |
74 | struct fb_info *info; | |
75 | unsigned int irq; | |
76 | }; | |
77 | ||
78 | static struct fb_fix_screeninfo hecubafb_fix __devinitdata = { | |
79 | .id = "hecubafb", | |
80 | .type = FB_TYPE_PACKED_PIXELS, | |
81 | .visual = FB_VISUAL_MONO01, | |
82 | .xpanstep = 0, | |
83 | .ypanstep = 0, | |
84 | .ywrapstep = 0, | |
85 | .accel = FB_ACCEL_NONE, | |
86 | }; | |
87 | ||
88 | static struct fb_var_screeninfo hecubafb_var __devinitdata = { | |
89 | .xres = DPY_W, | |
90 | .yres = DPY_H, | |
91 | .xres_virtual = DPY_W, | |
92 | .yres_virtual = DPY_H, | |
93 | .bits_per_pixel = 1, | |
94 | .nonstd = 1, | |
95 | }; | |
96 | ||
97 | static unsigned long dio_addr; | |
98 | static unsigned long cio_addr; | |
99 | static unsigned long c2io_addr; | |
100 | static unsigned long splashval; | |
101 | static unsigned int nosplash; | |
102 | static unsigned int hecubafb_enable; | |
103 | static unsigned int irq; | |
104 | ||
105 | static DECLARE_WAIT_QUEUE_HEAD(hecubafb_waitq); | |
106 | ||
107 | static void hcb_set_ctl(struct hecubafb_par *par) | |
108 | { | |
109 | outb(par->ctl, par->cio_addr); | |
110 | } | |
111 | ||
112 | static unsigned char hcb_get_ctl(struct hecubafb_par *par) | |
113 | { | |
114 | return inb(par->c2io_addr); | |
115 | } | |
116 | ||
117 | static void hcb_set_data(struct hecubafb_par *par, unsigned char value) | |
118 | { | |
119 | outb(value, par->dio_addr); | |
120 | } | |
121 | ||
122 | static int __devinit apollo_init_control(struct hecubafb_par *par) | |
123 | { | |
124 | unsigned char ctl; | |
125 | /* for init, we want the following setup to be set: | |
126 | WUP = lo | |
127 | ACK = hi | |
128 | DS = hi | |
129 | RW = hi | |
130 | CD = lo | |
131 | */ | |
132 | ||
133 | /* write WUP to lo, DS to hi, RW to hi, CD to lo */ | |
134 | par->ctl = HCB_NWUP_BIT | HCB_RW_BIT | HCB_NCD_BIT ; | |
135 | par->ctl &= ~HCB_NDS_BIT; | |
136 | hcb_set_ctl(par); | |
137 | ||
138 | /* check ACK is not lo */ | |
139 | ctl = hcb_get_ctl(par); | |
140 | if ((ctl & HCB_ACK_BIT)) { | |
141 | printk(KERN_ERR "Fail because ACK is already low\n"); | |
142 | return -ENXIO; | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | void hcb_wait_for_ack(struct hecubafb_par *par) | |
149 | { | |
150 | ||
151 | int timeout; | |
152 | unsigned char ctl; | |
153 | ||
154 | timeout=500; | |
155 | do { | |
156 | ctl = hcb_get_ctl(par); | |
157 | if ((ctl & HCB_ACK_BIT)) | |
158 | return; | |
159 | udelay(1); | |
160 | } while (timeout--); | |
161 | printk(KERN_ERR "timed out waiting for ack\n"); | |
162 | } | |
163 | ||
164 | void hcb_wait_for_ack_clear(struct hecubafb_par *par) | |
165 | { | |
166 | ||
167 | int timeout; | |
168 | unsigned char ctl; | |
169 | ||
170 | timeout=500; | |
171 | do { | |
172 | ctl = hcb_get_ctl(par); | |
173 | if (!(ctl & HCB_ACK_BIT)) | |
174 | return; | |
175 | udelay(1); | |
176 | } while (timeout--); | |
177 | printk(KERN_ERR "timed out waiting for clear\n"); | |
178 | } | |
179 | ||
180 | void apollo_send_data(struct hecubafb_par *par, unsigned char data) | |
181 | { | |
182 | /* set data */ | |
183 | hcb_set_data(par, data); | |
184 | ||
185 | /* set DS low */ | |
186 | par->ctl |= HCB_NDS_BIT; | |
187 | hcb_set_ctl(par); | |
188 | ||
189 | hcb_wait_for_ack(par); | |
190 | ||
191 | /* set DS hi */ | |
192 | par->ctl &= ~(HCB_NDS_BIT); | |
193 | hcb_set_ctl(par); | |
194 | ||
195 | hcb_wait_for_ack_clear(par); | |
196 | } | |
197 | ||
198 | void apollo_send_command(struct hecubafb_par *par, unsigned char data) | |
199 | { | |
200 | /* command so set CD to high */ | |
201 | par->ctl &= ~(HCB_NCD_BIT); | |
202 | hcb_set_ctl(par); | |
203 | ||
204 | /* actually strobe with command */ | |
205 | apollo_send_data(par, data); | |
206 | ||
207 | /* clear CD back to low */ | |
208 | par->ctl |= (HCB_NCD_BIT); | |
209 | hcb_set_ctl(par); | |
210 | } | |
211 | ||
212 | /* main hecubafb functions */ | |
213 | ||
214 | static void hecubafb_dpy_update(struct hecubafb_par *par) | |
215 | { | |
216 | int i; | |
217 | unsigned char *buf = par->info->screen_base; | |
218 | ||
219 | apollo_send_command(par, 0xA0); | |
220 | ||
221 | for (i=0; i < (DPY_W*DPY_H/8); i++) { | |
222 | apollo_send_data(par, *(buf++)); | |
223 | } | |
224 | ||
225 | apollo_send_command(par, 0xA1); | |
226 | apollo_send_command(par, 0xA2); | |
227 | } | |
228 | ||
229 | /* this is called back from the deferred io workqueue */ | |
230 | static void hecubafb_dpy_deferred_io(struct fb_info *info, | |
231 | struct list_head *pagelist) | |
232 | { | |
233 | hecubafb_dpy_update(info->par); | |
234 | } | |
235 | ||
236 | static void hecubafb_fillrect(struct fb_info *info, | |
237 | const struct fb_fillrect *rect) | |
238 | { | |
239 | struct hecubafb_par *par = info->par; | |
240 | ||
241 | cfb_fillrect(info, rect); | |
242 | ||
243 | hecubafb_dpy_update(par); | |
244 | } | |
245 | ||
246 | static void hecubafb_copyarea(struct fb_info *info, | |
247 | const struct fb_copyarea *area) | |
248 | { | |
249 | struct hecubafb_par *par = info->par; | |
250 | ||
251 | cfb_copyarea(info, area); | |
252 | ||
253 | hecubafb_dpy_update(par); | |
254 | } | |
255 | ||
256 | static void hecubafb_imageblit(struct fb_info *info, | |
257 | const struct fb_image *image) | |
258 | { | |
259 | struct hecubafb_par *par = info->par; | |
260 | ||
261 | cfb_imageblit(info, image); | |
262 | ||
263 | hecubafb_dpy_update(par); | |
264 | } | |
265 | ||
266 | /* | |
267 | * this is the slow path from userspace. they can seek and write to | |
268 | * the fb. it's inefficient to do anything less than a full screen draw | |
269 | */ | |
270 | static ssize_t hecubafb_write(struct file *file, const char __user *buf, | |
271 | size_t count, loff_t *ppos) | |
272 | { | |
273 | struct inode *inode; | |
274 | int fbidx; | |
275 | struct fb_info *info; | |
276 | unsigned long p; | |
277 | int err=-EINVAL; | |
278 | struct hecubafb_par *par; | |
279 | unsigned int xres; | |
280 | unsigned int fbmemlength; | |
281 | ||
282 | p = *ppos; | |
283 | inode = file->f_dentry->d_inode; | |
284 | fbidx = iminor(inode); | |
285 | info = registered_fb[fbidx]; | |
286 | ||
287 | if (!info || !info->screen_base) | |
288 | return -ENODEV; | |
289 | ||
290 | par = info->par; | |
291 | xres = info->var.xres; | |
292 | fbmemlength = (xres * info->var.yres)/8; | |
293 | ||
294 | if (p > fbmemlength) | |
295 | return -ENOSPC; | |
296 | ||
297 | err = 0; | |
298 | if ((count + p) > fbmemlength) { | |
299 | count = fbmemlength - p; | |
300 | err = -ENOSPC; | |
301 | } | |
302 | ||
303 | if (count) { | |
304 | char *base_addr; | |
305 | ||
306 | base_addr = info->screen_base; | |
307 | count -= copy_from_user(base_addr + p, buf, count); | |
308 | *ppos += count; | |
309 | err = -EFAULT; | |
310 | } | |
311 | ||
312 | hecubafb_dpy_update(par); | |
313 | ||
314 | if (count) | |
315 | return count; | |
316 | ||
317 | return err; | |
318 | } | |
319 | ||
320 | static struct fb_ops hecubafb_ops = { | |
321 | .owner = THIS_MODULE, | |
322 | .fb_write = hecubafb_write, | |
323 | .fb_fillrect = hecubafb_fillrect, | |
324 | .fb_copyarea = hecubafb_copyarea, | |
325 | .fb_imageblit = hecubafb_imageblit, | |
326 | }; | |
327 | ||
328 | static struct fb_deferred_io hecubafb_defio = { | |
329 | .delay = HZ, | |
330 | .deferred_io = hecubafb_dpy_deferred_io, | |
331 | }; | |
332 | ||
333 | static int __devinit hecubafb_probe(struct platform_device *dev) | |
334 | { | |
335 | struct fb_info *info; | |
336 | int retval = -ENOMEM; | |
337 | int videomemorysize; | |
338 | unsigned char *videomemory; | |
339 | struct hecubafb_par *par; | |
340 | ||
341 | videomemorysize = (DPY_W*DPY_H)/8; | |
342 | ||
343 | if (!(videomemory = vmalloc(videomemorysize))) | |
344 | return retval; | |
345 | ||
346 | memset(videomemory, 0, videomemorysize); | |
347 | ||
348 | info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); | |
349 | if (!info) | |
350 | goto err; | |
351 | ||
352 | info->screen_base = (char __iomem *) videomemory; | |
353 | info->fbops = &hecubafb_ops; | |
354 | ||
355 | info->var = hecubafb_var; | |
356 | info->fix = hecubafb_fix; | |
357 | info->fix.smem_len = videomemorysize; | |
358 | par = info->par; | |
359 | par->info = info; | |
360 | ||
361 | if (!dio_addr || !cio_addr || !c2io_addr) { | |
362 | printk(KERN_WARNING "no IO addresses supplied\n"); | |
363 | goto err1; | |
364 | } | |
365 | par->dio_addr = dio_addr; | |
366 | par->cio_addr = cio_addr; | |
367 | par->c2io_addr = c2io_addr; | |
368 | info->flags = FBINFO_FLAG_DEFAULT; | |
369 | ||
370 | info->fbdefio = &hecubafb_defio; | |
371 | fb_deferred_io_init(info); | |
372 | ||
373 | retval = register_framebuffer(info); | |
374 | if (retval < 0) | |
375 | goto err1; | |
376 | platform_set_drvdata(dev, info); | |
377 | ||
378 | printk(KERN_INFO | |
379 | "fb%d: Hecuba frame buffer device, using %dK of video memory\n", | |
380 | info->node, videomemorysize >> 10); | |
381 | ||
382 | /* this inits the dpy */ | |
383 | apollo_init_control(par); | |
384 | ||
385 | apollo_send_command(par, APOLLO_INIT_DISPLAY); | |
386 | apollo_send_data(par, 0x81); | |
387 | ||
388 | /* have to wait while display resets */ | |
389 | udelay(1000); | |
390 | ||
391 | /* if we were told to splash the screen, we just clear it */ | |
392 | if (!nosplash) { | |
393 | apollo_send_command(par, APOLLO_ERASE_DISPLAY); | |
394 | apollo_send_data(par, splashval); | |
395 | } | |
396 | ||
397 | return 0; | |
398 | err1: | |
399 | framebuffer_release(info); | |
400 | err: | |
401 | vfree(videomemory); | |
402 | return retval; | |
403 | } | |
404 | ||
405 | static int __devexit hecubafb_remove(struct platform_device *dev) | |
406 | { | |
407 | struct fb_info *info = platform_get_drvdata(dev); | |
408 | ||
409 | if (info) { | |
410 | fb_deferred_io_cleanup(info); | |
411 | unregister_framebuffer(info); | |
412 | vfree(info->screen_base); | |
413 | framebuffer_release(info); | |
414 | } | |
415 | return 0; | |
416 | } | |
417 | ||
418 | static struct platform_driver hecubafb_driver = { | |
419 | .probe = hecubafb_probe, | |
420 | .remove = hecubafb_remove, | |
421 | .driver = { | |
422 | .name = "hecubafb", | |
423 | }, | |
424 | }; | |
425 | ||
426 | static struct platform_device *hecubafb_device; | |
427 | ||
428 | static int __init hecubafb_init(void) | |
429 | { | |
430 | int ret; | |
431 | ||
432 | if (!hecubafb_enable) { | |
433 | printk(KERN_ERR "Use hecubafb_enable to enable the device\n"); | |
434 | return -ENXIO; | |
435 | } | |
436 | ||
437 | ret = platform_driver_register(&hecubafb_driver); | |
438 | if (!ret) { | |
439 | hecubafb_device = platform_device_alloc("hecubafb", 0); | |
440 | if (hecubafb_device) | |
441 | ret = platform_device_add(hecubafb_device); | |
442 | else | |
443 | ret = -ENOMEM; | |
444 | ||
445 | if (ret) { | |
446 | platform_device_put(hecubafb_device); | |
447 | platform_driver_unregister(&hecubafb_driver); | |
448 | } | |
449 | } | |
450 | return ret; | |
451 | ||
452 | } | |
453 | ||
454 | static void __exit hecubafb_exit(void) | |
455 | { | |
456 | platform_device_unregister(hecubafb_device); | |
457 | platform_driver_unregister(&hecubafb_driver); | |
458 | } | |
459 | ||
460 | module_param(nosplash, uint, 0); | |
461 | MODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); | |
462 | module_param(hecubafb_enable, uint, 0); | |
463 | MODULE_PARM_DESC(hecubafb_enable, "Enable communication with Hecuba board"); | |
464 | module_param(dio_addr, ulong, 0); | |
465 | MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); | |
466 | module_param(cio_addr, ulong, 0); | |
467 | MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); | |
468 | module_param(c2io_addr, ulong, 0); | |
469 | MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); | |
470 | module_param(splashval, ulong, 0); | |
471 | MODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white"); | |
472 | module_param(irq, uint, 0); | |
473 | MODULE_PARM_DESC(irq, "IRQ for the Hecuba board"); | |
474 | ||
475 | module_init(hecubafb_init); | |
476 | module_exit(hecubafb_exit); | |
477 | ||
478 | MODULE_DESCRIPTION("fbdev driver for Hecuba board"); | |
479 | MODULE_AUTHOR("Jaya Kumar"); | |
480 | MODULE_LICENSE("GPL"); |