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