1 | /* |
2 | * am300epd.c -- Platform device for AM300 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 | * This work was made possible by help and equipment support from E-Ink |
11 | * Corporation. http://support.eink.com/community |
12 | * |
13 | * This driver is written to be used with the Broadsheet display controller. |
14 | * on the AM300 EPD prototype kit/development kit with an E-Ink 800x600 |
15 | * Vizplex EPD on a Gumstix board using the Broadsheet interface board. |
16 | * |
17 | */ |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/errno.h> |
22 | #include <linux/string.h> |
23 | #include <linux/delay.h> |
24 | #include <linux/interrupt.h> |
25 | #include <linux/fb.h> |
26 | #include <linux/init.h> |
27 | #include <linux/platform_device.h> |
28 | #include <linux/irq.h> |
29 | #include <linux/gpio.h> |
30 | |
31 | #include "gumstix.h" |
32 | #include "mfp-pxa25x.h" |
33 | #include "irqs.h" |
34 | #include <linux/platform_data/video-pxafb.h> |
35 | |
36 | #include "generic.h" |
37 | |
38 | #include <video/broadsheetfb.h> |
39 | |
40 | static unsigned int panel_type = 6; |
41 | static struct platform_device *am300_device; |
42 | static struct broadsheet_board am300_board; |
43 | |
44 | static unsigned long am300_pin_config[] __initdata = { |
45 | GPIO16_GPIO, |
46 | GPIO17_GPIO, |
47 | GPIO32_GPIO, |
48 | GPIO48_GPIO, |
49 | GPIO49_GPIO, |
50 | GPIO51_GPIO, |
51 | GPIO74_GPIO, |
52 | GPIO75_GPIO, |
53 | GPIO76_GPIO, |
54 | GPIO77_GPIO, |
55 | |
56 | /* this is the 16-bit hdb bus 58-73 */ |
57 | GPIO58_GPIO, |
58 | GPIO59_GPIO, |
59 | GPIO60_GPIO, |
60 | GPIO61_GPIO, |
61 | |
62 | GPIO62_GPIO, |
63 | GPIO63_GPIO, |
64 | GPIO64_GPIO, |
65 | GPIO65_GPIO, |
66 | |
67 | GPIO66_GPIO, |
68 | GPIO67_GPIO, |
69 | GPIO68_GPIO, |
70 | GPIO69_GPIO, |
71 | |
72 | GPIO70_GPIO, |
73 | GPIO71_GPIO, |
74 | GPIO72_GPIO, |
75 | GPIO73_GPIO, |
76 | }; |
77 | |
78 | /* register offsets for gpio control */ |
79 | #define PWR_GPIO_PIN 16 |
80 | #define CFG_GPIO_PIN 17 |
81 | #define RDY_GPIO_PIN 32 |
82 | #define DC_GPIO_PIN 48 |
83 | #define RST_GPIO_PIN 49 |
84 | #define LED_GPIO_PIN 51 |
85 | #define RD_GPIO_PIN 74 |
86 | #define WR_GPIO_PIN 75 |
87 | #define CS_GPIO_PIN 76 |
88 | #define IRQ_GPIO_PIN 77 |
89 | |
90 | /* hdb bus */ |
91 | #define DB0_GPIO_PIN 58 |
92 | #define DB15_GPIO_PIN 73 |
93 | |
94 | static int gpios[] = { PWR_GPIO_PIN, CFG_GPIO_PIN, RDY_GPIO_PIN, DC_GPIO_PIN, |
95 | RST_GPIO_PIN, RD_GPIO_PIN, WR_GPIO_PIN, CS_GPIO_PIN, |
96 | IRQ_GPIO_PIN, LED_GPIO_PIN }; |
97 | static char *gpio_names[] = { "PWR" , "CFG" , "RDY" , "DC" , "RST" , "RD" , "WR" , |
98 | "CS" , "IRQ" , "LED" }; |
99 | |
100 | static int am300_wait_event(struct broadsheetfb_par *par) |
101 | { |
102 | /* todo: improve err recovery */ |
103 | wait_event(par->waitq, gpio_get_value(RDY_GPIO_PIN)); |
104 | return 0; |
105 | } |
106 | |
107 | static int am300_init_gpio_regs(struct broadsheetfb_par *par) |
108 | { |
109 | int i; |
110 | int err; |
111 | char dbname[8]; |
112 | |
113 | for (i = 0; i < ARRAY_SIZE(gpios); i++) { |
114 | err = gpio_request(gpio: gpios[i], label: gpio_names[i]); |
115 | if (err) { |
116 | dev_err(&am300_device->dev, "failed requesting " |
117 | "gpio %s, err=%d\n" , gpio_names[i], err); |
118 | goto err_req_gpio; |
119 | } |
120 | } |
121 | |
122 | /* we also need to take care of the hdb bus */ |
123 | for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) { |
124 | sprintf(buf: dbname, fmt: "DB%d" , i); |
125 | err = gpio_request(gpio: i, label: dbname); |
126 | if (err) { |
127 | dev_err(&am300_device->dev, "failed requesting " |
128 | "gpio %d, err=%d\n" , i, err); |
129 | goto err_req_gpio2; |
130 | } |
131 | } |
132 | |
133 | /* setup the outputs and init values */ |
134 | gpio_direction_output(PWR_GPIO_PIN, value: 0); |
135 | gpio_direction_output(CFG_GPIO_PIN, value: 1); |
136 | gpio_direction_output(DC_GPIO_PIN, value: 0); |
137 | gpio_direction_output(RD_GPIO_PIN, value: 1); |
138 | gpio_direction_output(WR_GPIO_PIN, value: 1); |
139 | gpio_direction_output(CS_GPIO_PIN, value: 1); |
140 | gpio_direction_output(RST_GPIO_PIN, value: 0); |
141 | |
142 | /* setup the inputs */ |
143 | gpio_direction_input(RDY_GPIO_PIN); |
144 | gpio_direction_input(IRQ_GPIO_PIN); |
145 | |
146 | /* start the hdb bus as an input */ |
147 | for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) |
148 | gpio_direction_output(gpio: i, value: 0); |
149 | |
150 | /* go into command mode */ |
151 | gpio_set_value(CFG_GPIO_PIN, value: 1); |
152 | gpio_set_value(RST_GPIO_PIN, value: 0); |
153 | msleep(msecs: 10); |
154 | gpio_set_value(RST_GPIO_PIN, value: 1); |
155 | msleep(msecs: 10); |
156 | am300_wait_event(par); |
157 | |
158 | return 0; |
159 | |
160 | err_req_gpio2: |
161 | while (--i >= DB0_GPIO_PIN) |
162 | gpio_free(gpio: i); |
163 | i = ARRAY_SIZE(gpios); |
164 | err_req_gpio: |
165 | while (--i >= 0) |
166 | gpio_free(gpio: gpios[i]); |
167 | |
168 | return err; |
169 | } |
170 | |
171 | static int am300_init_board(struct broadsheetfb_par *par) |
172 | { |
173 | return am300_init_gpio_regs(par); |
174 | } |
175 | |
176 | static void am300_cleanup(struct broadsheetfb_par *par) |
177 | { |
178 | int i; |
179 | |
180 | free_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), par); |
181 | |
182 | for (i = 0; i < ARRAY_SIZE(gpios); i++) |
183 | gpio_free(gpio: gpios[i]); |
184 | |
185 | for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) |
186 | gpio_free(gpio: i); |
187 | |
188 | } |
189 | |
190 | static u16 am300_get_hdb(struct broadsheetfb_par *par) |
191 | { |
192 | u16 res = 0; |
193 | int i; |
194 | |
195 | for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++) |
196 | res |= (gpio_get_value(DB0_GPIO_PIN + i)) ? (1 << i) : 0; |
197 | |
198 | return res; |
199 | } |
200 | |
201 | static void am300_set_hdb(struct broadsheetfb_par *par, u16 data) |
202 | { |
203 | int i; |
204 | |
205 | for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++) |
206 | gpio_set_value(DB0_GPIO_PIN + i, value: (data >> i) & 0x01); |
207 | } |
208 | |
209 | |
210 | static void am300_set_ctl(struct broadsheetfb_par *par, unsigned char bit, |
211 | u8 state) |
212 | { |
213 | switch (bit) { |
214 | case BS_CS: |
215 | gpio_set_value(CS_GPIO_PIN, value: state); |
216 | break; |
217 | case BS_DC: |
218 | gpio_set_value(DC_GPIO_PIN, value: state); |
219 | break; |
220 | case BS_WR: |
221 | gpio_set_value(WR_GPIO_PIN, value: state); |
222 | break; |
223 | } |
224 | } |
225 | |
226 | static int am300_get_panel_type(void) |
227 | { |
228 | return panel_type; |
229 | } |
230 | |
231 | static irqreturn_t am300_handle_irq(int irq, void *dev_id) |
232 | { |
233 | struct broadsheetfb_par *par = dev_id; |
234 | |
235 | wake_up(&par->waitq); |
236 | return IRQ_HANDLED; |
237 | } |
238 | |
239 | static int am300_setup_irq(struct fb_info *info) |
240 | { |
241 | int ret; |
242 | struct broadsheetfb_par *par = info->par; |
243 | |
244 | ret = request_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), handler: am300_handle_irq, |
245 | IRQF_TRIGGER_RISING, name: "AM300" , dev: par); |
246 | if (ret) |
247 | dev_err(&am300_device->dev, "request_irq failed: %d\n" , ret); |
248 | |
249 | return ret; |
250 | } |
251 | |
252 | static struct broadsheet_board am300_board = { |
253 | .owner = THIS_MODULE, |
254 | .init = am300_init_board, |
255 | .cleanup = am300_cleanup, |
256 | .set_hdb = am300_set_hdb, |
257 | .get_hdb = am300_get_hdb, |
258 | .set_ctl = am300_set_ctl, |
259 | .wait_for_rdy = am300_wait_event, |
260 | .get_panel_type = am300_get_panel_type, |
261 | .setup_irq = am300_setup_irq, |
262 | }; |
263 | |
264 | int __init am300_init(void) |
265 | { |
266 | int ret; |
267 | |
268 | pxa2xx_mfp_config(ARRAY_AND_SIZE(am300_pin_config)); |
269 | |
270 | /* request our platform independent driver */ |
271 | request_module("broadsheetfb" ); |
272 | |
273 | am300_device = platform_device_alloc(name: "broadsheetfb" , id: -1); |
274 | if (!am300_device) |
275 | return -ENOMEM; |
276 | |
277 | /* the am300_board that will be seen by broadsheetfb is a copy */ |
278 | platform_device_add_data(pdev: am300_device, data: &am300_board, |
279 | size: sizeof(am300_board)); |
280 | |
281 | ret = platform_device_add(pdev: am300_device); |
282 | |
283 | if (ret) { |
284 | platform_device_put(pdev: am300_device); |
285 | return ret; |
286 | } |
287 | |
288 | return 0; |
289 | } |
290 | |
291 | module_param(panel_type, uint, 0); |
292 | MODULE_PARM_DESC(panel_type, "Select the panel type: 37, 6, 97" ); |
293 | |
294 | MODULE_DESCRIPTION("board driver for am300 epd kit" ); |
295 | MODULE_AUTHOR("Jaya Kumar" ); |
296 | MODULE_LICENSE("GPL" ); |
297 | |