diff options
-rw-r--r-- | Documentation/fb/metronomefb.txt | 16 | ||||
-rw-r--r-- | drivers/video/Kconfig | 16 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/am200epd.c | 295 | ||||
-rw-r--r-- | drivers/video/metronomefb.c | 314 | ||||
-rw-r--r-- | include/video/metronomefb.h | 62 |
6 files changed, 422 insertions, 282 deletions
diff --git a/Documentation/fb/metronomefb.txt b/Documentation/fb/metronomefb.txt index b9a2e7b7e838..237ca412582d 100644 --- a/Documentation/fb/metronomefb.txt +++ b/Documentation/fb/metronomefb.txt | |||
@@ -1,7 +1,7 @@ | |||
1 | Metronomefb | 1 | Metronomefb |
2 | ----------- | 2 | ----------- |
3 | Maintained by Jaya Kumar <jayakumar.lkml.gmail.com> | 3 | Maintained by Jaya Kumar <jayakumar.lkml.gmail.com> |
4 | Last revised: Nov 20, 2007 | 4 | Last revised: Mar 10, 2008 |
5 | 5 | ||
6 | Metronomefb is a driver for the Metronome display controller. The controller | 6 | Metronomefb is a driver for the Metronome display controller. The controller |
7 | is from E-Ink Corporation. It is intended to be used to drive the E-Ink | 7 | is from E-Ink Corporation. It is intended to be used to drive the E-Ink |
@@ -11,20 +11,18 @@ display media here http://www.e-ink.com/products/matrix/metronome.html . | |||
11 | Metronome is interfaced to the host CPU through the AMLCD interface. The | 11 | Metronome is interfaced to the host CPU through the AMLCD interface. The |
12 | host CPU generates the control information and the image in a framebuffer | 12 | host CPU generates the control information and the image in a framebuffer |
13 | which is then delivered to the AMLCD interface by a host specific method. | 13 | which is then delivered to the AMLCD interface by a host specific method. |
14 | Currently, that's implemented for the PXA's LCDC controller. The display and | 14 | The display and error status are each pulled through individual GPIOs. |
15 | error status are each pulled through individual GPIOs. | ||
16 | 15 | ||
17 | Metronomefb was written for the PXA255/gumstix/lyre combination and | 16 | Metronomefb is platform independent and depends on a board specific driver |
18 | therefore currently has board set specific code in it. If other boards based on | 17 | to do all physical IO work. Currently, an example is implemented for the |
19 | other architectures are available, then the host specific code can be separated | 18 | PXA board used in the AM-200 EPD devkit. This example is am200epd.c |
20 | and abstracted out. | ||
21 | 19 | ||
22 | Metronomefb requires waveform information which is delivered via the AMLCD | 20 | Metronomefb requires waveform information which is delivered via the AMLCD |
23 | interface to the metronome controller. The waveform information is expected to | 21 | interface to the metronome controller. The waveform information is expected to |
24 | be delivered from userspace via the firmware class interface. The waveform file | 22 | be delivered from userspace via the firmware class interface. The waveform file |
25 | can be compressed as long as your udev or hotplug script is aware of the need | 23 | can be compressed as long as your udev or hotplug script is aware of the need |
26 | to uncompress it before delivering it. metronomefb will ask for waveform.wbf | 24 | to uncompress it before delivering it. metronomefb will ask for metronome.wbf |
27 | which would typically go into /lib/firmware/waveform.wbf depending on your | 25 | which would typically go into /lib/firmware/metronome.wbf depending on your |
28 | udev/hotplug setup. I have only tested with a single waveform file which was | 26 | udev/hotplug setup. I have only tested with a single waveform file which was |
29 | originally labeled 23P01201_60_WT0107_MTC. I do not know what it stands for. | 27 | originally labeled 23P01201_60_WT0107_MTC. I do not know what it stands for. |
30 | Caution should be exercised when manipulating the waveform as there may be | 28 | Caution should be exercised when manipulating the waveform as there may be |
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8da4683c5d20..ff6ea9a9be22 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -173,6 +173,11 @@ config FB_DEFERRED_IO | |||
173 | depends on FB | 173 | depends on FB |
174 | default y | 174 | default y |
175 | 175 | ||
176 | config FB_METRONOME | ||
177 | tristate | ||
178 | depends on FB | ||
179 | depends on FB_DEFERRED_IO | ||
180 | |||
176 | config FB_SVGALIB | 181 | config FB_SVGALIB |
177 | tristate | 182 | tristate |
178 | depends on FB | 183 | depends on FB |
@@ -1927,19 +1932,18 @@ config FB_XILINX | |||
1927 | framebuffer. ML300 carries a 640*480 LCD display on the board, | 1932 | framebuffer. ML300 carries a 640*480 LCD display on the board, |
1928 | ML403 uses a standard DB15 VGA connector. | 1933 | ML403 uses a standard DB15 VGA connector. |
1929 | 1934 | ||
1930 | config FB_METRONOME | 1935 | config FB_AM200EPD |
1931 | tristate "Metronome display controller support" | 1936 | tristate "AM-200 E-Ink EPD devkit support" |
1932 | depends on FB && ARCH_PXA && MMU | 1937 | depends on FB && ARCH_PXA && MMU |
1933 | select FB_SYS_FILLRECT | 1938 | select FB_SYS_FILLRECT |
1934 | select FB_SYS_COPYAREA | 1939 | select FB_SYS_COPYAREA |
1935 | select FB_SYS_IMAGEBLIT | 1940 | select FB_SYS_IMAGEBLIT |
1936 | select FB_SYS_FOPS | 1941 | select FB_SYS_FOPS |
1937 | select FB_DEFERRED_IO | 1942 | select FB_DEFERRED_IO |
1943 | select FB_METRONOME | ||
1938 | help | 1944 | help |
1939 | This enables support for the Metronome display controller. Tested | 1945 | This enables support for the Metronome display controller used on |
1940 | with an E-Ink 800x600 display and Gumstix Connex through an AMLCD | 1946 | the E-Ink AM-200 EPD devkit. |
1941 | interface. Please read <file:Documentation/fb/metronomefb.txt> | ||
1942 | for more information. | ||
1943 | 1947 | ||
1944 | config FB_VIRTUAL | 1948 | config FB_VIRTUAL |
1945 | tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" | 1949 | tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 80d58425c641..04bca35403ff 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -29,6 +29,7 @@ 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 | ||
32 | obj-$(CONFIG_FB_ARC) += arcfb.o | 33 | obj-$(CONFIG_FB_ARC) += arcfb.o |
33 | obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o | 34 | obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o |
34 | obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o | 35 | obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o |
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"); | ||
diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index 5602f3e3f919..17066dd5a190 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c | |||
@@ -13,12 +13,10 @@ | |||
13 | * Corporation. http://support.eink.com/community | 13 | * Corporation. http://support.eink.com/community |
14 | * | 14 | * |
15 | * This driver is written to be used with the Metronome display controller. | 15 | * This driver is written to be used with the Metronome display controller. |
16 | * It was tested with an E-Ink 800x600 Vizplex EPD on a Gumstix Connex board | 16 | * It is intended to be architecture independent. A board specific driver |
17 | * using the Lyre interface board. | 17 | * must be used to perform all the physical IO interactions. An example |
18 | * is provided as am200epd.c | ||
18 | * | 19 | * |
19 | * General notes: | ||
20 | * - User must set metronomefb_enable=1 to enable it. | ||
21 | * - See Documentation/fb/metronomefb.txt for how metronome works. | ||
22 | */ | 20 | */ |
23 | #include <linux/module.h> | 21 | #include <linux/module.h> |
24 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
@@ -38,9 +36,11 @@ | |||
38 | #include <linux/uaccess.h> | 36 | #include <linux/uaccess.h> |
39 | #include <linux/irq.h> | 37 | #include <linux/irq.h> |
40 | 38 | ||
41 | #include <asm/arch/pxa-regs.h> | 39 | #include <video/metronomefb.h> |
40 | |||
42 | #include <asm/unaligned.h> | 41 | #include <asm/unaligned.h> |
43 | 42 | ||
43 | |||
44 | #define DEBUG 1 | 44 | #define DEBUG 1 |
45 | #ifdef DEBUG | 45 | #ifdef DEBUG |
46 | #define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) | 46 | #define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) |
@@ -53,35 +53,6 @@ | |||
53 | #define DPY_W 832 | 53 | #define DPY_W 832 |
54 | #define DPY_H 622 | 54 | #define DPY_H 622 |
55 | 55 | ||
56 | struct metromem_desc { | ||
57 | u32 mFDADR0; | ||
58 | u32 mFSADR0; | ||
59 | u32 mFIDR0; | ||
60 | u32 mLDCMD0; | ||
61 | }; | ||
62 | |||
63 | struct metromem_cmd { | ||
64 | u16 opcode; | ||
65 | u16 args[((64-2)/2)]; | ||
66 | u16 csum; | ||
67 | }; | ||
68 | |||
69 | struct metronomefb_par { | ||
70 | unsigned char *metromem; | ||
71 | struct metromem_desc *metromem_desc; | ||
72 | struct metromem_cmd *metromem_cmd; | ||
73 | unsigned char *metromem_wfm; | ||
74 | unsigned char *metromem_img; | ||
75 | u16 *metromem_img_csum; | ||
76 | u16 *csum_table; | ||
77 | int metromemsize; | ||
78 | dma_addr_t metromem_dma; | ||
79 | dma_addr_t metromem_desc_dma; | ||
80 | struct fb_info *info; | ||
81 | wait_queue_head_t waitq; | ||
82 | u8 frame_count; | ||
83 | }; | ||
84 | |||
85 | /* frame differs from image. frame includes non-visible pixels */ | 56 | /* frame differs from image. frame includes non-visible pixels */ |
86 | struct epd_frame { | 57 | struct epd_frame { |
87 | int fw; /* frame width */ | 58 | int fw; /* frame width */ |
@@ -120,8 +91,7 @@ static struct fb_var_screeninfo metronomefb_var __devinitdata = { | |||
120 | .transp = { 0, 0, 0 }, | 91 | .transp = { 0, 0, 0 }, |
121 | }; | 92 | }; |
122 | 93 | ||
123 | static unsigned int metronomefb_enable; | 94 | /* the waveform structure that is coming from userspace firmware */ |
124 | |||
125 | struct waveform_hdr { | 95 | struct waveform_hdr { |
126 | u8 stuff[32]; | 96 | u8 stuff[32]; |
127 | 97 | ||
@@ -301,165 +271,6 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, | |||
301 | return 0; | 271 | return 0; |
302 | } | 272 | } |
303 | 273 | ||
304 | /* register offsets for gpio control */ | ||
305 | #define LED_GPIO_PIN 51 | ||
306 | #define STDBY_GPIO_PIN 48 | ||
307 | #define RST_GPIO_PIN 49 | ||
308 | #define RDY_GPIO_PIN 32 | ||
309 | #define ERR_GPIO_PIN 17 | ||
310 | #define PCBPWR_GPIO_PIN 16 | ||
311 | |||
312 | #define AF_SEL_GPIO_N 0x3 | ||
313 | #define GAFR0_U_OFFSET(pin) ((pin - 16) * 2) | ||
314 | #define GAFR1_L_OFFSET(pin) ((pin - 32) * 2) | ||
315 | #define GAFR1_U_OFFSET(pin) ((pin - 48) * 2) | ||
316 | #define GPDR1_OFFSET(pin) (pin - 32) | ||
317 | #define GPCR1_OFFSET(pin) (pin - 32) | ||
318 | #define GPSR1_OFFSET(pin) (pin - 32) | ||
319 | #define GPCR0_OFFSET(pin) (pin) | ||
320 | #define GPSR0_OFFSET(pin) (pin) | ||
321 | |||
322 | static void metronome_set_gpio_output(int pin, int val) | ||
323 | { | ||
324 | u8 index; | ||
325 | |||
326 | index = pin >> 4; | ||
327 | |||
328 | switch (index) { | ||
329 | case 1: | ||
330 | if (val) | ||
331 | GPSR0 |= (1 << GPSR0_OFFSET(pin)); | ||
332 | else | ||
333 | GPCR0 |= (1 << GPCR0_OFFSET(pin)); | ||
334 | break; | ||
335 | case 2: | ||
336 | break; | ||
337 | case 3: | ||
338 | if (val) | ||
339 | GPSR1 |= (1 << GPSR1_OFFSET(pin)); | ||
340 | else | ||
341 | GPCR1 |= (1 << GPCR1_OFFSET(pin)); | ||
342 | break; | ||
343 | default: | ||
344 | printk(KERN_ERR "unimplemented\n"); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | static void __devinit metronome_init_gpio_pin(int pin, int dir) | ||
349 | { | ||
350 | u8 index; | ||
351 | /* dir 0 is output, 1 is input | ||
352 | - do 2 things here: | ||
353 | - set gpio alternate function to standard gpio | ||
354 | - set gpio direction to input or output */ | ||
355 | |||
356 | index = pin >> 4; | ||
357 | switch (index) { | ||
358 | case 1: | ||
359 | GAFR0_U &= ~(AF_SEL_GPIO_N << GAFR0_U_OFFSET(pin)); | ||
360 | |||
361 | if (dir) | ||
362 | GPDR0 &= ~(1 << pin); | ||
363 | else | ||
364 | GPDR0 |= (1 << pin); | ||
365 | break; | ||
366 | case 2: | ||
367 | GAFR1_L &= ~(AF_SEL_GPIO_N << GAFR1_L_OFFSET(pin)); | ||
368 | |||
369 | if (dir) | ||
370 | GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); | ||
371 | else | ||
372 | GPDR1 |= (1 << GPDR1_OFFSET(pin)); | ||
373 | break; | ||
374 | case 3: | ||
375 | GAFR1_U &= ~(AF_SEL_GPIO_N << GAFR1_U_OFFSET(pin)); | ||
376 | |||
377 | if (dir) | ||
378 | GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); | ||
379 | else | ||
380 | GPDR1 |= (1 << GPDR1_OFFSET(pin)); | ||
381 | break; | ||
382 | default: | ||
383 | printk(KERN_ERR "unimplemented\n"); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | static void __devinit metronome_init_gpio_regs(void) | ||
388 | { | ||
389 | metronome_init_gpio_pin(LED_GPIO_PIN, 0); | ||
390 | metronome_set_gpio_output(LED_GPIO_PIN, 0); | ||
391 | |||
392 | metronome_init_gpio_pin(STDBY_GPIO_PIN, 0); | ||
393 | metronome_set_gpio_output(STDBY_GPIO_PIN, 0); | ||
394 | |||
395 | metronome_init_gpio_pin(RST_GPIO_PIN, 0); | ||
396 | metronome_set_gpio_output(RST_GPIO_PIN, 0); | ||
397 | |||
398 | metronome_init_gpio_pin(RDY_GPIO_PIN, 1); | ||
399 | |||
400 | metronome_init_gpio_pin(ERR_GPIO_PIN, 1); | ||
401 | |||
402 | metronome_init_gpio_pin(PCBPWR_GPIO_PIN, 0); | ||
403 | metronome_set_gpio_output(PCBPWR_GPIO_PIN, 0); | ||
404 | } | ||
405 | |||
406 | static void metronome_disable_lcd_controller(struct metronomefb_par *par) | ||
407 | { | ||
408 | LCSR = 0xffffffff; /* Clear LCD Status Register */ | ||
409 | LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ | ||
410 | |||
411 | /* we reset and just wait for things to settle */ | ||
412 | msleep(200); | ||
413 | } | ||
414 | |||
415 | static void metronome_enable_lcd_controller(struct metronomefb_par *par) | ||
416 | { | ||
417 | LCSR = 0xffffffff; | ||
418 | FDADR0 = par->metromem_desc_dma; | ||
419 | LCCR0 |= LCCR0_ENB; | ||
420 | } | ||
421 | |||
422 | static void __devinit metronome_init_lcdc_regs(struct metronomefb_par *par) | ||
423 | { | ||
424 | /* here we do: | ||
425 | - disable the lcd controller | ||
426 | - setup lcd control registers | ||
427 | - setup dma descriptor | ||
428 | - reenable lcd controller | ||
429 | */ | ||
430 | |||
431 | /* disable the lcd controller */ | ||
432 | metronome_disable_lcd_controller(par); | ||
433 | |||
434 | /* setup lcd control registers */ | ||
435 | LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS | ||
436 | | LCCR0_QDM | LCCR0_BM | LCCR0_OUM; | ||
437 | |||
438 | LCCR1 = (epd_frame_table[0].fw/2 - 1) /* pixels per line */ | ||
439 | | (27 << 10) /* hsync pulse width - 1 */ | ||
440 | | (33 << 16) /* eol pixel count */ | ||
441 | | (33 << 24); /* bol pixel count */ | ||
442 | |||
443 | LCCR2 = (epd_frame_table[0].fh - 1) /* lines per panel */ | ||
444 | | (24 << 10) /* vsync pulse width - 1 */ | ||
445 | | (2 << 16) /* eof pixel count */ | ||
446 | | (0 << 24); /* bof pixel count */ | ||
447 | |||
448 | LCCR3 = 2 /* pixel clock divisor */ | ||
449 | | (24 << 8) /* AC Bias pin freq */ | ||
450 | | LCCR3_16BPP /* BPP */ | ||
451 | | LCCR3_PCP; /* PCP falling edge */ | ||
452 | |||
453 | /* setup dma descriptor */ | ||
454 | par->metromem_desc->mFDADR0 = par->metromem_desc_dma; | ||
455 | par->metromem_desc->mFSADR0 = par->metromem_dma; | ||
456 | par->metromem_desc->mFIDR0 = 0; | ||
457 | par->metromem_desc->mLDCMD0 = epd_frame_table[0].fw | ||
458 | * epd_frame_table[0].fh; | ||
459 | /* reenable lcd controller */ | ||
460 | metronome_enable_lcd_controller(par); | ||
461 | } | ||
462 | |||
463 | static int metronome_display_cmd(struct metronomefb_par *par) | 274 | static int metronome_display_cmd(struct metronomefb_par *par) |
464 | { | 275 | { |
465 | int i; | 276 | int i; |
@@ -493,8 +304,7 @@ static int metronome_display_cmd(struct metronomefb_par *par) | |||
493 | par->metromem_cmd->csum = cs; | 304 | par->metromem_cmd->csum = cs; |
494 | par->metromem_cmd->opcode = opcode; /* display cmd */ | 305 | par->metromem_cmd->opcode = opcode; /* display cmd */ |
495 | 306 | ||
496 | i = wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ); | 307 | return par->board->met_wait_event_intr(par); |
497 | return i; | ||
498 | } | 308 | } |
499 | 309 | ||
500 | static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) | 310 | static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) |
@@ -518,13 +328,12 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) | |||
518 | par->metromem_cmd->csum = cs; | 328 | par->metromem_cmd->csum = cs; |
519 | 329 | ||
520 | msleep(1); | 330 | msleep(1); |
521 | metronome_set_gpio_output(RST_GPIO_PIN, 1); | 331 | par->board->set_rst(par, 1); |
522 | 332 | ||
523 | msleep(1); | 333 | msleep(1); |
524 | metronome_set_gpio_output(STDBY_GPIO_PIN, 1); | 334 | par->board->set_stdby(par, 1); |
525 | 335 | ||
526 | i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | 336 | return par->board->met_wait_event(par); |
527 | return i; | ||
528 | } | 337 | } |
529 | 338 | ||
530 | static int __devinit metronome_config_cmd(struct metronomefb_par *par) | 339 | static int __devinit metronome_config_cmd(struct metronomefb_par *par) |
@@ -569,8 +378,7 @@ static int __devinit metronome_config_cmd(struct metronomefb_par *par) | |||
569 | par->metromem_cmd->csum = cs; | 378 | par->metromem_cmd->csum = cs; |
570 | par->metromem_cmd->opcode = 0xCC10; /* config cmd */ | 379 | par->metromem_cmd->opcode = 0xCC10; /* config cmd */ |
571 | 380 | ||
572 | i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | 381 | return par->board->met_wait_event(par); |
573 | return i; | ||
574 | } | 382 | } |
575 | 383 | ||
576 | static int __devinit metronome_init_cmd(struct metronomefb_par *par) | 384 | static int __devinit metronome_init_cmd(struct metronomefb_par *par) |
@@ -596,16 +404,19 @@ static int __devinit metronome_init_cmd(struct metronomefb_par *par) | |||
596 | par->metromem_cmd->csum = cs; | 404 | par->metromem_cmd->csum = cs; |
597 | par->metromem_cmd->opcode = 0xCC20; /* init cmd */ | 405 | par->metromem_cmd->opcode = 0xCC20; /* init cmd */ |
598 | 406 | ||
599 | i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); | 407 | return par->board->met_wait_event(par); |
600 | return i; | ||
601 | } | 408 | } |
602 | 409 | ||
603 | static int __devinit metronome_init_regs(struct metronomefb_par *par) | 410 | static int __devinit metronome_init_regs(struct metronomefb_par *par) |
604 | { | 411 | { |
605 | int res; | 412 | int res; |
606 | 413 | ||
607 | metronome_init_gpio_regs(); | 414 | par->board->init_gpio_regs(par); |
608 | metronome_init_lcdc_regs(par); | 415 | |
416 | par->board->init_lcdc_regs(par); | ||
417 | |||
418 | /* now that lcd is setup, setup dma descriptor */ | ||
419 | par->board->post_dma_setup(par); | ||
609 | 420 | ||
610 | res = metronome_powerup_cmd(par); | 421 | res = metronome_powerup_cmd(par); |
611 | if (res) | 422 | if (res) |
@@ -616,8 +427,6 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) | |||
616 | return res; | 427 | return res; |
617 | 428 | ||
618 | res = metronome_init_cmd(par); | 429 | res = metronome_init_cmd(par); |
619 | if (res) | ||
620 | return res; | ||
621 | 430 | ||
622 | return res; | 431 | return res; |
623 | } | 432 | } |
@@ -632,7 +441,7 @@ static void metronomefb_dpy_update(struct metronomefb_par *par) | |||
632 | 441 | ||
633 | cksum = calc_img_cksum((u16 *) par->metromem_img, | 442 | cksum = calc_img_cksum((u16 *) par->metromem_img, |
634 | (epd_frame_table[0].fw * DPY_H)/2); | 443 | (epd_frame_table[0].fw * DPY_H)/2); |
635 | *((u16 *) (par->metromem_img) + | 444 | *((u16 *)(par->metromem_img) + |
636 | (epd_frame_table[0].fw * DPY_H)/2) = cksum; | 445 | (epd_frame_table[0].fw * DPY_H)/2) = cksum; |
637 | metronome_display_cmd(par); | 446 | metronome_display_cmd(par); |
638 | } | 447 | } |
@@ -641,8 +450,8 @@ static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index) | |||
641 | { | 450 | { |
642 | int i; | 451 | int i; |
643 | u16 csum = 0; | 452 | u16 csum = 0; |
644 | u16 *buf = (u16 __force *) (par->info->screen_base + index); | 453 | u16 *buf = (u16 __force *)(par->info->screen_base + index); |
645 | u16 *img = (u16 *) (par->metromem_img + index); | 454 | u16 *img = (u16 *)(par->metromem_img + index); |
646 | 455 | ||
647 | /* swizzle from vm to metromem and recalc cksum at the same time*/ | 456 | /* swizzle from vm to metromem and recalc cksum at the same time*/ |
648 | for (i = 0; i < PAGE_SIZE/2; i++) { | 457 | for (i = 0; i < PAGE_SIZE/2; i++) { |
@@ -733,7 +542,7 @@ static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf, | |||
733 | count = total_size - p; | 542 | count = total_size - p; |
734 | } | 543 | } |
735 | 544 | ||
736 | dst = (void __force *) (info->screen_base + p); | 545 | dst = (void __force *)(info->screen_base + p); |
737 | 546 | ||
738 | if (copy_from_user(dst, buf, count)) | 547 | if (copy_from_user(dst, buf, count)) |
739 | err = -EFAULT; | 548 | err = -EFAULT; |
@@ -759,18 +568,10 @@ static struct fb_deferred_io metronomefb_defio = { | |||
759 | .deferred_io = metronomefb_dpy_deferred_io, | 568 | .deferred_io = metronomefb_dpy_deferred_io, |
760 | }; | 569 | }; |
761 | 570 | ||
762 | static irqreturn_t metronome_handle_irq(int irq, void *dev_id) | ||
763 | { | ||
764 | struct fb_info *info = dev_id; | ||
765 | struct metronomefb_par *par = info->par; | ||
766 | |||
767 | wake_up_interruptible(&par->waitq); | ||
768 | return IRQ_HANDLED; | ||
769 | } | ||
770 | |||
771 | static int __devinit metronomefb_probe(struct platform_device *dev) | 571 | static int __devinit metronomefb_probe(struct platform_device *dev) |
772 | { | 572 | { |
773 | struct fb_info *info; | 573 | struct fb_info *info; |
574 | struct metronome_board *board; | ||
774 | int retval = -ENOMEM; | 575 | int retval = -ENOMEM; |
775 | int videomemorysize; | 576 | int videomemorysize; |
776 | unsigned char *videomemory; | 577 | unsigned char *videomemory; |
@@ -779,17 +580,26 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
779 | int cmd_size, wfm_size, img_size, padding_size, totalsize; | 580 | int cmd_size, wfm_size, img_size, padding_size, totalsize; |
780 | int i; | 581 | int i; |
781 | 582 | ||
583 | /* pick up board specific routines */ | ||
584 | board = dev->dev.platform_data; | ||
585 | if (!board) | ||
586 | return -EINVAL; | ||
587 | |||
588 | /* try to count device specific driver, if can't, platform recalls */ | ||
589 | if (!try_module_get(board->owner)) | ||
590 | return -ENODEV; | ||
591 | |||
782 | /* we have two blocks of memory. | 592 | /* we have two blocks of memory. |
783 | info->screen_base which is vm, and is the fb used by apps. | 593 | info->screen_base which is vm, and is the fb used by apps. |
784 | par->metromem which is physically contiguous memory and | 594 | par->metromem which is physically contiguous memory and |
785 | contains the display controller commands, waveform, | 595 | contains the display controller commands, waveform, |
786 | processed image data and padding. this is the data pulled | 596 | processed image data and padding. this is the data pulled |
787 | by the pxa255's LCD controller and pushed to Metronome */ | 597 | by the device's LCD controller and pushed to Metronome */ |
788 | 598 | ||
789 | videomemorysize = (DPY_W*DPY_H); | 599 | videomemorysize = (DPY_W*DPY_H); |
790 | videomemory = vmalloc(videomemorysize); | 600 | videomemory = vmalloc(videomemorysize); |
791 | if (!videomemory) | 601 | if (!videomemory) |
792 | return retval; | 602 | return -ENOMEM; |
793 | 603 | ||
794 | memset(videomemory, 0, videomemorysize); | 604 | memset(videomemory, 0, videomemorysize); |
795 | 605 | ||
@@ -797,7 +607,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
797 | if (!info) | 607 | if (!info) |
798 | goto err_vfree; | 608 | goto err_vfree; |
799 | 609 | ||
800 | info->screen_base = (char __iomem *) videomemory; | 610 | info->screen_base = (char __force __iomem *)videomemory; |
801 | info->fbops = &metronomefb_ops; | 611 | info->fbops = &metronomefb_ops; |
802 | 612 | ||
803 | info->var = metronomefb_var; | 613 | info->var = metronomefb_var; |
@@ -805,6 +615,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
805 | info->fix.smem_len = videomemorysize; | 615 | info->fix.smem_len = videomemorysize; |
806 | par = info->par; | 616 | par = info->par; |
807 | par->info = info; | 617 | par->info = info; |
618 | par->board = board; | ||
808 | init_waitqueue_head(&par->waitq); | 619 | init_waitqueue_head(&par->waitq); |
809 | 620 | ||
810 | /* this table caches per page csum values. */ | 621 | /* this table caches per page csum values. */ |
@@ -849,11 +660,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
849 | par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size | 660 | par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size |
850 | + img_size + padding_size; | 661 | + img_size + padding_size; |
851 | 662 | ||
852 | /* load the waveform in. assume mode 3, temp 31 for now */ | 663 | /* load the waveform in. assume mode 3, temp 31 for now |
853 | /* a) request the waveform file from userspace | 664 | a) request the waveform file from userspace |
854 | b) process waveform and decode into metromem */ | 665 | b) process waveform and decode into metromem */ |
855 | 666 | retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); | |
856 | retval = request_firmware(&fw_entry, "waveform.wbf", &dev->dev); | ||
857 | if (retval < 0) { | 667 | if (retval < 0) { |
858 | printk(KERN_ERR "metronomefb: couldn't get waveform\n"); | 668 | printk(KERN_ERR "metronomefb: couldn't get waveform\n"); |
859 | goto err_dma_free; | 669 | goto err_dma_free; |
@@ -867,13 +677,8 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
867 | } | 677 | } |
868 | release_firmware(fw_entry); | 678 | release_firmware(fw_entry); |
869 | 679 | ||
870 | retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), metronome_handle_irq, | 680 | if (board->setup_irq(info)) |
871 | IRQF_DISABLED, "Metronome", info); | ||
872 | if (retval) { | ||
873 | dev_err(&dev->dev, "request_irq failed: %d\n", retval); | ||
874 | goto err_ld_wfm; | 681 | goto err_ld_wfm; |
875 | } | ||
876 | set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQT_FALLING); | ||
877 | 682 | ||
878 | retval = metronome_init_regs(par); | 683 | retval = metronome_init_regs(par); |
879 | if (retval < 0) | 684 | if (retval < 0) |
@@ -913,7 +718,7 @@ err_cmap: | |||
913 | err_fb_rel: | 718 | err_fb_rel: |
914 | framebuffer_release(info); | 719 | framebuffer_release(info); |
915 | err_free_irq: | 720 | err_free_irq: |
916 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); | 721 | board->free_irq(info); |
917 | err_ld_wfm: | 722 | err_ld_wfm: |
918 | release_firmware(fw_entry); | 723 | release_firmware(fw_entry); |
919 | err_dma_free: | 724 | err_dma_free: |
@@ -923,6 +728,7 @@ err_csum_table: | |||
923 | vfree(par->csum_table); | 728 | vfree(par->csum_table); |
924 | err_vfree: | 729 | err_vfree: |
925 | vfree(videomemory); | 730 | vfree(videomemory); |
731 | module_put(board->owner); | ||
926 | return retval; | 732 | return retval; |
927 | } | 733 | } |
928 | 734 | ||
@@ -939,7 +745,8 @@ static int __devexit metronomefb_remove(struct platform_device *dev) | |||
939 | vfree(par->csum_table); | 745 | vfree(par->csum_table); |
940 | unregister_framebuffer(info); | 746 | unregister_framebuffer(info); |
941 | vfree((void __force *)info->screen_base); | 747 | vfree((void __force *)info->screen_base); |
942 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); | 748 | par->board->free_irq(info); |
749 | module_put(par->board->owner); | ||
943 | framebuffer_release(info); | 750 | framebuffer_release(info); |
944 | } | 751 | } |
945 | return 0; | 752 | return 0; |
@@ -949,48 +756,21 @@ static struct platform_driver metronomefb_driver = { | |||
949 | .probe = metronomefb_probe, | 756 | .probe = metronomefb_probe, |
950 | .remove = metronomefb_remove, | 757 | .remove = metronomefb_remove, |
951 | .driver = { | 758 | .driver = { |
759 | .owner = THIS_MODULE, | ||
952 | .name = "metronomefb", | 760 | .name = "metronomefb", |
953 | }, | 761 | }, |
954 | }; | 762 | }; |
955 | 763 | ||
956 | static struct platform_device *metronomefb_device; | ||
957 | |||
958 | static int __init metronomefb_init(void) | 764 | static int __init metronomefb_init(void) |
959 | { | 765 | { |
960 | int ret; | 766 | return platform_driver_register(&metronomefb_driver); |
961 | |||
962 | if (!metronomefb_enable) { | ||
963 | printk(KERN_ERR | ||
964 | "Use metronomefb_enable to enable the device\n"); | ||
965 | return -ENXIO; | ||
966 | } | ||
967 | |||
968 | ret = platform_driver_register(&metronomefb_driver); | ||
969 | if (!ret) { | ||
970 | metronomefb_device = platform_device_alloc("metronomefb", 0); | ||
971 | if (metronomefb_device) | ||
972 | ret = platform_device_add(metronomefb_device); | ||
973 | else | ||
974 | ret = -ENOMEM; | ||
975 | |||
976 | if (ret) { | ||
977 | platform_device_put(metronomefb_device); | ||
978 | platform_driver_unregister(&metronomefb_driver); | ||
979 | } | ||
980 | } | ||
981 | return ret; | ||
982 | |||
983 | } | 767 | } |
984 | 768 | ||
985 | static void __exit metronomefb_exit(void) | 769 | static void __exit metronomefb_exit(void) |
986 | { | 770 | { |
987 | platform_device_unregister(metronomefb_device); | ||
988 | platform_driver_unregister(&metronomefb_driver); | 771 | platform_driver_unregister(&metronomefb_driver); |
989 | } | 772 | } |
990 | 773 | ||
991 | module_param(metronomefb_enable, uint, 0); | ||
992 | MODULE_PARM_DESC(metronomefb_enable, "Enable communication with Metronome"); | ||
993 | |||
994 | module_init(metronomefb_init); | 774 | module_init(metronomefb_init); |
995 | module_exit(metronomefb_exit); | 775 | module_exit(metronomefb_exit); |
996 | 776 | ||
diff --git a/include/video/metronomefb.h b/include/video/metronomefb.h new file mode 100644 index 000000000000..dab04b4fad7f --- /dev/null +++ b/include/video/metronomefb.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * metronomefb.h - definitions for the metronome framebuffer driver | ||
3 | * | ||
4 | * Copyright (C) 2008 by 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 | */ | ||
11 | |||
12 | #ifndef _LINUX_METRONOMEFB_H_ | ||
13 | #define _LINUX_METRONOMEFB_H_ | ||
14 | |||
15 | /* address and control descriptors used by metronome controller */ | ||
16 | struct metromem_desc { | ||
17 | u32 mFDADR0; | ||
18 | u32 mFSADR0; | ||
19 | u32 mFIDR0; | ||
20 | u32 mLDCMD0; | ||
21 | }; | ||
22 | |||
23 | /* command structure used by metronome controller */ | ||
24 | struct metromem_cmd { | ||
25 | u16 opcode; | ||
26 | u16 args[((64-2)/2)]; | ||
27 | u16 csum; | ||
28 | }; | ||
29 | |||
30 | /* struct used by metronome. board specific stuff comes from *board */ | ||
31 | struct metronomefb_par { | ||
32 | unsigned char *metromem; | ||
33 | struct metromem_desc *metromem_desc; | ||
34 | struct metromem_cmd *metromem_cmd; | ||
35 | unsigned char *metromem_wfm; | ||
36 | unsigned char *metromem_img; | ||
37 | u16 *metromem_img_csum; | ||
38 | u16 *csum_table; | ||
39 | int metromemsize; | ||
40 | dma_addr_t metromem_dma; | ||
41 | dma_addr_t metromem_desc_dma; | ||
42 | struct fb_info *info; | ||
43 | struct metronome_board *board; | ||
44 | wait_queue_head_t waitq; | ||
45 | u8 frame_count; | ||
46 | }; | ||
47 | |||
48 | /* board specific routines */ | ||
49 | struct metronome_board { | ||
50 | struct module *owner; | ||
51 | void (*free_irq)(struct fb_info *); | ||
52 | void (*init_gpio_regs)(struct metronomefb_par *); | ||
53 | void (*init_lcdc_regs)(struct metronomefb_par *); | ||
54 | void (*post_dma_setup)(struct metronomefb_par *); | ||
55 | void (*set_rst)(struct metronomefb_par *, int); | ||
56 | void (*set_stdby)(struct metronomefb_par *, int); | ||
57 | int (*met_wait_event)(struct metronomefb_par *); | ||
58 | int (*met_wait_event_intr)(struct metronomefb_par *); | ||
59 | int (*setup_irq)(struct fb_info *); | ||
60 | }; | ||
61 | |||
62 | #endif | ||