2 * board-n516-display.c -- Platform device for N516 display
4 * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
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
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/errno.h>
14 #include <linux/string.h>
15 #include <linux/delay.h>
16 #include <linux/interrupt.h>
18 #include <linux/init.h>
19 #include <linux/platform_device.h>
20 #include <linux/irq.h>
21 #include <linux/gpio.h>
22 #include <linux/jz4740_fb.h>
24 #include <asm/mach-jz4740/platform.h>
25 #include <asm/mach-jz4740/board-n516.h>
27 #include <video/metronomefb.h>
28 #include <linux/console.h>
30 extern struct platform_device jz_lcd_device
;
32 static struct fb_videomode n516_fb_modes
[] = {
34 .name
= "Metronome 800x600",
44 .sync
= FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
,
48 static struct jz4740_fb_platform_data n516_fb_pdata
= {
49 .num_modes
= ARRAY_SIZE(n516_fb_modes
),
50 .modes
= n516_fb_modes
,
52 .lcd_type
= JZ_LCD_TYPE_GENERIC_16_BIT
,
55 struct n516_board_info
{
58 struct fb_info
*host_fbinfo
; /* the host LCD controller's fbi */
63 static struct platform_device
*n516_device
;
64 static struct n516_board_info n516_board_info
;
66 static int metronome_gpios
[] = {
71 /* GPIO_DISPLAY_OFF,*/
74 static const char *metronome_gpio_names
[] = {
82 static int n516_enable_hostfb(bool enable
)
85 int blank
= enable
? FB_BLANK_UNBLANK
: FB_BLANK_POWERDOWN
;
87 acquire_console_sem();
88 ret
= fb_blank(n516_board_info
.host_fbinfo
, blank
);
89 release_console_sem();
94 static int n516_init_metronome_gpios(struct metronomefb_par
*par
)
99 for (i
= 0; i
< ARRAY_SIZE(metronome_gpios
); ++i
) {
100 ret
= gpio_request(metronome_gpios
[i
], metronome_gpio_names
[i
]);
105 gpio_direction_output(GPIO_DISPLAY_OFF
, 0);
106 gpio_direction_output(GPIO_DISPLAY_RST_L
, 0);
107 gpio_direction_output(GPIO_DISPLAY_STBY
, 0);
108 gpio_direction_input(GPIO_DISPLAY_RDY
);
109 gpio_direction_input(GPIO_DISPLAY_ERR
);
113 for (--i
; i
>= 0; --i
)
114 gpio_free(metronome_gpios
[i
]);
119 static int n516_share_video_mem(struct fb_info
*info
)
123 dev_dbg(&n516_device
->dev
, "ENTER %s\n", __func__
);
124 dev_dbg(&n516_device
->dev
, "%s, info->var.xres = %u, info->var.yres = %u\n", __func__
, info
->var
.xres
, info
->var
.yres
);
125 /* rough check if this is our desired fb and not something else */
126 if ((info
->var
.xres
!= n516_fb_pdata
.modes
[0].xres
)
127 || (info
->var
.yres
!= n516_fb_pdata
.modes
[0].yres
))
130 /* we've now been notified that we have our new fb */
131 n516_board_info
.metromem
= info
->screen_base
;
132 n516_board_info
.host_fbinfo
= info
;
134 n516_enable_hostfb(false);
135 /* try to refcount host drv since we are the consumer after this */
136 if (!try_module_get(info
->fbops
->owner
))
139 /* this _add binds metronomefb to n516. metronomefb refcounts n516 */
140 ret
= platform_device_add(n516_device
);
143 platform_device_put(n516_device
);
147 /* request our platform independent driver */
148 request_module("metronomefb");
153 static int n516_unshare_video_mem(struct fb_info
*info
)
155 dev_dbg(&n516_device
->dev
, "ENTER %s\n", __func__
);
157 if (info
!= n516_board_info
.host_fbinfo
)
160 module_put(n516_board_info
.host_fbinfo
->fbops
->owner
);
164 static int n516_fb_notifier_callback(struct notifier_block
*self
,
165 unsigned long event
, void *data
)
167 struct fb_event
*evdata
= data
;
168 struct fb_info
*info
= evdata
->info
;
170 dev_dbg(&n516_device
->dev
, "ENTER %s\n", __func__
);
172 if (event
== FB_EVENT_FB_REGISTERED
)
173 return n516_share_video_mem(info
);
174 else if (event
== FB_EVENT_FB_UNREGISTERED
)
175 return n516_unshare_video_mem(info
);
180 static struct notifier_block n516_fb_notif
= {
181 .notifier_call
= n516_fb_notifier_callback
,
184 /* this gets called as part of our init. these steps must be done now so
185 * that we can use set_pxa_fb_info */
186 static void __init
n516_presetup_fb(void)
191 /* the frame buffer is divided as follows:
192 command | CRC | padding
193 16kb waveform data | CRC | padding
197 n516_board_info
.fw
= 800;
198 n516_board_info
.fh
= 624;
200 /* waveform must be 16k + 2 for checksum */
201 n516_board_info
.wfm_size
= roundup(16*1024 + 2, n516_board_info
.fw
);
203 padding_size
= PAGE_SIZE
+ (4 * n516_board_info
.fw
);
205 /* total is 1 cmd , 1 wfm, padding and image */
206 totalsize
= n516_board_info
.fw
+ n516_board_info
.wfm_size
;
207 totalsize
+= padding_size
+ (n516_board_info
.fw
*n516_board_info
.fh
);
209 /* save this off because we're manipulating fw after this and
210 * we'll need it when we're ready to setup the framebuffer */
212 /* the reason we do this adjustment is because we want to acquire
213 * more framebuffer memory without imposing custom awareness on the
214 * underlying driver */
215 n516_fb_pdata
.modes
[0].yres
= DIV_ROUND_UP(totalsize
, n516_board_info
.fw
);
217 jz4740_framebuffer_device
.dev
.platform_data
= &n516_fb_pdata
;
218 platform_device_register(&jz4740_framebuffer_device
);
221 /* this gets called by metronomefb as part of its init, in our case, we
222 * have already completed initial framebuffer init in presetup_fb so we
223 * can just setup the fb access pointers */
224 static int n516_setup_fb(struct metronomefb_par
*par
)
226 /* metromem was set up by the notifier in share_video_mem so now
227 * we can use its value to calculate the other entries */
228 par
->metromem_cmd
= (struct metromem_cmd
*) n516_board_info
.metromem
;
229 par
->metromem_wfm
= n516_board_info
.metromem
+ n516_board_info
.fw
;
230 par
->metromem_img
= par
->metromem_wfm
+ n516_board_info
.wfm_size
;
231 par
->metromem_img_csum
= (u16
*) (par
->metromem_img
+ (n516_board_info
.fw
* n516_board_info
.fh
));
232 par
->metromem_dma
= n516_board_info
.host_fbinfo
->fix
.smem_start
;
237 static int n516_get_panel_type(void)
242 static irqreturn_t
n516_handle_irq(int irq
, void *dev_id
)
244 struct metronomefb_par
*par
= dev_id
;
246 dev_dbg(&par
->pdev
->dev
, "Metronome IRQ! RDY=%d\n", gpio_get_value(GPIO_DISPLAY_RDY
));
247 wake_up_all(&par
->waitq
);
252 static void n516_power_ctl(struct metronomefb_par
*par
, int cmd
)
255 case METRONOME_POWER_OFF
:
256 gpio_set_value(GPIO_DISPLAY_OFF
, 1);
257 n516_enable_hostfb(false);
259 case METRONOME_POWER_ON
:
260 gpio_set_value(GPIO_DISPLAY_OFF
, 0);
261 n516_enable_hostfb(true);
266 static int n516_get_rdy(struct metronomefb_par
*par
)
268 return gpio_get_value(GPIO_DISPLAY_RDY
);
271 static int n516_get_err(struct metronomefb_par
*par
)
273 return gpio_get_value(GPIO_DISPLAY_ERR
);
276 static int n516_setup_irq(struct fb_info
*info
)
280 dev_dbg(&n516_device
->dev
, "ENTER %s\n", __func__
);
282 ret
= request_irq(gpio_to_irq(GPIO_DISPLAY_RDY
), n516_handle_irq
,
286 dev_err(&n516_device
->dev
, "request_irq failed: %d\n", ret
);
291 static void n516_set_rst(struct metronomefb_par
*par
, int state
)
293 dev_dbg(&n516_device
->dev
, "ENTER %s, RDY=%d\n", __func__
, gpio_get_value(GPIO_DISPLAY_RDY
));
295 gpio_set_value(GPIO_DISPLAY_RST_L
, 1);
297 gpio_set_value(GPIO_DISPLAY_RST_L
, 0);
300 static void n516_set_stdby(struct metronomefb_par
*par
, int state
)
302 dev_dbg(&n516_device
->dev
, "ENTER %s, RDY=%d\n", __func__
, gpio_get_value(GPIO_DISPLAY_RDY
));
304 gpio_set_value(GPIO_DISPLAY_STBY
, 1);
306 gpio_set_value(GPIO_DISPLAY_STBY
, 0);
309 static int n516_wait_event(struct metronomefb_par
*par
)
311 unsigned long timeout
= jiffies
+ HZ
/ 20;
313 dev_dbg(&n516_device
->dev
, "ENTER1 %s, RDY=%d\n",
314 __func__
, gpio_get_value(GPIO_DISPLAY_RDY
));
315 while (n516_get_rdy(par
) && time_before(jiffies
, timeout
))
318 dev_dbg(&n516_device
->dev
, "ENTER2 %s, RDY=%d\n",
319 __func__
, gpio_get_value(GPIO_DISPLAY_RDY
));
320 return wait_event_timeout(par
->waitq
,
321 n516_get_rdy(par
), HZ
* 2) ? 0 : -EIO
;
324 static int n516_wait_event_intr(struct metronomefb_par
*par
)
326 unsigned long timeout
= jiffies
+ HZ
/20;
328 dev_dbg(&n516_device
->dev
, "ENTER1 %s, RDY=%d\n",
329 __func__
, gpio_get_value(GPIO_DISPLAY_RDY
));
330 while (n516_get_rdy(par
) && time_before(jiffies
, timeout
))
333 dev_dbg(&n516_device
->dev
, "ENTER2 %s, RDY=%d\n",
334 __func__
, gpio_get_value(GPIO_DISPLAY_RDY
));
335 return wait_event_interruptible_timeout(par
->waitq
,
336 n516_get_rdy(par
), HZ
* 2) ? 0 : -EIO
;
339 static void n516_cleanup(struct metronomefb_par
*par
)
343 free_irq(gpio_to_irq(GPIO_DISPLAY_RDY
), par
);
344 for (i
= 0; i
< ARRAY_SIZE(metronome_gpios
); ++i
)
345 gpio_free(metronome_gpios
[i
]);
348 static struct metronome_board n516_board __initdata
= {
349 .owner
= THIS_MODULE
,
350 .power_ctl
= n516_power_ctl
,
351 .setup_irq
= n516_setup_irq
,
352 .setup_io
= n516_init_metronome_gpios
,
353 .setup_fb
= n516_setup_fb
,
354 .set_rst
= n516_set_rst
,
355 .get_err
= n516_get_err
,
356 .get_rdy
= n516_get_rdy
,
357 .set_stdby
= n516_set_stdby
,
358 .met_wait_event
= n516_wait_event
,
359 .met_wait_event_intr
= n516_wait_event_intr
,
360 .get_panel_type
= n516_get_panel_type
,
361 .cleanup
= n516_cleanup
,
364 static int __init
n516_init(void)
368 /* Keep the metronome off, until its driver is loaded */
369 ret
= gpio_request(GPIO_DISPLAY_OFF
, "Display off");
373 gpio_direction_output(GPIO_DISPLAY_OFF
, 1);
375 /* before anything else, we request notification for any fb
377 fb_register_client(&n516_fb_notif
);
379 n516_device
= platform_device_alloc("metronomefb", -1);
383 /* the n516_board that will be seen by metronomefb is a copy */
384 platform_device_add_data(n516_device
, &n516_board
,
391 module_init(n516_init
);
393 MODULE_DESCRIPTION("board driver for n516 display");
394 MODULE_AUTHOR("Yauhen Kharuzhy");
395 MODULE_LICENSE("GPL");