Root/target/linux/xburst/files-2.6.32/arch/mips/jz4740/board-n516-display.c

1/*
2 * board-n516-display.c -- Platform device for N516 display
3 *
4 * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
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#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>
17#include <linux/fb.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>
23
24#include <asm/mach-jz4740/platform.h>
25#include <asm/mach-jz4740/board-n516.h>
26
27#include <video/metronomefb.h>
28#include <linux/console.h>
29
30extern struct platform_device jz_lcd_device;
31
32static struct fb_videomode n516_fb_modes[] = {
33    [0] = {
34        .name = "Metronome 800x600",
35        .refresh = 50,
36        .xres = 400,
37        .yres = 624,
38        .hsync_len = 31,
39        .vsync_len = 23,
40        .right_margin = 31,
41        .left_margin = 5,
42        .upper_margin = 1,
43        .lower_margin = 2,
44        .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
45    },
46};
47
48static struct jz4740_fb_platform_data n516_fb_pdata = {
49    .num_modes = ARRAY_SIZE(n516_fb_modes),
50    .modes = n516_fb_modes,
51    .bpp = 16,
52    .lcd_type = JZ_LCD_TYPE_GENERIC_16_BIT,
53};
54
55struct n516_board_info {
56    uint8_t *metromem;
57    size_t wfm_size;
58    struct fb_info *host_fbinfo; /* the host LCD controller's fbi */
59    unsigned int fw;
60    unsigned int fh;
61};
62
63static struct platform_device *n516_device;
64static struct n516_board_info n516_board_info;
65
66static int metronome_gpios[] = {
67    GPIO_DISPLAY_STBY,
68    GPIO_DISPLAY_RST_L,
69    GPIO_DISPLAY_RDY,
70    GPIO_DISPLAY_ERR,
71/* GPIO_DISPLAY_OFF,*/
72};
73
74static const char *metronome_gpio_names[] = {
75    "Metronome STDBY",
76    "Metronome RST",
77    "Metronome RDY",
78    "Metronome ERR",
79/* "Metronone OFF",*/
80};
81
82static int n516_enable_hostfb(bool enable)
83{
84    int ret;
85    int blank = enable ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
86
87    acquire_console_sem();
88    ret = fb_blank(n516_board_info.host_fbinfo, blank);
89    release_console_sem();
90
91    return ret;
92}
93
94static int n516_init_metronome_gpios(struct metronomefb_par *par)
95{
96    int i;
97    int ret;
98
99    for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) {
100        ret = gpio_request(metronome_gpios[i], metronome_gpio_names[i]);
101        if (ret)
102            goto err;
103    }
104
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);
110
111    return 0;
112err:
113    for (--i; i >= 0; --i)
114        gpio_free(metronome_gpios[i]);
115
116    return ret;
117}
118
119static int n516_share_video_mem(struct fb_info *info)
120{
121    int ret;
122
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))
128        return 0;
129
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;
133
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))
137        return -ENODEV;
138
139    /* this _add binds metronomefb to n516. metronomefb refcounts n516 */
140    ret = platform_device_add(n516_device);
141
142    if (ret) {
143        platform_device_put(n516_device);
144        return ret;
145    }
146
147    /* request our platform independent driver */
148    request_module("metronomefb");
149
150    return 0;
151}
152
153static int n516_unshare_video_mem(struct fb_info *info)
154{
155    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
156
157    if (info != n516_board_info.host_fbinfo)
158        return 0;
159
160    module_put(n516_board_info.host_fbinfo->fbops->owner);
161    return 0;
162}
163
164static int n516_fb_notifier_callback(struct notifier_block *self,
165                 unsigned long event, void *data)
166{
167    struct fb_event *evdata = data;
168    struct fb_info *info = evdata->info;
169
170    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
171
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);
176
177    return 0;
178}
179
180static struct notifier_block n516_fb_notif = {
181    .notifier_call = n516_fb_notifier_callback,
182};
183
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 */
186static void __init n516_presetup_fb(void)
187{
188    int padding_size;
189    int totalsize;
190
191    /* the frame buffer is divided as follows:
192    command | CRC | padding
193    16kb waveform data | CRC | padding
194    image data | CRC
195    */
196
197    n516_board_info.fw = 800;
198    n516_board_info.fh = 624;
199
200    /* waveform must be 16k + 2 for checksum */
201    n516_board_info.wfm_size = roundup(16*1024 + 2, n516_board_info.fw);
202
203    padding_size = PAGE_SIZE + (4 * n516_board_info.fw);
204
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);
208
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 */
211
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);
216
217    jz4740_framebuffer_device.dev.platform_data = &n516_fb_pdata;
218    platform_device_register(&jz4740_framebuffer_device);
219}
220
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 */
224static int n516_setup_fb(struct metronomefb_par *par)
225{
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;
233
234    return 0;
235}
236
237static int n516_get_panel_type(void)
238{
239    return 5;
240}
241
242static irqreturn_t n516_handle_irq(int irq, void *dev_id)
243{
244    struct metronomefb_par *par = dev_id;
245
246    dev_dbg(&par->pdev->dev, "Metronome IRQ! RDY=%d\n", gpio_get_value(GPIO_DISPLAY_RDY));
247    wake_up_all(&par->waitq);
248
249    return IRQ_HANDLED;
250}
251
252static void n516_power_ctl(struct metronomefb_par *par, int cmd)
253{
254    switch (cmd) {
255    case METRONOME_POWER_OFF:
256        gpio_set_value(GPIO_DISPLAY_OFF, 1);
257        n516_enable_hostfb(false);
258        break;
259    case METRONOME_POWER_ON:
260        gpio_set_value(GPIO_DISPLAY_OFF, 0);
261        n516_enable_hostfb(true);
262        break;
263    }
264}
265
266static int n516_get_rdy(struct metronomefb_par *par)
267{
268    return gpio_get_value(GPIO_DISPLAY_RDY);
269}
270
271static int n516_get_err(struct metronomefb_par *par)
272{
273    return gpio_get_value(GPIO_DISPLAY_ERR);
274}
275
276static int n516_setup_irq(struct fb_info *info)
277{
278    int ret;
279
280    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
281
282    ret = request_irq(gpio_to_irq(GPIO_DISPLAY_RDY), n516_handle_irq,
283                IRQF_TRIGGER_RISING,
284                "n516", info->par);
285    if (ret)
286        dev_err(&n516_device->dev, "request_irq failed: %d\n", ret);
287
288    return ret;
289}
290
291static void n516_set_rst(struct metronomefb_par *par, int state)
292{
293    dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
294    if (state)
295        gpio_set_value(GPIO_DISPLAY_RST_L, 1);
296    else
297        gpio_set_value(GPIO_DISPLAY_RST_L, 0);
298}
299
300static void n516_set_stdby(struct metronomefb_par *par, int state)
301{
302    dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
303    if (state)
304        gpio_set_value(GPIO_DISPLAY_STBY, 1);
305    else
306        gpio_set_value(GPIO_DISPLAY_STBY, 0);
307}
308
309static int n516_wait_event(struct metronomefb_par *par)
310{
311    unsigned long timeout = jiffies + HZ / 20;
312
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))
316        schedule();
317
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;
322}
323
324static int n516_wait_event_intr(struct metronomefb_par *par)
325{
326    unsigned long timeout = jiffies + HZ/20;
327
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))
331        schedule();
332
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;
337}
338
339static void n516_cleanup(struct metronomefb_par *par)
340{
341    int i;
342
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]);
346}
347
348static 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,
362};
363
364static int __init n516_init(void)
365{
366    int ret;
367
368    /* Keep the metronome off, until its driver is loaded */
369    ret = gpio_request(GPIO_DISPLAY_OFF, "Display off");
370    if (ret)
371        return ret;
372
373    gpio_direction_output(GPIO_DISPLAY_OFF, 1);
374
375    /* before anything else, we request notification for any fb
376     * creation events */
377    fb_register_client(&n516_fb_notif);
378
379    n516_device = platform_device_alloc("metronomefb", -1);
380    if (!n516_device)
381        return -ENOMEM;
382
383    /* the n516_board that will be seen by metronomefb is a copy */
384    platform_device_add_data(n516_device, &n516_board,
385                    sizeof(n516_board));
386
387    n516_presetup_fb();
388
389    return 0;
390}
391module_init(n516_init);
392
393MODULE_DESCRIPTION("board driver for n516 display");
394MODULE_AUTHOR("Yauhen Kharuzhy");
395MODULE_LICENSE("GPL");
396

Archive Download this file



interactive