]>
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 | ||
9a268a62 | 148 | static void hcb_wait_for_ack(struct hecubafb_par *par) |
aad09e51 JK |
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 | ||
9a268a62 | 164 | static void hcb_wait_for_ack_clear(struct hecubafb_par *par) |
aad09e51 JK |
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 | ||
9a268a62 | 180 | static void apollo_send_data(struct hecubafb_par *par, unsigned char data) |
aad09e51 JK |
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 | ||
9a268a62 | 198 | static void apollo_send_command(struct hecubafb_par *par, unsigned char data) |
aad09e51 JK |
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; | |
28cdf76b | 217 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; |
aad09e51 JK |
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 | ||
d6774935 | 241 | sys_fillrect(info, rect); |
aad09e51 JK |
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 | ||
d6774935 | 251 | sys_copyarea(info, area); |
aad09e51 JK |
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 | ||
d6774935 | 261 | sys_imageblit(info, image); |
aad09e51 JK |
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 | */ | |
3f9b0880 | 270 | static ssize_t hecubafb_write(struct fb_info *info, const char __user *buf, |
aad09e51 JK |
271 | size_t count, loff_t *ppos) |
272 | { | |
aad09e51 JK |
273 | unsigned long p; |
274 | int err=-EINVAL; | |
275 | struct hecubafb_par *par; | |
276 | unsigned int xres; | |
277 | unsigned int fbmemlength; | |
278 | ||
279 | p = *ppos; | |
aad09e51 JK |
280 | par = info->par; |
281 | xres = info->var.xres; | |
282 | fbmemlength = (xres * info->var.yres)/8; | |
283 | ||
284 | if (p > fbmemlength) | |
285 | return -ENOSPC; | |
286 | ||
287 | err = 0; | |
288 | if ((count + p) > fbmemlength) { | |
289 | count = fbmemlength - p; | |
290 | err = -ENOSPC; | |
291 | } | |
292 | ||
293 | if (count) { | |
294 | char *base_addr; | |
295 | ||
28cdf76b | 296 | base_addr = (char __force *)info->screen_base; |
aad09e51 JK |
297 | count -= copy_from_user(base_addr + p, buf, count); |
298 | *ppos += count; | |
299 | err = -EFAULT; | |
300 | } | |
301 | ||
302 | hecubafb_dpy_update(par); | |
303 | ||
304 | if (count) | |
305 | return count; | |
306 | ||
307 | return err; | |
308 | } | |
309 | ||
310 | static struct fb_ops hecubafb_ops = { | |
311 | .owner = THIS_MODULE, | |
28d4564b | 312 | .fb_read = fb_sys_read, |
aad09e51 JK |
313 | .fb_write = hecubafb_write, |
314 | .fb_fillrect = hecubafb_fillrect, | |
315 | .fb_copyarea = hecubafb_copyarea, | |
316 | .fb_imageblit = hecubafb_imageblit, | |
317 | }; | |
318 | ||
319 | static struct fb_deferred_io hecubafb_defio = { | |
320 | .delay = HZ, | |
321 | .deferred_io = hecubafb_dpy_deferred_io, | |
322 | }; | |
323 | ||
324 | static int __devinit hecubafb_probe(struct platform_device *dev) | |
325 | { | |
326 | struct fb_info *info; | |
327 | int retval = -ENOMEM; | |
328 | int videomemorysize; | |
329 | unsigned char *videomemory; | |
330 | struct hecubafb_par *par; | |
331 | ||
332 | videomemorysize = (DPY_W*DPY_H)/8; | |
333 | ||
334 | if (!(videomemory = vmalloc(videomemorysize))) | |
335 | return retval; | |
336 | ||
337 | memset(videomemory, 0, videomemorysize); | |
338 | ||
339 | info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); | |
340 | if (!info) | |
341 | goto err; | |
342 | ||
343 | info->screen_base = (char __iomem *) videomemory; | |
344 | info->fbops = &hecubafb_ops; | |
345 | ||
346 | info->var = hecubafb_var; | |
347 | info->fix = hecubafb_fix; | |
348 | info->fix.smem_len = videomemorysize; | |
349 | par = info->par; | |
350 | par->info = info; | |
351 | ||
352 | if (!dio_addr || !cio_addr || !c2io_addr) { | |
353 | printk(KERN_WARNING "no IO addresses supplied\n"); | |
354 | goto err1; | |
355 | } | |
356 | par->dio_addr = dio_addr; | |
357 | par->cio_addr = cio_addr; | |
358 | par->c2io_addr = c2io_addr; | |
359 | info->flags = FBINFO_FLAG_DEFAULT; | |
360 | ||
361 | info->fbdefio = &hecubafb_defio; | |
362 | fb_deferred_io_init(info); | |
363 | ||
364 | retval = register_framebuffer(info); | |
365 | if (retval < 0) | |
366 | goto err1; | |
367 | platform_set_drvdata(dev, info); | |
368 | ||
369 | printk(KERN_INFO | |
370 | "fb%d: Hecuba frame buffer device, using %dK of video memory\n", | |
371 | info->node, videomemorysize >> 10); | |
372 | ||
373 | /* this inits the dpy */ | |
374 | apollo_init_control(par); | |
375 | ||
376 | apollo_send_command(par, APOLLO_INIT_DISPLAY); | |
377 | apollo_send_data(par, 0x81); | |
378 | ||
379 | /* have to wait while display resets */ | |
380 | udelay(1000); | |
381 | ||
382 | /* if we were told to splash the screen, we just clear it */ | |
383 | if (!nosplash) { | |
384 | apollo_send_command(par, APOLLO_ERASE_DISPLAY); | |
385 | apollo_send_data(par, splashval); | |
386 | } | |
387 | ||
388 | return 0; | |
389 | err1: | |
390 | framebuffer_release(info); | |
391 | err: | |
392 | vfree(videomemory); | |
393 | return retval; | |
394 | } | |
395 | ||
396 | static int __devexit hecubafb_remove(struct platform_device *dev) | |
397 | { | |
398 | struct fb_info *info = platform_get_drvdata(dev); | |
399 | ||
400 | if (info) { | |
401 | fb_deferred_io_cleanup(info); | |
402 | unregister_framebuffer(info); | |
28cdf76b | 403 | vfree((void __force *)info->screen_base); |
aad09e51 JK |
404 | framebuffer_release(info); |
405 | } | |
406 | return 0; | |
407 | } | |
408 | ||
409 | static struct platform_driver hecubafb_driver = { | |
410 | .probe = hecubafb_probe, | |
411 | .remove = hecubafb_remove, | |
412 | .driver = { | |
413 | .name = "hecubafb", | |
414 | }, | |
415 | }; | |
416 | ||
417 | static struct platform_device *hecubafb_device; | |
418 | ||
419 | static int __init hecubafb_init(void) | |
420 | { | |
421 | int ret; | |
422 | ||
423 | if (!hecubafb_enable) { | |
424 | printk(KERN_ERR "Use hecubafb_enable to enable the device\n"); | |
425 | return -ENXIO; | |
426 | } | |
427 | ||
428 | ret = platform_driver_register(&hecubafb_driver); | |
429 | if (!ret) { | |
430 | hecubafb_device = platform_device_alloc("hecubafb", 0); | |
431 | if (hecubafb_device) | |
432 | ret = platform_device_add(hecubafb_device); | |
433 | else | |
434 | ret = -ENOMEM; | |
435 | ||
436 | if (ret) { | |
437 | platform_device_put(hecubafb_device); | |
438 | platform_driver_unregister(&hecubafb_driver); | |
439 | } | |
440 | } | |
441 | return ret; | |
442 | ||
443 | } | |
444 | ||
445 | static void __exit hecubafb_exit(void) | |
446 | { | |
447 | platform_device_unregister(hecubafb_device); | |
448 | platform_driver_unregister(&hecubafb_driver); | |
449 | } | |
450 | ||
451 | module_param(nosplash, uint, 0); | |
452 | MODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); | |
453 | module_param(hecubafb_enable, uint, 0); | |
454 | MODULE_PARM_DESC(hecubafb_enable, "Enable communication with Hecuba board"); | |
455 | module_param(dio_addr, ulong, 0); | |
456 | MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); | |
457 | module_param(cio_addr, ulong, 0); | |
458 | MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); | |
459 | module_param(c2io_addr, ulong, 0); | |
460 | MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); | |
461 | module_param(splashval, ulong, 0); | |
462 | MODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white"); | |
463 | module_param(irq, uint, 0); | |
464 | MODULE_PARM_DESC(irq, "IRQ for the Hecuba board"); | |
465 | ||
466 | module_init(hecubafb_init); | |
467 | module_exit(hecubafb_exit); | |
468 | ||
469 | MODULE_DESCRIPTION("fbdev driver for Hecuba board"); | |
470 | MODULE_AUTHOR("Jaya Kumar"); | |
471 | MODULE_LICENSE("GPL"); |