diff options
Diffstat (limited to 'drivers/video/n411.c')
-rw-r--r-- | drivers/video/n411.c | 202 |
1 files changed, 202 insertions, 0 deletions
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 | |||