diff options
author | Jaya Kumar <jayakumar.lkml@gmail.com> | 2007-05-08 03:37:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:27 -0400 |
commit | aad09e51eeb6ec68029271642a7528ffc08fee81 (patch) | |
tree | 420336fb8dbcb452d96ca77d820d27a25f03cd11 /drivers | |
parent | 5e841b88d23d0ea0a6ee4e76c489899d4d23ce25 (diff) |
fbdev: hecuba Framebuffer Driver
This patch implements support for the E-Ink/hecuba display device. It uses
deferred IO support.
[akpm@linux-foundation.org: linker section fixes]
Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com>
Signed-off-by: Antonino Daplas <adaplas@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/Kconfig | 14 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/hecubafb.c | 480 |
3 files changed, 495 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 61c0a03d33f2..0aeab4e725a4 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -557,6 +557,20 @@ config FB_IMAC | |||
557 | help | 557 | help |
558 | This is the frame buffer device driver for the Intel-based Macintosh | 558 | This is the frame buffer device driver for the Intel-based Macintosh |
559 | 559 | ||
560 | config FB_HECUBA | ||
561 | tristate "Hecuba board support" | ||
562 | depends on FB && X86 && MMU | ||
563 | select FB_CFB_FILLRECT | ||
564 | select FB_CFB_COPYAREA | ||
565 | select FB_CFB_IMAGEBLIT | ||
566 | select FB_DEFERRED_IO | ||
567 | help | ||
568 | This enables support for the Hecuba board. This driver was tested | ||
569 | with an E-Ink 800x600 display and x86 SBCs through a 16 bit GPIO | ||
570 | interface (8 bit data, 4 bit control). If you anticpate using | ||
571 | this driver, say Y or M; otherwise say N. You must specify the | ||
572 | GPIO IO address to be used for setting control and data. | ||
573 | |||
560 | config FB_HGA | 574 | config FB_HGA |
561 | tristate "Hercules mono graphics support" | 575 | tristate "Hercules mono graphics support" |
562 | depends on FB && X86 | 576 | depends on FB && X86 |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index deb5fa55f783..49d01eb48c7b 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -67,6 +67,7 @@ obj-$(CONFIG_FB_ACORN) += acornfb.o | |||
67 | obj-$(CONFIG_FB_ATARI) += atafb.o c2p.o atafb_mfb.o \ | 67 | obj-$(CONFIG_FB_ATARI) += atafb.o c2p.o atafb_mfb.o \ |
68 | atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o | 68 | atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o |
69 | obj-$(CONFIG_FB_MAC) += macfb.o | 69 | obj-$(CONFIG_FB_MAC) += macfb.o |
70 | obj-$(CONFIG_FB_HECUBA) += hecubafb.o | ||
70 | obj-$(CONFIG_FB_HGA) += hgafb.o | 71 | obj-$(CONFIG_FB_HGA) += hgafb.o |
71 | obj-$(CONFIG_FB_XVR500) += sunxvr500.o | 72 | obj-$(CONFIG_FB_XVR500) += sunxvr500.o |
72 | obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o | 73 | obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o |
diff --git a/drivers/video/hecubafb.c b/drivers/video/hecubafb.c new file mode 100644 index 000000000000..396dc277ab51 --- /dev/null +++ b/drivers/video/hecubafb.c | |||
@@ -0,0 +1,480 @@ | |||
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"); | ||