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 /drivers/video/hecubafb.c | |
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>
Diffstat (limited to 'drivers/video/hecubafb.c')
-rw-r--r-- | drivers/video/hecubafb.c | 250 |
1 files changed, 51 insertions, 199 deletions
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"); |