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");