diff options
Diffstat (limited to 'drivers/video/am200epd.c')
-rw-r--r-- | drivers/video/am200epd.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/drivers/video/am200epd.c b/drivers/video/am200epd.c new file mode 100644 index 000000000000..51e26c1f5e8b --- /dev/null +++ b/drivers/video/am200epd.c | |||
@@ -0,0 +1,295 @@ | |||
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 <asm/arch/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), IRQT_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"); | ||