fbdev: platforming metronomefb and am200epd
[linux-2.6.git] / drivers / video / am200epd.c
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");