diff options
-rw-r--r-- | arch/arm/mach-pxa/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/mach-pxa/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-pxa/am200epd.c | 374 | ||||
-rw-r--r-- | drivers/video/Kconfig | 13 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/am200epd.c | 295 |
6 files changed, 378 insertions, 309 deletions
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index e8ee7ec9ff6d..2c4851a35308 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig | |||
@@ -256,6 +256,9 @@ config PCM990_DISPLAY_NONE | |||
256 | 256 | ||
257 | endchoice | 257 | endchoice |
258 | 258 | ||
259 | config MACH_AM200EPD | ||
260 | depends on MACH_GUMSTIX_F | ||
261 | bool "Enable AM200EPD board support" | ||
259 | 262 | ||
260 | config PXA_EZX | 263 | config PXA_EZX |
261 | bool "Motorola EZX Platform" | 264 | bool "Motorola EZX Platform" |
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile index 99ecbe7f8506..ccbe1b08f88e 100644 --- a/arch/arm/mach-pxa/Makefile +++ b/arch/arm/mach-pxa/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_CPU_PXA930) += pxa930.o | |||
22 | 22 | ||
23 | # Specific board support | 23 | # Specific board support |
24 | obj-$(CONFIG_ARCH_GUMSTIX) += gumstix.o | 24 | obj-$(CONFIG_ARCH_GUMSTIX) += gumstix.o |
25 | obj-$(CONFIG_MACH_AM200EPD) += am200epd.o | ||
25 | obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o | 26 | obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o |
26 | obj-$(CONFIG_MACH_LOGICPD_PXA270) += lpd270.o | 27 | obj-$(CONFIG_MACH_LOGICPD_PXA270) += lpd270.o |
27 | obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o | 28 | obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o |
diff --git a/arch/arm/mach-pxa/am200epd.c b/arch/arm/mach-pxa/am200epd.c new file mode 100644 index 000000000000..b965085a37b9 --- /dev/null +++ b/arch/arm/mach-pxa/am200epd.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * am200epd.c -- Platform device for AM200 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 work was made possible by help and equipment support from E-Ink | ||
13 | * Corporation. http://support.eink.com/community | ||
14 | * | ||
15 | * This driver is written to be used with the Metronome display controller. | ||
16 | * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600 | ||
17 | * Vizplex EPD on a Gumstix board using the Lyre interface board. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/fb.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/irq.h> | ||
31 | #include <linux/gpio.h> | ||
32 | |||
33 | #include <mach/pxafb.h> | ||
34 | |||
35 | #include <video/metronomefb.h> | ||
36 | |||
37 | static unsigned int panel_type = 6; | ||
38 | static struct platform_device *am200_device; | ||
39 | static struct metronome_board am200_board; | ||
40 | |||
41 | static struct pxafb_mode_info am200_fb_mode_9inch7 = { | ||
42 | .pixclock = 40000, | ||
43 | .xres = 1200, | ||
44 | .yres = 842, | ||
45 | .bpp = 16, | ||
46 | .hsync_len = 2, | ||
47 | .left_margin = 2, | ||
48 | .right_margin = 2, | ||
49 | .vsync_len = 1, | ||
50 | .upper_margin = 2, | ||
51 | .lower_margin = 25, | ||
52 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
53 | }; | ||
54 | |||
55 | static struct pxafb_mode_info am200_fb_mode_8inch = { | ||
56 | .pixclock = 40000, | ||
57 | .xres = 1088, | ||
58 | .yres = 791, | ||
59 | .bpp = 16, | ||
60 | .hsync_len = 28, | ||
61 | .left_margin = 8, | ||
62 | .right_margin = 30, | ||
63 | .vsync_len = 8, | ||
64 | .upper_margin = 10, | ||
65 | .lower_margin = 8, | ||
66 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
67 | }; | ||
68 | |||
69 | static struct pxafb_mode_info am200_fb_mode_6inch = { | ||
70 | .pixclock = 40189, | ||
71 | .xres = 832, | ||
72 | .yres = 622, | ||
73 | .bpp = 16, | ||
74 | .hsync_len = 28, | ||
75 | .left_margin = 34, | ||
76 | .right_margin = 34, | ||
77 | .vsync_len = 25, | ||
78 | .upper_margin = 0, | ||
79 | .lower_margin = 2, | ||
80 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
81 | }; | ||
82 | |||
83 | static struct pxafb_mach_info am200_fb_info = { | ||
84 | .modes = &am200_fb_mode_6inch, | ||
85 | .num_modes = 1, | ||
86 | .lcd_conn = LCD_TYPE_COLOR_TFT | LCD_PCLK_EDGE_FALL | | ||
87 | LCD_AC_BIAS_FREQ(24), | ||
88 | }; | ||
89 | |||
90 | /* register offsets for gpio control */ | ||
91 | #define LED_GPIO_PIN 51 | ||
92 | #define STDBY_GPIO_PIN 48 | ||
93 | #define RST_GPIO_PIN 49 | ||
94 | #define RDY_GPIO_PIN 32 | ||
95 | #define ERR_GPIO_PIN 17 | ||
96 | #define PCBPWR_GPIO_PIN 16 | ||
97 | static int gpios[] = { LED_GPIO_PIN , STDBY_GPIO_PIN , RST_GPIO_PIN, | ||
98 | RDY_GPIO_PIN, ERR_GPIO_PIN, PCBPWR_GPIO_PIN }; | ||
99 | static char *gpio_names[] = { "LED" , "STDBY" , "RST", "RDY", "ERR", "PCBPWR" }; | ||
100 | |||
101 | static int am200_init_gpio_regs(struct metronomefb_par *par) | ||
102 | { | ||
103 | int i; | ||
104 | int err; | ||
105 | |||
106 | for (i = 0; i < ARRAY_SIZE(gpios); i++) { | ||
107 | err = gpio_request(gpios[i], gpio_names[i]); | ||
108 | if (err) { | ||
109 | dev_err(&am200_device->dev, "failed requesting " | ||
110 | "gpio %s, err=%d\n", gpio_names[i], err); | ||
111 | goto err_req_gpio; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | gpio_direction_output(LED_GPIO_PIN, 0); | ||
116 | gpio_direction_output(STDBY_GPIO_PIN, 0); | ||
117 | gpio_direction_output(RST_GPIO_PIN, 0); | ||
118 | |||
119 | gpio_direction_input(RDY_GPIO_PIN); | ||
120 | gpio_direction_input(ERR_GPIO_PIN); | ||
121 | |||
122 | gpio_direction_output(PCBPWR_GPIO_PIN, 0); | ||
123 | |||
124 | return 0; | ||
125 | |||
126 | err_req_gpio: | ||
127 | while (i > 0) | ||
128 | gpio_free(gpios[i--]); | ||
129 | |||
130 | return err; | ||
131 | } | ||
132 | |||
133 | static void am200_cleanup(struct metronomefb_par *par) | ||
134 | { | ||
135 | int i; | ||
136 | |||
137 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), par); | ||
138 | |||
139 | for (i = 0; i < ARRAY_SIZE(gpios); i++) | ||
140 | gpio_free(gpios[i]); | ||
141 | } | ||
142 | |||
143 | static int am200_share_video_mem(struct fb_info *info) | ||
144 | { | ||
145 | /* rough check if this is our desired fb and not something else */ | ||
146 | if ((info->var.xres != am200_fb_info.modes->xres) | ||
147 | || (info->var.yres != am200_fb_info.modes->yres)) | ||
148 | return 0; | ||
149 | |||
150 | /* we've now been notified that we have our new fb */ | ||
151 | am200_board.metromem = info->screen_base; | ||
152 | am200_board.host_fbinfo = info; | ||
153 | |||
154 | /* try to refcount host drv since we are the consumer after this */ | ||
155 | if (!try_module_get(info->fbops->owner)) | ||
156 | return -ENODEV; | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int am200_unshare_video_mem(struct fb_info *info) | ||
162 | { | ||
163 | dev_dbg(&am200_device->dev, "ENTER %s\n", __func__); | ||
164 | |||
165 | if (info != am200_board.host_fbinfo) | ||
166 | return 0; | ||
167 | |||
168 | module_put(am200_board.host_fbinfo->fbops->owner); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static int am200_fb_notifier_callback(struct notifier_block *self, | ||
173 | unsigned long event, void *data) | ||
174 | { | ||
175 | struct fb_event *evdata = data; | ||
176 | struct fb_info *info = evdata->info; | ||
177 | |||
178 | dev_dbg(&am200_device->dev, "ENTER %s\n", __func__); | ||
179 | |||
180 | if (event == FB_EVENT_FB_REGISTERED) | ||
181 | return am200_share_video_mem(info); | ||
182 | else if (event == FB_EVENT_FB_UNREGISTERED) | ||
183 | return am200_unshare_video_mem(info); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static struct notifier_block am200_fb_notif = { | ||
189 | .notifier_call = am200_fb_notifier_callback, | ||
190 | }; | ||
191 | |||
192 | /* this gets called as part of our init. these steps must be done now so | ||
193 | * that we can use set_pxa_fb_info */ | ||
194 | static void __init am200_presetup_fb(void) | ||
195 | { | ||
196 | int fw; | ||
197 | int fh; | ||
198 | int padding_size; | ||
199 | int totalsize; | ||
200 | |||
201 | switch (panel_type) { | ||
202 | case 6: | ||
203 | am200_fb_info.modes = &am200_fb_mode_6inch; | ||
204 | break; | ||
205 | case 8: | ||
206 | am200_fb_info.modes = &am200_fb_mode_8inch; | ||
207 | break; | ||
208 | case 97: | ||
209 | am200_fb_info.modes = &am200_fb_mode_9inch7; | ||
210 | break; | ||
211 | default: | ||
212 | dev_err(&am200_device->dev, "invalid panel_type selection," | ||
213 | " setting to 6\n"); | ||
214 | am200_fb_info.modes = &am200_fb_mode_6inch; | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | /* the frame buffer is divided as follows: | ||
219 | command | CRC | padding | ||
220 | 16kb waveform data | CRC | padding | ||
221 | image data | CRC | ||
222 | */ | ||
223 | |||
224 | fw = am200_fb_info.modes->xres; | ||
225 | fh = am200_fb_info.modes->yres; | ||
226 | |||
227 | /* waveform must be 16k + 2 for checksum */ | ||
228 | am200_board.wfm_size = roundup(16*1024 + 2, fw); | ||
229 | |||
230 | padding_size = PAGE_SIZE + (4 * fw); | ||
231 | |||
232 | /* total is 1 cmd , 1 wfm, padding and image */ | ||
233 | totalsize = fw + am200_board.wfm_size + padding_size + (fw*fh); | ||
234 | |||
235 | /* save this off because we're manipulating fw after this and | ||
236 | * we'll need it when we're ready to setup the framebuffer */ | ||
237 | am200_board.fw = fw; | ||
238 | am200_board.fh = fh; | ||
239 | |||
240 | /* the reason we do this adjustment is because we want to acquire | ||
241 | * more framebuffer memory without imposing custom awareness on the | ||
242 | * underlying pxafb driver */ | ||
243 | am200_fb_info.modes->yres = DIV_ROUND_UP(totalsize, fw); | ||
244 | |||
245 | /* we divide since we told the LCD controller we're 16bpp */ | ||
246 | am200_fb_info.modes->xres /= 2; | ||
247 | |||
248 | set_pxa_fb_info(&am200_fb_info); | ||
249 | |||
250 | } | ||
251 | |||
252 | /* this gets called by metronomefb as part of its init, in our case, we | ||
253 | * have already completed initial framebuffer init in presetup_fb so we | ||
254 | * can just setup the fb access pointers */ | ||
255 | static int am200_setup_fb(struct metronomefb_par *par) | ||
256 | { | ||
257 | int fw; | ||
258 | int fh; | ||
259 | |||
260 | fw = am200_board.fw; | ||
261 | fh = am200_board.fh; | ||
262 | |||
263 | /* metromem was set up by the notifier in share_video_mem so now | ||
264 | * we can use its value to calculate the other entries */ | ||
265 | par->metromem_cmd = (struct metromem_cmd *) am200_board.metromem; | ||
266 | par->metromem_wfm = am200_board.metromem + fw; | ||
267 | par->metromem_img = par->metromem_wfm + am200_board.wfm_size; | ||
268 | par->metromem_img_csum = (u16 *) (par->metromem_img + (fw * fh)); | ||
269 | par->metromem_dma = am200_board.host_fbinfo->fix.smem_start; | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int am200_get_panel_type(void) | ||
275 | { | ||
276 | return panel_type; | ||
277 | } | ||
278 | |||
279 | static irqreturn_t am200_handle_irq(int irq, void *dev_id) | ||
280 | { | ||
281 | struct metronomefb_par *par = dev_id; | ||
282 | |||
283 | wake_up_interruptible(&par->waitq); | ||
284 | return IRQ_HANDLED; | ||
285 | } | ||
286 | |||
287 | static int am200_setup_irq(struct fb_info *info) | ||
288 | { | ||
289 | int ret; | ||
290 | |||
291 | ret = request_irq(IRQ_GPIO(RDY_GPIO_PIN), am200_handle_irq, | ||
292 | IRQF_DISABLED|IRQF_TRIGGER_FALLING, | ||
293 | "AM200", info->par); | ||
294 | if (ret) | ||
295 | dev_err(&am200_device->dev, "request_irq failed: %d\n", ret); | ||
296 | |||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | static void am200_set_rst(struct metronomefb_par *par, int state) | ||
301 | { | ||
302 | gpio_set_value(RST_GPIO_PIN, state); | ||
303 | } | ||
304 | |||
305 | static void am200_set_stdby(struct metronomefb_par *par, int state) | ||
306 | { | ||
307 | gpio_set_value(STDBY_GPIO_PIN, state); | ||
308 | } | ||
309 | |||
310 | static int am200_wait_event(struct metronomefb_par *par) | ||
311 | { | ||
312 | return wait_event_timeout(par->waitq, gpio_get_value(RDY_GPIO_PIN), HZ); | ||
313 | } | ||
314 | |||
315 | static int am200_wait_event_intr(struct metronomefb_par *par) | ||
316 | { | ||
317 | return wait_event_interruptible_timeout(par->waitq, | ||
318 | gpio_get_value(RDY_GPIO_PIN), HZ); | ||
319 | } | ||
320 | |||
321 | static struct metronome_board am200_board = { | ||
322 | .owner = THIS_MODULE, | ||
323 | .setup_irq = am200_setup_irq, | ||
324 | .setup_io = am200_init_gpio_regs, | ||
325 | .setup_fb = am200_setup_fb, | ||
326 | .set_rst = am200_set_rst, | ||
327 | .set_stdby = am200_set_stdby, | ||
328 | .met_wait_event = am200_wait_event, | ||
329 | .met_wait_event_intr = am200_wait_event_intr, | ||
330 | .get_panel_type = am200_get_panel_type, | ||
331 | .cleanup = am200_cleanup, | ||
332 | }; | ||
333 | |||
334 | static int __init am200_init(void) | ||
335 | { | ||
336 | int ret; | ||
337 | |||
338 | /* before anything else, we request notification for any fb | ||
339 | * creation events */ | ||
340 | fb_register_client(&am200_fb_notif); | ||
341 | |||
342 | /* request our platform independent driver */ | ||
343 | request_module("metronomefb"); | ||
344 | |||
345 | am200_device = platform_device_alloc("metronomefb", -1); | ||
346 | if (!am200_device) | ||
347 | return -ENOMEM; | ||
348 | |||
349 | /* the am200_board that will be seen by metronomefb is a copy */ | ||
350 | platform_device_add_data(am200_device, &am200_board, | ||
351 | sizeof(am200_board)); | ||
352 | |||
353 | /* this _add binds metronomefb to am200. metronomefb refcounts am200 */ | ||
354 | ret = platform_device_add(am200_device); | ||
355 | |||
356 | if (ret) { | ||
357 | platform_device_put(am200_device); | ||
358 | fb_unregister_client(&am200_fb_notif); | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | am200_presetup_fb(); | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | module_param(panel_type, uint, 0); | ||
368 | MODULE_PARM_DESC(panel_type, "Select the panel type: 6, 8, 97"); | ||
369 | |||
370 | module_init(am200_init); | ||
371 | |||
372 | MODULE_DESCRIPTION("board driver for am200 metronome epd kit"); | ||
373 | MODULE_AUTHOR("Jaya Kumar"); | ||
374 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6fd4a2f63cf8..d85a74c64b54 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1969,19 +1969,6 @@ config FB_XILINX | |||
1969 | framebuffer. ML300 carries a 640*480 LCD display on the board, | 1969 | framebuffer. ML300 carries a 640*480 LCD display on the board, |
1970 | ML403 uses a standard DB15 VGA connector. | 1970 | ML403 uses a standard DB15 VGA connector. |
1971 | 1971 | ||
1972 | config FB_AM200EPD | ||
1973 | tristate "AM-200 E-Ink EPD devkit support" | ||
1974 | depends on FB && ARCH_PXA && MMU | ||
1975 | select FB_SYS_FILLRECT | ||
1976 | select FB_SYS_COPYAREA | ||
1977 | select FB_SYS_IMAGEBLIT | ||
1978 | select FB_SYS_FOPS | ||
1979 | select FB_DEFERRED_IO | ||
1980 | select FB_METRONOME | ||
1981 | help | ||
1982 | This enables support for the Metronome display controller used on | ||
1983 | the E-Ink AM-200 EPD devkit. | ||
1984 | |||
1985 | config FB_COBALT | 1972 | config FB_COBALT |
1986 | tristate "Cobalt server LCD frame buffer support" | 1973 | tristate "Cobalt server LCD frame buffer support" |
1987 | depends on FB && MIPS_COBALT | 1974 | depends on FB && MIPS_COBALT |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a6b55297a7fb..ad0330bf9be3 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -29,7 +29,6 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o | |||
29 | 29 | ||
30 | # Hardware specific drivers go first | 30 | # Hardware specific drivers go first |
31 | obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o | 31 | obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o |
32 | obj-$(CONFIG_FB_AM200EPD) += am200epd.o | ||
33 | obj-$(CONFIG_FB_ARC) += arcfb.o | 32 | obj-$(CONFIG_FB_ARC) += arcfb.o |
34 | obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o | 33 | obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o |
35 | obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o | 34 | obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o |
diff --git a/drivers/video/am200epd.c b/drivers/video/am200epd.c deleted file mode 100644 index 0c35b8b0160e..000000000000 --- a/drivers/video/am200epd.c +++ /dev/null | |||
@@ -1,295 +0,0 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/am200epd.c -- Platform device for AM200 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 work was made possible by help and equipment support from E-Ink | ||
13 | * Corporation. http://support.eink.com/community | ||
14 | * | ||
15 | * This driver is written to be used with the Metronome display controller. | ||
16 | * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600 | ||
17 | * Vizplex EPD on a Gumstix board using the Lyre interface board. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/fb.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/list.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | #include <linux/irq.h> | ||
33 | |||
34 | #include <video/metronomefb.h> | ||
35 | |||
36 | #include <mach/pxa-regs.h> | ||
37 | |||
38 | /* register offsets for gpio control */ | ||
39 | #define LED_GPIO_PIN 51 | ||
40 | #define STDBY_GPIO_PIN 48 | ||
41 | #define RST_GPIO_PIN 49 | ||
42 | #define RDY_GPIO_PIN 32 | ||
43 | #define ERR_GPIO_PIN 17 | ||
44 | #define PCBPWR_GPIO_PIN 16 | ||
45 | |||
46 | #define AF_SEL_GPIO_N 0x3 | ||
47 | #define GAFR0_U_OFFSET(pin) ((pin - 16) * 2) | ||
48 | #define GAFR1_L_OFFSET(pin) ((pin - 32) * 2) | ||
49 | #define GAFR1_U_OFFSET(pin) ((pin - 48) * 2) | ||
50 | #define GPDR1_OFFSET(pin) (pin - 32) | ||
51 | #define GPCR1_OFFSET(pin) (pin - 32) | ||
52 | #define GPSR1_OFFSET(pin) (pin - 32) | ||
53 | #define GPCR0_OFFSET(pin) (pin) | ||
54 | #define GPSR0_OFFSET(pin) (pin) | ||
55 | |||
56 | static void am200_set_gpio_output(int pin, int val) | ||
57 | { | ||
58 | u8 index; | ||
59 | |||
60 | index = pin >> 4; | ||
61 | |||
62 | switch (index) { | ||
63 | case 1: | ||
64 | if (val) | ||
65 | GPSR0 |= (1 << GPSR0_OFFSET(pin)); | ||
66 | else | ||
67 | GPCR0 |= (1 << GPCR0_OFFSET(pin)); | ||
68 | break; | ||
69 | case 2: | ||
70 | break; | ||
71 | case 3: | ||
72 | if (val) | ||
73 | GPSR1 |= (1 << GPSR1_OFFSET(pin)); | ||
74 | else | ||
75 | GPCR1 |= (1 << GPCR1_OFFSET(pin)); | ||
76 | break; | ||
77 | default: | ||
78 | printk(KERN_ERR "unimplemented\n"); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void __devinit am200_init_gpio_pin(int pin, int dir) | ||
83 | { | ||
84 | u8 index; | ||
85 | /* dir 0 is output, 1 is input | ||
86 | - do 2 things here: | ||
87 | - set gpio alternate function to standard gpio | ||
88 | - set gpio direction to input or output */ | ||
89 | |||
90 | index = pin >> 4; | ||
91 | switch (index) { | ||
92 | case 1: | ||
93 | GAFR0_U &= ~(AF_SEL_GPIO_N << GAFR0_U_OFFSET(pin)); | ||
94 | |||
95 | if (dir) | ||
96 | GPDR0 &= ~(1 << pin); | ||
97 | else | ||
98 | GPDR0 |= (1 << pin); | ||
99 | break; | ||
100 | case 2: | ||
101 | GAFR1_L &= ~(AF_SEL_GPIO_N << GAFR1_L_OFFSET(pin)); | ||
102 | |||
103 | if (dir) | ||
104 | GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); | ||
105 | else | ||
106 | GPDR1 |= (1 << GPDR1_OFFSET(pin)); | ||
107 | break; | ||
108 | case 3: | ||
109 | GAFR1_U &= ~(AF_SEL_GPIO_N << GAFR1_U_OFFSET(pin)); | ||
110 | |||
111 | if (dir) | ||
112 | GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); | ||
113 | else | ||
114 | GPDR1 |= (1 << GPDR1_OFFSET(pin)); | ||
115 | break; | ||
116 | default: | ||
117 | printk(KERN_ERR "unimplemented\n"); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void am200_init_gpio_regs(struct metronomefb_par *par) | ||
122 | { | ||
123 | am200_init_gpio_pin(LED_GPIO_PIN, 0); | ||
124 | am200_set_gpio_output(LED_GPIO_PIN, 0); | ||
125 | |||
126 | am200_init_gpio_pin(STDBY_GPIO_PIN, 0); | ||
127 | am200_set_gpio_output(STDBY_GPIO_PIN, 0); | ||
128 | |||
129 | am200_init_gpio_pin(RST_GPIO_PIN, 0); | ||
130 | am200_set_gpio_output(RST_GPIO_PIN, 0); | ||
131 | |||
132 | am200_init_gpio_pin(RDY_GPIO_PIN, 1); | ||
133 | |||
134 | am200_init_gpio_pin(ERR_GPIO_PIN, 1); | ||
135 | |||
136 | am200_init_gpio_pin(PCBPWR_GPIO_PIN, 0); | ||
137 | am200_set_gpio_output(PCBPWR_GPIO_PIN, 0); | ||
138 | } | ||
139 | |||
140 | static void am200_disable_lcd_controller(struct metronomefb_par *par) | ||
141 | { | ||
142 | LCSR = 0xffffffff; /* Clear LCD Status Register */ | ||
143 | LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ | ||
144 | |||
145 | /* we reset and just wait for things to settle */ | ||
146 | msleep(200); | ||
147 | } | ||
148 | |||
149 | static void am200_enable_lcd_controller(struct metronomefb_par *par) | ||
150 | { | ||
151 | LCSR = 0xffffffff; | ||
152 | FDADR0 = par->metromem_desc_dma; | ||
153 | LCCR0 |= LCCR0_ENB; | ||
154 | } | ||
155 | |||
156 | static void am200_init_lcdc_regs(struct metronomefb_par *par) | ||
157 | { | ||
158 | /* here we do: | ||
159 | - disable the lcd controller | ||
160 | - setup lcd control registers | ||
161 | - setup dma descriptor | ||
162 | - reenable lcd controller | ||
163 | */ | ||
164 | |||
165 | /* disable the lcd controller */ | ||
166 | am200_disable_lcd_controller(par); | ||
167 | |||
168 | /* setup lcd control registers */ | ||
169 | LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS | ||
170 | | LCCR0_QDM | LCCR0_BM | LCCR0_OUM; | ||
171 | |||
172 | LCCR1 = (par->info->var.xres/2 - 1) /* pixels per line */ | ||
173 | | (27 << 10) /* hsync pulse width - 1 */ | ||
174 | | (33 << 16) /* eol pixel count */ | ||
175 | | (33 << 24); /* bol pixel count */ | ||
176 | |||
177 | LCCR2 = (par->info->var.yres - 1) /* lines per panel */ | ||
178 | | (24 << 10) /* vsync pulse width - 1 */ | ||
179 | | (2 << 16) /* eof pixel count */ | ||
180 | | (0 << 24); /* bof pixel count */ | ||
181 | |||
182 | LCCR3 = 2 /* pixel clock divisor */ | ||
183 | | (24 << 8) /* AC Bias pin freq */ | ||
184 | | LCCR3_16BPP /* BPP */ | ||
185 | | LCCR3_PCP; /* PCP falling edge */ | ||
186 | |||
187 | } | ||
188 | |||
189 | static void am200_post_dma_setup(struct metronomefb_par *par) | ||
190 | { | ||
191 | par->metromem_desc->mFDADR0 = par->metromem_desc_dma; | ||
192 | par->metromem_desc->mFSADR0 = par->metromem_dma; | ||
193 | par->metromem_desc->mFIDR0 = 0; | ||
194 | par->metromem_desc->mLDCMD0 = par->info->var.xres | ||
195 | * par->info->var.yres; | ||
196 | am200_enable_lcd_controller(par); | ||
197 | } | ||
198 | |||
199 | static void am200_free_irq(struct fb_info *info) | ||
200 | { | ||
201 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); | ||
202 | } | ||
203 | |||
204 | static irqreturn_t am200_handle_irq(int irq, void *dev_id) | ||
205 | { | ||
206 | struct fb_info *info = dev_id; | ||
207 | struct metronomefb_par *par = info->par; | ||
208 | |||
209 | wake_up_interruptible(&par->waitq); | ||
210 | return IRQ_HANDLED; | ||
211 | } | ||
212 | |||
213 | static int am200_setup_irq(struct fb_info *info) | ||
214 | { | ||
215 | int retval; | ||
216 | |||
217 | retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), am200_handle_irq, | ||
218 | IRQF_DISABLED, "AM200", info); | ||
219 | if (retval) { | ||
220 | printk(KERN_ERR "am200epd: request_irq failed: %d\n", retval); | ||
221 | return retval; | ||
222 | } | ||
223 | |||
224 | return set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQ_TYPE_EDGE_FALLING); | ||
225 | } | ||
226 | |||
227 | static void am200_set_rst(struct metronomefb_par *par, int state) | ||
228 | { | ||
229 | am200_set_gpio_output(RST_GPIO_PIN, state); | ||
230 | } | ||
231 | |||
232 | static void am200_set_stdby(struct metronomefb_par *par, int state) | ||
233 | { | ||
234 | am200_set_gpio_output(STDBY_GPIO_PIN, state); | ||
235 | } | ||
236 | |||
237 | static int am200_wait_event(struct metronomefb_par *par) | ||
238 | { | ||
239 | return wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | ||
240 | } | ||
241 | |||
242 | static int am200_wait_event_intr(struct metronomefb_par *par) | ||
243 | { | ||
244 | return wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ); | ||
245 | } | ||
246 | |||
247 | static struct metronome_board am200_board = { | ||
248 | .owner = THIS_MODULE, | ||
249 | .free_irq = am200_free_irq, | ||
250 | .setup_irq = am200_setup_irq, | ||
251 | .init_gpio_regs = am200_init_gpio_regs, | ||
252 | .init_lcdc_regs = am200_init_lcdc_regs, | ||
253 | .post_dma_setup = am200_post_dma_setup, | ||
254 | .set_rst = am200_set_rst, | ||
255 | .set_stdby = am200_set_stdby, | ||
256 | .met_wait_event = am200_wait_event, | ||
257 | .met_wait_event_intr = am200_wait_event_intr, | ||
258 | }; | ||
259 | |||
260 | static struct platform_device *am200_device; | ||
261 | |||
262 | static int __init am200_init(void) | ||
263 | { | ||
264 | int ret; | ||
265 | |||
266 | /* request our platform independent driver */ | ||
267 | request_module("metronomefb"); | ||
268 | |||
269 | am200_device = platform_device_alloc("metronomefb", -1); | ||
270 | if (!am200_device) | ||
271 | return -ENOMEM; | ||
272 | |||
273 | platform_device_add_data(am200_device, &am200_board, | ||
274 | sizeof(am200_board)); | ||
275 | |||
276 | /* this _add binds metronomefb to am200. metronomefb refcounts am200 */ | ||
277 | ret = platform_device_add(am200_device); | ||
278 | |||
279 | if (ret) | ||
280 | platform_device_put(am200_device); | ||
281 | |||
282 | return ret; | ||
283 | } | ||
284 | |||
285 | static void __exit am200_exit(void) | ||
286 | { | ||
287 | platform_device_unregister(am200_device); | ||
288 | } | ||
289 | |||
290 | module_init(am200_init); | ||
291 | module_exit(am200_exit); | ||
292 | |||
293 | MODULE_DESCRIPTION("board driver for am200 metronome epd kit"); | ||
294 | MODULE_AUTHOR("Jaya Kumar"); | ||
295 | MODULE_LICENSE("GPL"); | ||