Date:2010-04-24 17:25:01 (13 years 7 months ago)
Author:Lars C.
Commit:3709d7a38e0790b6ec427abbdda084747238a5fe
Message:Add n516 board support

Files: arch/mips/include/asm/mach-jz4740/board-n516.h (1 diff)
arch/mips/jz4740/Kconfig (1 diff)
arch/mips/jz4740/Makefile (1 diff)
arch/mips/jz4740/board-n516-display.c (1 diff)
arch/mips/jz4740/board-n516.c (1 diff)

Change Details

arch/mips/include/asm/mach-jz4740/board-n516.h
1/*
2 * linux/include/asm-mips/mach-jz4740/board-n516.h
3 *
4 * JZ4730-based N516 board definition.
5 *
6 * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __ASM_JZ4740_N516_H__
15#define __ASM_JZ4740_N516_H__
16
17#include <asm/mach-jz4740/gpio.h>
18
19/*
20 * GPIO
21 */
22#define GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(17)
23#define GPIO_SD_CD_N JZ_GPIO_PORTD(7)
24#define GPIO_SD_WP JZ_GPIO_PORTD(15)
25#define GPIO_USB_DETECT JZ_GPIO_PORTD(19)
26#define GPIO_CHARG_STAT_N JZ_GPIO_PORTD(16)
27#define GPIO_LED_ENABLE JZ_GPIO_PORTD(28)
28#define GPIO_LPC_INT JZ_GPIO_PORTD(14)
29#define GPIO_HPHONE_DETECT JZ_GPIO_PORTD(20)
30#define GPIO_SPEAKER_ENABLE JZ_GPIO_PORTD(21)
31
32/* Display */
33#define GPIO_DISPLAY_RST_L JZ_GPIO_PORTB(18)
34#define GPIO_DISPLAY_RDY JZ_GPIO_PORTB(17)
35#define GPIO_DISPLAY_STBY JZ_GPIO_PORTC(22)
36#define GPIO_DISPLAY_ERR JZ_GPIO_PORTC(23)
37#define GPIO_DISPLAY_OFF_N JZ_GPIO_PORTD(1)
38
39#endif /* __ASM_JZ4740_N516_H__ */
arch/mips/jz4740/Kconfig
77    bool "Qi Hardware Ben NanoNote"
88    select SOC_JZ4740
99
10config JZ4740_N516
11    bool "Hanvon n516 eBook reader"
12    select SOC_JZ4740
13
1014endchoice
1115
1216config SOC_JZ4740
arch/mips/jz4740/Makefile
1212# board specific support
1313
1414obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
15obj-$(CONFIG_JZ4740_N516) += board-n516.o board-n516-display.o
1516
1617# PM support
1718
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
30static struct fb_videomode n516_fb_modes[] = {
31    [0] = {
32        .name = "Metronome 800x600",
33        .refresh = 50,
34        .xres = 400,
35        .yres = 624,
36        .hsync_len = 31,
37        .vsync_len = 23,
38        .right_margin = 31,
39        .left_margin = 5,
40        .upper_margin = 1,
41        .lower_margin = 2,
42        .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
43    },
44};
45
46static struct jz4740_fb_platform_data n516_fb_pdata = {
47    .num_modes = ARRAY_SIZE(n516_fb_modes),
48    .modes = n516_fb_modes,
49    .bpp = 16,
50    .lcd_type = JZ_LCD_TYPE_GENERIC_16_BIT,
51};
52
53struct n516_board_info {
54    uint8_t *metromem;
55    size_t wfm_size;
56    struct fb_info *host_fbinfo; /* the host LCD controller's fbi */
57    unsigned int fw;
58    unsigned int fh;
59};
60
61static struct platform_device *n516_device;
62static struct n516_board_info n516_board_info;
63
64static int metronome_gpios[] = {
65    GPIO_DISPLAY_STBY,
66    GPIO_DISPLAY_RST_L,
67    GPIO_DISPLAY_RDY,
68    GPIO_DISPLAY_ERR,
69/* GPIO_DISPLAY_OFF,*/
70};
71
72static const char *metronome_gpio_names[] = {
73    "Metronome STDBY",
74    "Metronome RST",
75    "Metronome RDY",
76    "Metronome ERR",
77/* "Metronone OFF",*/
78};
79
80static int n516_enable_hostfb(bool enable)
81{
82    int ret;
83    int blank = enable ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
84
85    acquire_console_sem();
86    ret = fb_blank(n516_board_info.host_fbinfo, blank);
87    release_console_sem();
88
89    return ret;
90}
91
92static int n516_init_metronome_gpios(struct metronomefb_par *par)
93{
94    int i;
95    int ret;
96
97    for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) {
98        ret = gpio_request(metronome_gpios[i], metronome_gpio_names[i]);
99        if (ret)
100            goto err;
101    }
102
103    gpio_direction_output(GPIO_DISPLAY_OFF, 0);
104    gpio_direction_output(GPIO_DISPLAY_RST_L, 0);
105    gpio_direction_output(GPIO_DISPLAY_STBY, 0);
106    gpio_direction_input(GPIO_DISPLAY_RDY);
107    gpio_direction_input(GPIO_DISPLAY_ERR);
108
109    return 0;
110err:
111    for (--i; i >= 0; --i)
112        gpio_free(metronome_gpios[i]);
113
114    return ret;
115}
116
117static int n516_share_video_mem(struct fb_info *info)
118{
119    int ret;
120
121    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
122    dev_dbg(&n516_device->dev, "%s, info->var.xres = %u, info->var.yres = %u\n", __func__, info->var.xres, info->var.yres);
123    /* rough check if this is our desired fb and not something else */
124    if ((info->var.xres != n516_fb_pdata.modes[0].xres)
125        || (info->var.yres != n516_fb_pdata.modes[0].yres))
126        return 0;
127
128    /* we've now been notified that we have our new fb */
129    n516_board_info.metromem = info->screen_base;
130    n516_board_info.host_fbinfo = info;
131
132    n516_enable_hostfb(false);
133    /* try to refcount host drv since we are the consumer after this */
134    if (!try_module_get(info->fbops->owner))
135        return -ENODEV;
136
137    /* this _add binds metronomefb to n516. metronomefb refcounts n516 */
138    ret = platform_device_add(n516_device);
139
140    if (ret) {
141        platform_device_put(n516_device);
142        return ret;
143    }
144
145    /* request our platform independent driver */
146    request_module("metronomefb");
147
148    return 0;
149}
150
151static int n516_unshare_video_mem(struct fb_info *info)
152{
153    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
154
155    if (info != n516_board_info.host_fbinfo)
156        return 0;
157
158    module_put(n516_board_info.host_fbinfo->fbops->owner);
159    return 0;
160}
161
162static int n516_fb_notifier_callback(struct notifier_block *self,
163                 unsigned long event, void *data)
164{
165    struct fb_event *evdata = data;
166    struct fb_info *info = evdata->info;
167
168    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
169
170    if (event == FB_EVENT_FB_REGISTERED)
171        return n516_share_video_mem(info);
172    else if (event == FB_EVENT_FB_UNREGISTERED)
173        return n516_unshare_video_mem(info);
174
175    return 0;
176}
177
178static struct notifier_block n516_fb_notif = {
179    .notifier_call = n516_fb_notifier_callback,
180};
181
182/* this gets called as part of our init. these steps must be done now so
183 * that we can use set_pxa_fb_info */
184static void __init n516_presetup_fb(void)
185{
186    int padding_size;
187    int totalsize;
188
189    /* the frame buffer is divided as follows:
190    command | CRC | padding
191    16kb waveform data | CRC | padding
192    image data | CRC
193    */
194
195    n516_board_info.fw = 800;
196    n516_board_info.fh = 624;
197
198    /* waveform must be 16k + 2 for checksum */
199    n516_board_info.wfm_size = roundup(16*1024 + 2, n516_board_info.fw);
200
201    padding_size = PAGE_SIZE + (4 * n516_board_info.fw);
202
203    /* total is 1 cmd , 1 wfm, padding and image */
204    totalsize = n516_board_info.fw + n516_board_info.wfm_size;
205    totalsize += padding_size + (n516_board_info.fw*n516_board_info.fh);
206
207    /* save this off because we're manipulating fw after this and
208     * we'll need it when we're ready to setup the framebuffer */
209
210    /* the reason we do this adjustment is because we want to acquire
211     * more framebuffer memory without imposing custom awareness on the
212     * underlying driver */
213    n516_fb_pdata.modes[0].yres = DIV_ROUND_UP(totalsize, n516_board_info.fw);
214
215    jz4740_framebuffer_device.dev.platform_data = &n516_fb_pdata;
216    platform_device_register(&jz4740_framebuffer_device);
217}
218
219/* this gets called by metronomefb as part of its init, in our case, we
220 * have already completed initial framebuffer init in presetup_fb so we
221 * can just setup the fb access pointers */
222static int n516_setup_fb(struct metronomefb_par *par)
223{
224    /* metromem was set up by the notifier in share_video_mem so now
225     * we can use its value to calculate the other entries */
226    par->metromem_cmd = (struct metromem_cmd *) n516_board_info.metromem;
227    par->metromem_wfm = n516_board_info.metromem + n516_board_info.fw;
228    par->metromem_img = par->metromem_wfm + n516_board_info.wfm_size;
229    par->metromem_img_csum = (u16 *) (par->metromem_img + (n516_board_info.fw * n516_board_info.fh));
230    par->metromem_dma = n516_board_info.host_fbinfo->fix.smem_start;
231
232    return 0;
233}
234
235static int n516_get_panel_type(void)
236{
237    return 5;
238}
239
240static irqreturn_t n516_handle_irq(int irq, void *dev_id)
241{
242    struct metronomefb_par *par = dev_id;
243
244    dev_dbg(&par->pdev->dev, "Metronome IRQ! RDY=%d\n", gpio_get_value(GPIO_DISPLAY_RDY));
245    wake_up_all(&par->waitq);
246
247    return IRQ_HANDLED;
248}
249
250static void n516_power_ctl(struct metronomefb_par *par, int cmd)
251{
252    switch (cmd) {
253    case METRONOME_POWER_OFF:
254        gpio_set_value(GPIO_DISPLAY_OFF, 1);
255        n516_enable_hostfb(false);
256        break;
257    case METRONOME_POWER_ON:
258        gpio_set_value(GPIO_DISPLAY_OFF, 0);
259        n516_enable_hostfb(true);
260        break;
261    }
262}
263
264static int n516_get_rdy(struct metronomefb_par *par)
265{
266    return gpio_get_value(GPIO_DISPLAY_RDY);
267}
268
269static int n516_get_err(struct metronomefb_par *par)
270{
271    return gpio_get_value(GPIO_DISPLAY_ERR);
272}
273
274static int n516_setup_irq(struct fb_info *info)
275{
276    int ret;
277
278    dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
279
280    ret = request_irq(gpio_to_irq(GPIO_DISPLAY_RDY), n516_handle_irq,
281                IRQF_TRIGGER_RISING,
282                "n516", info->par);
283    if (ret)
284        dev_err(&n516_device->dev, "request_irq failed: %d\n", ret);
285
286    return ret;
287}
288
289static void n516_set_rst(struct metronomefb_par *par, int state)
290{
291    dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
292    if (state)
293        gpio_set_value(GPIO_DISPLAY_RST_L, 1);
294    else
295        gpio_set_value(GPIO_DISPLAY_RST_L, 0);
296}
297
298static void n516_set_stdby(struct metronomefb_par *par, int state)
299{
300    dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
301    if (state)
302        gpio_set_value(GPIO_DISPLAY_STBY, 1);
303    else
304        gpio_set_value(GPIO_DISPLAY_STBY, 0);
305}
306
307static int n516_wait_event(struct metronomefb_par *par)
308{
309    unsigned long timeout = jiffies + HZ / 20;
310
311    dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n",
312            __func__, gpio_get_value(GPIO_DISPLAY_RDY));
313    while (n516_get_rdy(par) && time_before(jiffies, timeout))
314        schedule();
315
316    dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n",
317            __func__, gpio_get_value(GPIO_DISPLAY_RDY));
318    return wait_event_timeout(par->waitq,
319            n516_get_rdy(par), HZ * 2) ? 0 : -EIO;
320}
321
322static int n516_wait_event_intr(struct metronomefb_par *par)
323{
324    unsigned long timeout = jiffies + HZ/20;
325
326    dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n",
327            __func__, gpio_get_value(GPIO_DISPLAY_RDY));
328    while (n516_get_rdy(par) && time_before(jiffies, timeout))
329        schedule();
330
331    dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n",
332            __func__, gpio_get_value(GPIO_DISPLAY_RDY));
333    return wait_event_interruptible_timeout(par->waitq,
334                    n516_get_rdy(par), HZ * 2) ? 0 : -EIO;
335}
336
337static void n516_cleanup(struct metronomefb_par *par)
338{
339    int i;
340
341    free_irq(gpio_to_irq(GPIO_DISPLAY_RDY), par);
342    for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i)
343        gpio_free(metronome_gpios[i]);
344}
345
346static struct metronome_board n516_board __initdata = {
347    .owner = THIS_MODULE,
348    .power_ctl = n516_power_ctl,
349    .setup_irq = n516_setup_irq,
350    .setup_io = n516_init_metronome_gpios,
351    .setup_fb = n516_setup_fb,
352    .set_rst = n516_set_rst,
353    .get_err = n516_get_err,
354    .get_rdy = n516_get_rdy,
355    .set_stdby = n516_set_stdby,
356    .met_wait_event = n516_wait_event,
357    .met_wait_event_intr = n516_wait_event_intr,
358    .get_panel_type = n516_get_panel_type,
359    .cleanup = n516_cleanup,
360};
361
362static int __init n516_init(void)
363{
364    int ret;
365
366    /* Keep the metronome off, until its driver is loaded */
367    ret = gpio_request(GPIO_DISPLAY_OFF, "Display off");
368    if (ret)
369        return ret;
370
371    gpio_direction_output(GPIO_DISPLAY_OFF, 1);
372
373    /* before anything else, we request notification for any fb
374     * creation events */
375    fb_register_client(&n516_fb_notif);
376
377    n516_device = platform_device_alloc("metronomefb", -1);
378    if (!n516_device)
379        return -ENOMEM;
380
381    /* the n516_board that will be seen by metronomefb is a copy */
382    platform_device_add_data(n516_device, &n516_board,
383                    sizeof(n516_board));
384
385    n516_presetup_fb();
386
387    return 0;
388}
389module_init(n516_init);
390
391MODULE_DESCRIPTION("board driver for n516 display");
392MODULE_AUTHOR("Yauhen Kharuzhy");
393MODULE_LICENSE("GPL");
arch/mips/jz4740/board-n516.c
1/*
2 * linux/arch/mips/jz4740/board-516.c
3 *
4 * JZ4740 n516 board setup routines.
5 *
6 * Copyright (c) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/init.h>
15#include <linux/sched.h>
16#include <linux/ioport.h>
17#include <linux/mm.h>
18#include <linux/console.h>
19#include <linux/delay.h>
20#include <linux/i2c.h>
21#include <linux/platform_device.h>
22#include <linux/mtd/mtd.h>
23#include <linux/mmc/jz4740_mmc.h>
24#include <linux/mtd/jz4740_nand.h>
25#include <linux/leds.h>
26
27#include <linux/power_supply.h>
28#include <linux/power/gpio-charger.h>
29
30#include <linux/i2c.h>
31#include <linux/i2c-gpio.h>
32
33#include <asm/mach-jz4740/board-n516.h>
34#include <asm/mach-jz4740/platform.h>
35
36#include "clock.h"
37
38static long n516_panic_blink(long time)
39{
40    gpio_set_value(GPIO_LED_ENABLE, 1);
41    mdelay(200);
42    gpio_set_value(GPIO_LED_ENABLE, 0);
43    mdelay(200);
44
45    return 400;
46}
47
48static void __init board_gpio_setup(void)
49{
50/* jz_gpio_enable_pullup(JZ_GPIO_PORTD(23));
51    jz_gpio_enable_pullup(JZ_GPIO_PORTD(24));*/
52}
53
54static struct i2c_gpio_platform_data n516_i2c_pdata = {
55    .sda_pin = JZ_GPIO_PORTD(23),
56    .scl_pin = JZ_GPIO_PORTD(24),
57    .udelay = 2,
58    .timeout = 3 * HZ,
59};
60
61static struct platform_device n516_i2c_device = {
62    .name = "i2c-gpio",
63    .id = -1,
64    .dev = {
65        .platform_data = &n516_i2c_pdata,
66    },
67};
68
69static const struct i2c_board_info n516_i2c_board_info[] = {
70    {
71        .type = "LPC524",
72        .addr = 0x54,
73    },
74    {
75        .type = "lm75a",
76        .addr = 0x48,
77    }
78};
79
80static struct jz4740_mmc_platform_data n516_mmc_pdata = {
81    .gpio_card_detect = GPIO_SD_CD_N,
82    .card_detect_active_low = 1,
83    .gpio_read_only = -1,
84    .gpio_power = GPIO_SD_VCC_EN_N,
85    .power_active_low = 1,
86};
87
88static struct gpio_led n516_leds[] = {
89    {
90        .name = "n516:blue:power",
91        .gpio = GPIO_LED_ENABLE,
92        .default_state = LEDS_GPIO_DEFSTATE_ON,
93        .default_trigger = "nand-disk",
94    }
95};
96
97static struct gpio_led_platform_data n516_leds_pdata = {
98    .leds = n516_leds,
99    .num_leds = ARRAY_SIZE(n516_leds),
100};
101
102static struct platform_device n516_leds_device = {
103    .name = "leds-gpio",
104    .id = -1,
105    .dev = {
106        .platform_data = &n516_leds_pdata,
107    },
108};
109
110static struct mtd_partition n516_partitions[] = {
111    { .name = "NAND BOOT partition",
112      .offset = 0 * 0x100000,
113      .size = 4 * 0x100000,
114    },
115    { .name = "NAND KERNEL partition",
116      .offset = 4 * 0x100000,
117      .size = 4 * 0x100000,
118     },
119    { .name = "NAND ROOTFS partition",
120      .offset = 8 * 0x100000,
121      .size = 504 * 0x100000,
122     },
123};
124
125static struct nand_ecclayout n516_ecclayout = {
126    .eccbytes = 36,
127    .eccpos = {
128         6, 7, 8, 9, 10, 11, 12, 13, 14,
129        15, 16, 17, 18, 19, 20, 21, 22, 23,
130        24, 25, 26, 27, 28, 29, 30, 31, 32,
131        33, 34, 35, 36, 37, 38, 39, 40, 41,
132    },
133    .oobfree = {
134        {
135            .offset = 2,
136            .length = 4
137        },
138        {
139            .offset = 42,
140            .length = 22,
141        }
142    }
143};
144
145static struct jz_nand_platform_data n516_nand_pdata = {
146    .ecc_layout = &n516_ecclayout,
147    .partitions = n516_partitions,
148    .num_partitions = ARRAY_SIZE(n516_partitions),
149    .busy_gpio = 94,
150};
151
152static char *n516_batteries[] = {
153    "n516_battery",
154};
155
156static struct gpio_charger_platform_data n516_charger_pdata = {
157    .name = "usb",
158    .type = POWER_SUPPLY_TYPE_USB,
159    .gpio = GPIO_USB_DETECT,
160    .gpio_active_low = 1,
161    .batteries = n516_batteries,
162    .num_batteries = ARRAY_SIZE(n516_batteries),
163};
164
165static struct platform_device n516_charger_device = {
166    .name = "gpio-charger",
167    .dev = {
168        .platform_data = &n516_charger_pdata,
169    },
170};
171
172static struct platform_device *n516_devices[] __initdata = {
173    &jz4740_nand_device,
174    &n516_leds_device,
175    &jz4740_mmc_device,
176    &jz4740_i2s_device,
177    &jz4740_codec_device,
178    &jz4740_rtc_device,
179    &jz4740_usb_gdt_device,
180    &n516_i2c_device,
181    &n516_charger_device,
182};
183
184struct jz4740_clock_board_data jz4740_clock_bdata = {
185    .ext_rate = 12000000,
186    .rtc_rate = 32768,
187};
188
189extern int jz_gpiolib_init(void);
190
191static int n516_setup_platform(void)
192{
193    if (jz_gpiolib_init())
194        panic("Failed to initalize jz gpio\n");
195
196    jz4740_clock_init();
197    board_gpio_setup();
198
199    panic_blink = n516_panic_blink;
200    i2c_register_board_info(0, n516_i2c_board_info, ARRAY_SIZE(n516_i2c_board_info));
201    jz4740_mmc_device.dev.platform_data = &n516_mmc_pdata;
202    jz4740_nand_device.dev.platform_data = &n516_nand_pdata;
203
204    return platform_add_devices(n516_devices, ARRAY_SIZE(n516_devices));
205}
206arch_initcall(n516_setup_platform);

Archive Download the corresponding diff file



interactive