diff options
author | Jaya Kumar <jayakumar.lkml@gmail.com> | 2008-04-28 05:15:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-28 11:58:41 -0400 |
commit | 0e27aa3dabb541edee9f23b37114856a528de01e (patch) | |
tree | 5516f7e6445f0bd95a1e4bbca2c3c678643b28a4 | |
parent | 2422fbba0684ddf08898ec2f3cf23cb16a54b3f5 (diff) |
fbdev: platforming hecubafb and n411
This patch splits hecubafb into the platform independent hecubafb and the
platform dependent n411.
Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/video/Kconfig | 17 | ||||
-rw-r--r-- | drivers/video/hecubafb.c | 250 | ||||
-rw-r--r-- | drivers/video/n411.c | 202 | ||||
-rw-r--r-- | include/video/hecubafb.h | 51 |
4 files changed, 314 insertions, 206 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ff6ea9a9be22..73d3afad7c50 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -178,6 +178,11 @@ config FB_METRONOME | |||
178 | depends on FB | 178 | depends on FB |
179 | depends on FB_DEFERRED_IO | 179 | depends on FB_DEFERRED_IO |
180 | 180 | ||
181 | config FB_HECUBA | ||
182 | tristate | ||
183 | depends on FB | ||
184 | depends on FB_DEFERRED_IO | ||
185 | |||
181 | config FB_SVGALIB | 186 | config FB_SVGALIB |
182 | tristate | 187 | tristate |
183 | depends on FB | 188 | depends on FB |
@@ -703,20 +708,18 @@ config FB_IMAC | |||
703 | help | 708 | help |
704 | This is the frame buffer device driver for the Intel-based Macintosh | 709 | This is the frame buffer device driver for the Intel-based Macintosh |
705 | 710 | ||
706 | config FB_HECUBA | 711 | config FB_N411 |
707 | tristate "Hecuba board support" | 712 | tristate "N411 Apollo/Hecuba devkit support" |
708 | depends on FB && X86 && MMU | 713 | depends on FB && X86 && MMU |
709 | select FB_SYS_FILLRECT | 714 | select FB_SYS_FILLRECT |
710 | select FB_SYS_COPYAREA | 715 | select FB_SYS_COPYAREA |
711 | select FB_SYS_IMAGEBLIT | 716 | select FB_SYS_IMAGEBLIT |
712 | select FB_SYS_FOPS | 717 | select FB_SYS_FOPS |
713 | select FB_DEFERRED_IO | 718 | select FB_DEFERRED_IO |
719 | select FB_HECUBA | ||
714 | help | 720 | help |
715 | This enables support for the Hecuba board. This driver was tested | 721 | This enables support for the Apollo display controller in its |
716 | with an E-Ink 800x600 display and x86 SBCs through a 16 bit GPIO | 722 | Hecuba form using the n411 devkit. |
717 | interface (8 bit data, 4 bit control). If you anticipate using | ||
718 | this driver, say Y or M; otherwise say N. You must specify the | ||
719 | GPIO IO address to be used for setting control and data. | ||
720 | 723 | ||
721 | config FB_HGA | 724 | config FB_HGA |
722 | tristate "Hercules mono graphics support" | 725 | tristate "Hercules mono graphics support" |
diff --git a/drivers/video/hecubafb.c b/drivers/video/hecubafb.c index b77d033665da..0b4bffbe67c8 100644 --- a/drivers/video/hecubafb.c +++ b/drivers/video/hecubafb.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * linux/drivers/video/hecubafb.c -- FB driver for Hecuba controller | 2 | * linux/drivers/video/hecubafb.c -- FB driver for Hecuba/Apollo controller |
3 | * | 3 | * |
4 | * Copyright (C) 2006, Jaya Kumar | 4 | * Copyright (C) 2006, Jaya Kumar |
5 | * This work was sponsored by CIS(M) Sdn Bhd | 5 | * This work was sponsored by CIS(M) Sdn Bhd |
@@ -17,18 +17,13 @@ | |||
17 | * values. There are other commands that the display is capable of, | 17 | * values. There are other commands that the display is capable of, |
18 | * beyond the 5 used here but they are more complex. | 18 | * beyond the 5 used here but they are more complex. |
19 | * | 19 | * |
20 | * This driver is written to be used with the Hecuba display controller | 20 | * This driver is written to be used with the Hecuba display architecture. |
21 | * board, and tested with the EInk 800x600 display in 1 bit mode. | 21 | * The actual display chip is called Apollo and the interface electronics |
22 | * The interface between Hecuba and the host is TTL based GPIO. The | 22 | * it needs is called Hecuba. |
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 | * | 23 | * |
29 | * General notes: | 24 | * It is intended to be architecture independent. A board specific driver |
30 | * - User must set hecubafb_enable=1 to enable it | 25 | * must be used to perform all the physical IO interactions. An example |
31 | * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR c2io_addr=0xIOADDR | 26 | * is provided as n411.c |
32 | * | 27 | * |
33 | */ | 28 | */ |
34 | 29 | ||
@@ -47,34 +42,12 @@ | |||
47 | #include <linux/list.h> | 42 | #include <linux/list.h> |
48 | #include <linux/uaccess.h> | 43 | #include <linux/uaccess.h> |
49 | 44 | ||
50 | /* Apollo controller specific defines */ | 45 | #include <video/hecubafb.h> |
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 | 46 | ||
65 | /* Display specific information */ | 47 | /* Display specific information */ |
66 | #define DPY_W 600 | 48 | #define DPY_W 600 |
67 | #define DPY_H 800 | 49 | #define DPY_H 800 |
68 | 50 | ||
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 = { | 51 | static struct fb_fix_screeninfo hecubafb_fix __devinitdata = { |
79 | .id = "hecubafb", | 52 | .id = "hecubafb", |
80 | .type = FB_TYPE_PACKED_PIXELS, | 53 | .type = FB_TYPE_PACKED_PIXELS, |
@@ -82,6 +55,7 @@ static struct fb_fix_screeninfo hecubafb_fix __devinitdata = { | |||
82 | .xpanstep = 0, | 55 | .xpanstep = 0, |
83 | .ypanstep = 0, | 56 | .ypanstep = 0, |
84 | .ywrapstep = 0, | 57 | .ywrapstep = 0, |
58 | .line_length = DPY_W, | ||
85 | .accel = FB_ACCEL_NONE, | 59 | .accel = FB_ACCEL_NONE, |
86 | }; | 60 | }; |
87 | 61 | ||
@@ -94,136 +68,51 @@ static struct fb_var_screeninfo hecubafb_var __devinitdata = { | |||
94 | .nonstd = 1, | 68 | .nonstd = 1, |
95 | }; | 69 | }; |
96 | 70 | ||
97 | static unsigned long dio_addr; | 71 | /* main hecubafb functions */ |
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 | static 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 | static 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 | 72 | ||
180 | static void apollo_send_data(struct hecubafb_par *par, unsigned char data) | 73 | static void apollo_send_data(struct hecubafb_par *par, unsigned char data) |
181 | { | 74 | { |
182 | /* set data */ | 75 | /* set data */ |
183 | hcb_set_data(par, data); | 76 | par->board->set_data(par, data); |
184 | 77 | ||
185 | /* set DS low */ | 78 | /* set DS low */ |
186 | par->ctl |= HCB_NDS_BIT; | 79 | par->board->set_ctl(par, HCB_DS_BIT, 0); |
187 | hcb_set_ctl(par); | ||
188 | 80 | ||
189 | hcb_wait_for_ack(par); | 81 | /* wait for ack */ |
82 | par->board->wait_for_ack(par, 0); | ||
190 | 83 | ||
191 | /* set DS hi */ | 84 | /* set DS hi */ |
192 | par->ctl &= ~(HCB_NDS_BIT); | 85 | par->board->set_ctl(par, HCB_DS_BIT, 1); |
193 | hcb_set_ctl(par); | ||
194 | 86 | ||
195 | hcb_wait_for_ack_clear(par); | 87 | /* wait for ack to clear */ |
88 | par->board->wait_for_ack(par, 1); | ||
196 | } | 89 | } |
197 | 90 | ||
198 | static void apollo_send_command(struct hecubafb_par *par, unsigned char data) | 91 | static void apollo_send_command(struct hecubafb_par *par, unsigned char data) |
199 | { | 92 | { |
200 | /* command so set CD to high */ | 93 | /* command so set CD to high */ |
201 | par->ctl &= ~(HCB_NCD_BIT); | 94 | par->board->set_ctl(par, HCB_CD_BIT, 1); |
202 | hcb_set_ctl(par); | ||
203 | 95 | ||
204 | /* actually strobe with command */ | 96 | /* actually strobe with command */ |
205 | apollo_send_data(par, data); | 97 | apollo_send_data(par, data); |
206 | 98 | ||
207 | /* clear CD back to low */ | 99 | /* clear CD back to low */ |
208 | par->ctl |= (HCB_NCD_BIT); | 100 | par->board->set_ctl(par, HCB_CD_BIT, 0); |
209 | hcb_set_ctl(par); | ||
210 | } | 101 | } |
211 | 102 | ||
212 | /* main hecubafb functions */ | ||
213 | |||
214 | static void hecubafb_dpy_update(struct hecubafb_par *par) | 103 | static void hecubafb_dpy_update(struct hecubafb_par *par) |
215 | { | 104 | { |
216 | int i; | 105 | int i; |
217 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; | 106 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; |
218 | 107 | ||
219 | apollo_send_command(par, 0xA0); | 108 | apollo_send_command(par, APOLLO_START_NEW_IMG); |
220 | 109 | ||
221 | for (i=0; i < (DPY_W*DPY_H/8); i++) { | 110 | for (i=0; i < (DPY_W*DPY_H/8); i++) { |
222 | apollo_send_data(par, *(buf++)); | 111 | apollo_send_data(par, *(buf++)); |
223 | } | 112 | } |
224 | 113 | ||
225 | apollo_send_command(par, 0xA1); | 114 | apollo_send_command(par, APOLLO_STOP_IMG_DATA); |
226 | apollo_send_command(par, 0xA2); | 115 | apollo_send_command(par, APOLLO_DISPLAY_IMG); |
227 | } | 116 | } |
228 | 117 | ||
229 | /* this is called back from the deferred io workqueue */ | 118 | /* this is called back from the deferred io workqueue */ |
@@ -326,11 +215,21 @@ static struct fb_deferred_io hecubafb_defio = { | |||
326 | static int __devinit hecubafb_probe(struct platform_device *dev) | 215 | static int __devinit hecubafb_probe(struct platform_device *dev) |
327 | { | 216 | { |
328 | struct fb_info *info; | 217 | struct fb_info *info; |
218 | struct hecuba_board *board; | ||
329 | int retval = -ENOMEM; | 219 | int retval = -ENOMEM; |
330 | int videomemorysize; | 220 | int videomemorysize; |
331 | unsigned char *videomemory; | 221 | unsigned char *videomemory; |
332 | struct hecubafb_par *par; | 222 | struct hecubafb_par *par; |
333 | 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 | |||
334 | videomemorysize = (DPY_W*DPY_H)/8; | 233 | videomemorysize = (DPY_W*DPY_H)/8; |
335 | 234 | ||
336 | if (!(videomemory = vmalloc(videomemorysize))) | 235 | if (!(videomemory = vmalloc(videomemorysize))) |
@@ -340,9 +239,9 @@ static int __devinit hecubafb_probe(struct platform_device *dev) | |||
340 | 239 | ||
341 | info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); | 240 | info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); |
342 | if (!info) | 241 | if (!info) |
343 | goto err; | 242 | goto err_fballoc; |
344 | 243 | ||
345 | info->screen_base = (char __iomem *) videomemory; | 244 | info->screen_base = (char __force __iomem *)videomemory; |
346 | info->fbops = &hecubafb_ops; | 245 | info->fbops = &hecubafb_ops; |
347 | 246 | ||
348 | info->var = hecubafb_var; | 247 | info->var = hecubafb_var; |
@@ -350,14 +249,10 @@ static int __devinit hecubafb_probe(struct platform_device *dev) | |||
350 | info->fix.smem_len = videomemorysize; | 249 | info->fix.smem_len = videomemorysize; |
351 | par = info->par; | 250 | par = info->par; |
352 | par->info = info; | 251 | par->info = info; |
252 | par->board = board; | ||
253 | par->send_command = apollo_send_command; | ||
254 | par->send_data = apollo_send_data; | ||
353 | 255 | ||
354 | if (!dio_addr || !cio_addr || !c2io_addr) { | ||
355 | printk(KERN_WARNING "no IO addresses supplied\n"); | ||
356 | goto err1; | ||
357 | } | ||
358 | par->dio_addr = dio_addr; | ||
359 | par->cio_addr = cio_addr; | ||
360 | par->c2io_addr = c2io_addr; | ||
361 | info->flags = FBINFO_FLAG_DEFAULT; | 256 | info->flags = FBINFO_FLAG_DEFAULT; |
362 | 257 | ||
363 | info->fbdefio = &hecubafb_defio; | 258 | info->fbdefio = &hecubafb_defio; |
@@ -365,7 +260,7 @@ static int __devinit hecubafb_probe(struct platform_device *dev) | |||
365 | 260 | ||
366 | retval = register_framebuffer(info); | 261 | retval = register_framebuffer(info); |
367 | if (retval < 0) | 262 | if (retval < 0) |
368 | goto err1; | 263 | goto err_fbreg; |
369 | platform_set_drvdata(dev, info); | 264 | platform_set_drvdata(dev, info); |
370 | 265 | ||
371 | printk(KERN_INFO | 266 | printk(KERN_INFO |
@@ -373,25 +268,16 @@ static int __devinit hecubafb_probe(struct platform_device *dev) | |||
373 | info->node, videomemorysize >> 10); | 268 | info->node, videomemorysize >> 10); |
374 | 269 | ||
375 | /* this inits the dpy */ | 270 | /* this inits the dpy */ |
376 | apollo_init_control(par); | 271 | retval = par->board->init(par); |
377 | 272 | if (retval < 0) | |
378 | apollo_send_command(par, APOLLO_INIT_DISPLAY); | 273 | goto err_fbreg; |
379 | apollo_send_data(par, 0x81); | ||
380 | |||
381 | /* have to wait while display resets */ | ||
382 | udelay(1000); | ||
383 | |||
384 | /* if we were told to splash the screen, we just clear it */ | ||
385 | if (!nosplash) { | ||
386 | apollo_send_command(par, APOLLO_ERASE_DISPLAY); | ||
387 | apollo_send_data(par, splashval); | ||
388 | } | ||
389 | 274 | ||
390 | return 0; | 275 | return 0; |
391 | err1: | 276 | err_fbreg: |
392 | framebuffer_release(info); | 277 | framebuffer_release(info); |
393 | err: | 278 | err_fballoc: |
394 | vfree(videomemory); | 279 | vfree(videomemory); |
280 | module_put(board->owner); | ||
395 | return retval; | 281 | return retval; |
396 | } | 282 | } |
397 | 283 | ||
@@ -400,9 +286,13 @@ static int __devexit hecubafb_remove(struct platform_device *dev) | |||
400 | struct fb_info *info = platform_get_drvdata(dev); | 286 | struct fb_info *info = platform_get_drvdata(dev); |
401 | 287 | ||
402 | if (info) { | 288 | if (info) { |
289 | struct hecubafb_par *par = info->par; | ||
403 | fb_deferred_io_cleanup(info); | 290 | fb_deferred_io_cleanup(info); |
404 | unregister_framebuffer(info); | 291 | unregister_framebuffer(info); |
405 | vfree((void __force *)info->screen_base); | 292 | vfree((void __force *)info->screen_base); |
293 | if (par->board->remove) | ||
294 | par->board->remove(par); | ||
295 | module_put(par->board->owner); | ||
406 | framebuffer_release(info); | 296 | framebuffer_release(info); |
407 | } | 297 | } |
408 | return 0; | 298 | return 0; |
@@ -412,62 +302,24 @@ static struct platform_driver hecubafb_driver = { | |||
412 | .probe = hecubafb_probe, | 302 | .probe = hecubafb_probe, |
413 | .remove = hecubafb_remove, | 303 | .remove = hecubafb_remove, |
414 | .driver = { | 304 | .driver = { |
305 | .owner = THIS_MODULE, | ||
415 | .name = "hecubafb", | 306 | .name = "hecubafb", |
416 | }, | 307 | }, |
417 | }; | 308 | }; |
418 | 309 | ||
419 | static struct platform_device *hecubafb_device; | ||
420 | |||
421 | static int __init hecubafb_init(void) | 310 | static int __init hecubafb_init(void) |
422 | { | 311 | { |
423 | int ret; | 312 | return platform_driver_register(&hecubafb_driver); |
424 | |||
425 | if (!hecubafb_enable) { | ||
426 | printk(KERN_ERR "Use hecubafb_enable to enable the device\n"); | ||
427 | return -ENXIO; | ||
428 | } | ||
429 | |||
430 | ret = platform_driver_register(&hecubafb_driver); | ||
431 | if (!ret) { | ||
432 | hecubafb_device = platform_device_alloc("hecubafb", 0); | ||
433 | if (hecubafb_device) | ||
434 | ret = platform_device_add(hecubafb_device); | ||
435 | else | ||
436 | ret = -ENOMEM; | ||
437 | |||
438 | if (ret) { | ||
439 | platform_device_put(hecubafb_device); | ||
440 | platform_driver_unregister(&hecubafb_driver); | ||
441 | } | ||
442 | } | ||
443 | return ret; | ||
444 | |||
445 | } | 313 | } |
446 | 314 | ||
447 | static void __exit hecubafb_exit(void) | 315 | static void __exit hecubafb_exit(void) |
448 | { | 316 | { |
449 | platform_device_unregister(hecubafb_device); | ||
450 | platform_driver_unregister(&hecubafb_driver); | 317 | platform_driver_unregister(&hecubafb_driver); |
451 | } | 318 | } |
452 | 319 | ||
453 | module_param(nosplash, uint, 0); | ||
454 | MODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); | ||
455 | module_param(hecubafb_enable, uint, 0); | ||
456 | MODULE_PARM_DESC(hecubafb_enable, "Enable communication with Hecuba board"); | ||
457 | module_param(dio_addr, ulong, 0); | ||
458 | MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); | ||
459 | module_param(cio_addr, ulong, 0); | ||
460 | MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); | ||
461 | module_param(c2io_addr, ulong, 0); | ||
462 | MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); | ||
463 | module_param(splashval, ulong, 0); | ||
464 | MODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white"); | ||
465 | module_param(irq, uint, 0); | ||
466 | MODULE_PARM_DESC(irq, "IRQ for the Hecuba board"); | ||
467 | |||
468 | module_init(hecubafb_init); | 320 | module_init(hecubafb_init); |
469 | module_exit(hecubafb_exit); | 321 | module_exit(hecubafb_exit); |
470 | 322 | ||
471 | MODULE_DESCRIPTION("fbdev driver for Hecuba board"); | 323 | MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller"); |
472 | MODULE_AUTHOR("Jaya Kumar"); | 324 | MODULE_AUTHOR("Jaya Kumar"); |
473 | MODULE_LICENSE("GPL"); | 325 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/video/n411.c b/drivers/video/n411.c new file mode 100644 index 000000000000..935830fea7b6 --- /dev/null +++ b/drivers/video/n411.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/n411.c -- Platform device for N411 EPD kit | ||
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 Hecuba display controller | ||
13 | * board, and tested with the EInk 800x600 display in 1 bit mode. | ||
14 | * The interface between Hecuba and the host is TTL based GPIO. The | ||
15 | * GPIO requirements are 8 writable data lines and 6 lines for control. | ||
16 | * Only 4 of the controls are actually used here but 6 for future use. | ||
17 | * The driver requires the IO addresses for data and control GPIO at | ||
18 | * load time. It is also possible to use this display with a standard | ||
19 | * PC parallel port. | ||
20 | * | ||
21 | * General notes: | ||
22 | * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR c2io_addr=0xIOADDR | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/string.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/fb.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/platform_device.h> | ||
35 | #include <linux/list.h> | ||
36 | #include <linux/uaccess.h> | ||
37 | #include <linux/irq.h> | ||
38 | |||
39 | #include <video/hecubafb.h> | ||
40 | |||
41 | static unsigned long dio_addr; | ||
42 | static unsigned long cio_addr; | ||
43 | static unsigned long c2io_addr; | ||
44 | static unsigned long splashval; | ||
45 | static unsigned int nosplash; | ||
46 | static unsigned char ctl; | ||
47 | |||
48 | static void n411_set_ctl(struct hecubafb_par *par, unsigned char bit, unsigned | ||
49 | char state) | ||
50 | { | ||
51 | switch (bit) { | ||
52 | case HCB_CD_BIT: | ||
53 | if (state) | ||
54 | ctl &= ~(HCB_CD_BIT); | ||
55 | else | ||
56 | ctl |= HCB_CD_BIT; | ||
57 | break; | ||
58 | case HCB_DS_BIT: | ||
59 | if (state) | ||
60 | ctl &= ~(HCB_DS_BIT); | ||
61 | else | ||
62 | ctl |= HCB_DS_BIT; | ||
63 | break; | ||
64 | } | ||
65 | outb(ctl, cio_addr); | ||
66 | } | ||
67 | |||
68 | static unsigned char n411_get_ctl(struct hecubafb_par *par) | ||
69 | { | ||
70 | return inb(c2io_addr); | ||
71 | } | ||
72 | |||
73 | static void n411_set_data(struct hecubafb_par *par, unsigned char value) | ||
74 | { | ||
75 | outb(value, dio_addr); | ||
76 | } | ||
77 | |||
78 | static void n411_wait_for_ack(struct hecubafb_par *par, int clear) | ||
79 | { | ||
80 | int timeout; | ||
81 | unsigned char tmp; | ||
82 | |||
83 | timeout = 500; | ||
84 | do { | ||
85 | tmp = n411_get_ctl(par); | ||
86 | if ((tmp & HCB_ACK_BIT) && (!clear)) | ||
87 | return; | ||
88 | else if (!(tmp & HCB_ACK_BIT) && (clear)) | ||
89 | return; | ||
90 | udelay(1); | ||
91 | } while (timeout--); | ||
92 | printk(KERN_ERR "timed out waiting for ack\n"); | ||
93 | } | ||
94 | |||
95 | static int n411_init_control(struct hecubafb_par *par) | ||
96 | { | ||
97 | unsigned char tmp; | ||
98 | /* for init, we want the following setup to be set: | ||
99 | WUP = lo | ||
100 | ACK = hi | ||
101 | DS = hi | ||
102 | RW = hi | ||
103 | CD = lo | ||
104 | */ | ||
105 | |||
106 | /* write WUP to lo, DS to hi, RW to hi, CD to lo */ | ||
107 | ctl = HCB_WUP_BIT | HCB_RW_BIT | HCB_CD_BIT ; | ||
108 | n411_set_ctl(par, HCB_DS_BIT, 1); | ||
109 | |||
110 | /* check ACK is not lo */ | ||
111 | tmp = n411_get_ctl(par); | ||
112 | if (tmp & HCB_ACK_BIT) { | ||
113 | printk(KERN_ERR "Fail because ACK is already low\n"); | ||
114 | return -ENXIO; | ||
115 | } | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | |||
121 | static int n411_init_board(struct hecubafb_par *par) | ||
122 | { | ||
123 | int retval; | ||
124 | |||
125 | retval = n411_init_control(par); | ||
126 | if (retval) | ||
127 | return retval; | ||
128 | |||
129 | par->send_command(par, APOLLO_INIT_DISPLAY); | ||
130 | par->send_data(par, 0x81); | ||
131 | |||
132 | /* have to wait while display resets */ | ||
133 | udelay(1000); | ||
134 | |||
135 | /* if we were told to splash the screen, we just clear it */ | ||
136 | if (!nosplash) { | ||
137 | par->send_command(par, APOLLO_ERASE_DISPLAY); | ||
138 | par->send_data(par, splashval); | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static struct hecuba_board n411_board = { | ||
145 | .owner = THIS_MODULE, | ||
146 | .init = n411_init_board, | ||
147 | .set_ctl = n411_set_ctl, | ||
148 | .set_data = n411_set_data, | ||
149 | .wait_for_ack = n411_wait_for_ack, | ||
150 | }; | ||
151 | |||
152 | static struct platform_device *n411_device; | ||
153 | static int __init n411_init(void) | ||
154 | { | ||
155 | int ret; | ||
156 | if (!dio_addr || !cio_addr || !c2io_addr) { | ||
157 | printk(KERN_WARNING "no IO addresses supplied\n"); | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | /* request our platform independent driver */ | ||
162 | request_module("hecubafb"); | ||
163 | |||
164 | n411_device = platform_device_alloc("hecubafb", -1); | ||
165 | if (!n411_device) | ||
166 | return -ENOMEM; | ||
167 | |||
168 | platform_device_add_data(n411_device, &n411_board, sizeof(n411_board)); | ||
169 | |||
170 | /* this _add binds hecubafb to n411. hecubafb refcounts n411 */ | ||
171 | ret = platform_device_add(n411_device); | ||
172 | |||
173 | if (ret) | ||
174 | platform_device_put(n411_device); | ||
175 | |||
176 | return ret; | ||
177 | |||
178 | } | ||
179 | |||
180 | static void __exit n411_exit(void) | ||
181 | { | ||
182 | platform_device_unregister(n411_device); | ||
183 | } | ||
184 | |||
185 | module_init(n411_init); | ||
186 | module_exit(n411_exit); | ||
187 | |||
188 | module_param(nosplash, uint, 0); | ||
189 | MODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); | ||
190 | module_param(dio_addr, ulong, 0); | ||
191 | MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); | ||
192 | module_param(cio_addr, ulong, 0); | ||
193 | MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); | ||
194 | module_param(c2io_addr, ulong, 0); | ||
195 | MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); | ||
196 | module_param(splashval, ulong, 0); | ||
197 | MODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white"); | ||
198 | |||
199 | MODULE_DESCRIPTION("board driver for n411 hecuba/apollo epd kit"); | ||
200 | MODULE_AUTHOR("Jaya Kumar"); | ||
201 | MODULE_LICENSE("GPL"); | ||
202 | |||
diff --git a/include/video/hecubafb.h b/include/video/hecubafb.h new file mode 100644 index 000000000000..7b9952339762 --- /dev/null +++ b/include/video/hecubafb.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * hecubafb.h - definitions for the hecuba framebuffer driver | ||
3 | * | ||
4 | * Copyright (C) 2008 by 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 | */ | ||
11 | |||
12 | #ifndef _LINUX_HECUBAFB_H_ | ||
13 | #define _LINUX_HECUBAFB_H_ | ||
14 | |||
15 | /* Apollo controller specific defines */ | ||
16 | #define APOLLO_START_NEW_IMG 0xA0 | ||
17 | #define APOLLO_STOP_IMG_DATA 0xA1 | ||
18 | #define APOLLO_DISPLAY_IMG 0xA2 | ||
19 | #define APOLLO_ERASE_DISPLAY 0xA3 | ||
20 | #define APOLLO_INIT_DISPLAY 0xA4 | ||
21 | |||
22 | /* Hecuba interface specific defines */ | ||
23 | #define HCB_WUP_BIT 0x01 | ||
24 | #define HCB_DS_BIT 0x02 | ||
25 | #define HCB_RW_BIT 0x04 | ||
26 | #define HCB_CD_BIT 0x08 | ||
27 | #define HCB_ACK_BIT 0x80 | ||
28 | |||
29 | /* struct used by hecuba. board specific stuff comes from *board */ | ||
30 | struct hecubafb_par { | ||
31 | struct fb_info *info; | ||
32 | struct hecuba_board *board; | ||
33 | void (*send_command)(struct hecubafb_par *, unsigned char); | ||
34 | void (*send_data)(struct hecubafb_par *, unsigned char); | ||
35 | }; | ||
36 | |||
37 | /* board specific routines | ||
38 | board drivers can implement wait_for_ack with interrupts if desired. if | ||
39 | wait_for_ack is called with clear=0, then go to sleep and return when ack | ||
40 | goes hi or if wait_for_ack with clear=1, then return when ack goes lo */ | ||
41 | struct hecuba_board { | ||
42 | struct module *owner; | ||
43 | void (*remove)(struct hecubafb_par *); | ||
44 | void (*set_ctl)(struct hecubafb_par *, unsigned char, unsigned char); | ||
45 | void (*set_data)(struct hecubafb_par *, unsigned char); | ||
46 | void (*wait_for_ack)(struct hecubafb_par *, int); | ||
47 | int (*init)(struct hecubafb_par *); | ||
48 | }; | ||
49 | |||
50 | |||
51 | #endif | ||