1 From 49f148f79887cec83c4b051c104072d770a6a9af Mon Sep 17 00:00:00 2001
2 From: Lars-Peter Clausen <lars@metafoo.de>
3 Date: Sat, 24 Apr 2010 17:25:01 +0200
4 Subject: [PATCH] Add n516 board support
7 arch/mips/include/asm/mach-jz4740/board-n516.h | 39 +++
8 arch/mips/jz4740/Kconfig | 4 +
9 arch/mips/jz4740/Makefile | 1 +
10 arch/mips/jz4740/board-n516-display.c | 393 ++++++++++++++++++++++++
11 arch/mips/jz4740/board-n516.c | 206 +++++++++++++
12 5 files changed, 643 insertions(+), 0 deletions(-)
13 create mode 100644 arch/mips/include/asm/mach-jz4740/board-n516.h
14 create mode 100644 arch/mips/jz4740/board-n516-display.c
15 create mode 100644 arch/mips/jz4740/board-n516.c
17 diff --git a/arch/mips/include/asm/mach-jz4740/board-n516.h b/arch/mips/include/asm/mach-jz4740/board-n516.h
19 index 0000000..f3f1e0a
21 +++ b/arch/mips/include/asm/mach-jz4740/board-n516.h
24 + * linux/include/asm-mips/mach-jz4740/board-n516.h
26 + * JZ4730-based N516 board definition.
28 + * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
30 + * This program is free software; you can redistribute it and/or modify
31 + * it under the terms of the GNU General Public License as published by
32 + * the Free Software Foundation; either version 2 of the License, or
33 + * (at your option) any later version.
36 +#ifndef __ASM_JZ4740_N516_H__
37 +#define __ASM_JZ4740_N516_H__
39 +#include <asm/mach-jz4740/gpio.h>
44 +#define GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(17)
45 +#define GPIO_SD_CD_N JZ_GPIO_PORTD(7)
46 +#define GPIO_SD_WP JZ_GPIO_PORTD(15)
47 +#define GPIO_USB_DETECT JZ_GPIO_PORTD(19)
48 +#define GPIO_CHARG_STAT_N JZ_GPIO_PORTD(16)
49 +#define GPIO_LED_ENABLE JZ_GPIO_PORTD(28)
50 +#define GPIO_LPC_INT JZ_GPIO_PORTD(14)
51 +#define GPIO_HPHONE_DETECT JZ_GPIO_PORTD(20)
52 +#define GPIO_SPEAKER_ENABLE JZ_GPIO_PORTD(21)
55 +#define GPIO_DISPLAY_RST_L JZ_GPIO_PORTB(18)
56 +#define GPIO_DISPLAY_RDY JZ_GPIO_PORTB(17)
57 +#define GPIO_DISPLAY_STBY JZ_GPIO_PORTC(22)
58 +#define GPIO_DISPLAY_ERR JZ_GPIO_PORTC(23)
59 +#define GPIO_DISPLAY_OFF JZ_GPIO_PORTD(1)
61 +#endif /* __ASM_JZ4740_N516_H__ */
62 diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
63 index 3de3b99..b987c27 100644
64 --- a/arch/mips/jz4740/Kconfig
65 +++ b/arch/mips/jz4740/Kconfig
66 @@ -7,6 +7,10 @@ config JZ4740_QI_LB60
67 bool "Qi Hardware Ben NanoNote"
71 + bool "Hanvon n516 eBook reader"
77 diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
78 index deca88e..9ed5e5b 100644
79 --- a/arch/mips/jz4740/Makefile
80 +++ b/arch/mips/jz4740/Makefile
81 @@ -12,6 +12,7 @@ obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
82 # board specific support
84 obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
85 +obj-$(CONFIG_JZ4740_N516) += board-n516.o board-n516-display.o
89 diff --git a/arch/mips/jz4740/board-n516-display.c b/arch/mips/jz4740/board-n516-display.c
91 index 0000000..44d8237
93 +++ b/arch/mips/jz4740/board-n516-display.c
96 + * board-n516-display.c -- Platform device for N516 display
98 + * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
100 + * This file is subject to the terms and conditions of the GNU General Public
101 + * License. See the file COPYING in the main directory of this archive for
105 +#include <linux/module.h>
106 +#include <linux/kernel.h>
107 +#include <linux/errno.h>
108 +#include <linux/string.h>
109 +#include <linux/delay.h>
110 +#include <linux/interrupt.h>
111 +#include <linux/fb.h>
112 +#include <linux/init.h>
113 +#include <linux/platform_device.h>
114 +#include <linux/irq.h>
115 +#include <linux/gpio.h>
116 +#include <linux/jz4740_fb.h>
118 +#include <asm/mach-jz4740/platform.h>
119 +#include <asm/mach-jz4740/board-n516.h>
121 +#include <video/metronomefb.h>
122 +#include <linux/console.h>
124 +static struct fb_videomode n516_fb_modes[] = {
126 + .name = "Metronome 800x600",
132 + .right_margin = 31,
136 + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
140 +static struct jz4740_fb_platform_data n516_fb_pdata = {
141 + .num_modes = ARRAY_SIZE(n516_fb_modes),
142 + .modes = n516_fb_modes,
144 + .lcd_type = JZ_LCD_TYPE_GENERIC_16_BIT,
147 +struct n516_board_info {
150 + struct fb_info *host_fbinfo; /* the host LCD controller's fbi */
155 +static struct platform_device *n516_device;
156 +static struct n516_board_info n516_board_info;
158 +static int metronome_gpios[] = {
160 + GPIO_DISPLAY_RST_L,
163 +/* GPIO_DISPLAY_OFF,*/
166 +static const char *metronome_gpio_names[] = {
171 +/* "Metronone OFF",*/
174 +static int n516_enable_hostfb(bool enable)
177 + int blank = enable ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
179 + acquire_console_sem();
180 + ret = fb_blank(n516_board_info.host_fbinfo, blank);
181 + release_console_sem();
186 +static int n516_init_metronome_gpios(struct metronomefb_par *par)
191 + for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) {
192 + ret = gpio_request(metronome_gpios[i], metronome_gpio_names[i]);
197 + gpio_direction_output(GPIO_DISPLAY_OFF, 0);
198 + gpio_direction_output(GPIO_DISPLAY_RST_L, 0);
199 + gpio_direction_output(GPIO_DISPLAY_STBY, 0);
200 + gpio_direction_input(GPIO_DISPLAY_RDY);
201 + gpio_direction_input(GPIO_DISPLAY_ERR);
205 + for (--i; i >= 0; --i)
206 + gpio_free(metronome_gpios[i]);
211 +static int n516_share_video_mem(struct fb_info *info)
215 + dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
216 + dev_dbg(&n516_device->dev, "%s, info->var.xres = %u, info->var.yres = %u\n", __func__, info->var.xres, info->var.yres);
217 + /* rough check if this is our desired fb and not something else */
218 + if ((info->var.xres != n516_fb_pdata.modes[0].xres)
219 + || (info->var.yres != n516_fb_pdata.modes[0].yres))
222 + /* we've now been notified that we have our new fb */
223 + n516_board_info.metromem = info->screen_base;
224 + n516_board_info.host_fbinfo = info;
226 + n516_enable_hostfb(false);
227 + /* try to refcount host drv since we are the consumer after this */
228 + if (!try_module_get(info->fbops->owner))
231 + /* this _add binds metronomefb to n516. metronomefb refcounts n516 */
232 + ret = platform_device_add(n516_device);
235 + platform_device_put(n516_device);
239 + /* request our platform independent driver */
240 + request_module("metronomefb");
245 +static int n516_unshare_video_mem(struct fb_info *info)
247 + dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
249 + if (info != n516_board_info.host_fbinfo)
252 + module_put(n516_board_info.host_fbinfo->fbops->owner);
256 +static int n516_fb_notifier_callback(struct notifier_block *self,
257 + unsigned long event, void *data)
259 + struct fb_event *evdata = data;
260 + struct fb_info *info = evdata->info;
262 + dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
264 + if (event == FB_EVENT_FB_REGISTERED)
265 + return n516_share_video_mem(info);
266 + else if (event == FB_EVENT_FB_UNREGISTERED)
267 + return n516_unshare_video_mem(info);
272 +static struct notifier_block n516_fb_notif = {
273 + .notifier_call = n516_fb_notifier_callback,
276 +/* this gets called as part of our init. these steps must be done now so
277 + * that we can use set_pxa_fb_info */
278 +static void __init n516_presetup_fb(void)
283 + /* the frame buffer is divided as follows:
284 + command | CRC | padding
285 + 16kb waveform data | CRC | padding
289 + n516_board_info.fw = 800;
290 + n516_board_info.fh = 624;
292 + /* waveform must be 16k + 2 for checksum */
293 + n516_board_info.wfm_size = roundup(16*1024 + 2, n516_board_info.fw);
295 + padding_size = PAGE_SIZE + (4 * n516_board_info.fw);
297 + /* total is 1 cmd , 1 wfm, padding and image */
298 + totalsize = n516_board_info.fw + n516_board_info.wfm_size;
299 + totalsize += padding_size + (n516_board_info.fw*n516_board_info.fh);
301 + /* save this off because we're manipulating fw after this and
302 + * we'll need it when we're ready to setup the framebuffer */
304 + /* the reason we do this adjustment is because we want to acquire
305 + * more framebuffer memory without imposing custom awareness on the
306 + * underlying driver */
307 + n516_fb_pdata.modes[0].yres = DIV_ROUND_UP(totalsize, n516_board_info.fw);
309 + jz4740_framebuffer_device.dev.platform_data = &n516_fb_pdata;
310 + platform_device_register(&jz4740_framebuffer_device);
313 +/* this gets called by metronomefb as part of its init, in our case, we
314 + * have already completed initial framebuffer init in presetup_fb so we
315 + * can just setup the fb access pointers */
316 +static int n516_setup_fb(struct metronomefb_par *par)
318 + /* metromem was set up by the notifier in share_video_mem so now
319 + * we can use its value to calculate the other entries */
320 + par->metromem_cmd = (struct metromem_cmd *) n516_board_info.metromem;
321 + par->metromem_wfm = n516_board_info.metromem + n516_board_info.fw;
322 + par->metromem_img = par->metromem_wfm + n516_board_info.wfm_size;
323 + par->metromem_img_csum = (u16 *) (par->metromem_img + (n516_board_info.fw * n516_board_info.fh));
324 + par->metromem_dma = n516_board_info.host_fbinfo->fix.smem_start;
329 +static int n516_get_panel_type(void)
334 +static irqreturn_t n516_handle_irq(int irq, void *dev_id)
336 + struct metronomefb_par *par = dev_id;
338 + dev_dbg(&par->pdev->dev, "Metronome IRQ! RDY=%d\n", gpio_get_value(GPIO_DISPLAY_RDY));
339 + wake_up_all(&par->waitq);
341 + return IRQ_HANDLED;
344 +static void n516_power_ctl(struct metronomefb_par *par, int cmd)
347 + case METRONOME_POWER_OFF:
348 + gpio_set_value(GPIO_DISPLAY_OFF, 1);
349 + n516_enable_hostfb(false);
351 + case METRONOME_POWER_ON:
352 + gpio_set_value(GPIO_DISPLAY_OFF, 0);
353 + n516_enable_hostfb(true);
358 +static int n516_get_rdy(struct metronomefb_par *par)
360 + return gpio_get_value(GPIO_DISPLAY_RDY);
363 +static int n516_get_err(struct metronomefb_par *par)
365 + return gpio_get_value(GPIO_DISPLAY_ERR);
368 +static int n516_setup_irq(struct fb_info *info)
372 + dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
374 + ret = request_irq(gpio_to_irq(GPIO_DISPLAY_RDY), n516_handle_irq,
375 + IRQF_TRIGGER_RISING,
376 + "n516", info->par);
378 + dev_err(&n516_device->dev, "request_irq failed: %d\n", ret);
383 +static void n516_set_rst(struct metronomefb_par *par, int state)
385 + dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
387 + gpio_set_value(GPIO_DISPLAY_RST_L, 1);
389 + gpio_set_value(GPIO_DISPLAY_RST_L, 0);
392 +static void n516_set_stdby(struct metronomefb_par *par, int state)
394 + dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
396 + gpio_set_value(GPIO_DISPLAY_STBY, 1);
398 + gpio_set_value(GPIO_DISPLAY_STBY, 0);
401 +static int n516_wait_event(struct metronomefb_par *par)
403 + unsigned long timeout = jiffies + HZ / 20;
405 + dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n",
406 + __func__, gpio_get_value(GPIO_DISPLAY_RDY));
407 + while (n516_get_rdy(par) && time_before(jiffies, timeout))
410 + dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n",
411 + __func__, gpio_get_value(GPIO_DISPLAY_RDY));
412 + return wait_event_timeout(par->waitq,
413 + n516_get_rdy(par), HZ * 2) ? 0 : -EIO;
416 +static int n516_wait_event_intr(struct metronomefb_par *par)
418 + unsigned long timeout = jiffies + HZ/20;
420 + dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n",
421 + __func__, gpio_get_value(GPIO_DISPLAY_RDY));
422 + while (n516_get_rdy(par) && time_before(jiffies, timeout))
425 + dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n",
426 + __func__, gpio_get_value(GPIO_DISPLAY_RDY));
427 + return wait_event_interruptible_timeout(par->waitq,
428 + n516_get_rdy(par), HZ * 2) ? 0 : -EIO;
431 +static void n516_cleanup(struct metronomefb_par *par)
435 + free_irq(gpio_to_irq(GPIO_DISPLAY_RDY), par);
436 + for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i)
437 + gpio_free(metronome_gpios[i]);
440 +static struct metronome_board n516_board __initdata = {
441 + .owner = THIS_MODULE,
442 + .power_ctl = n516_power_ctl,
443 + .setup_irq = n516_setup_irq,
444 + .setup_io = n516_init_metronome_gpios,
445 + .setup_fb = n516_setup_fb,
446 + .set_rst = n516_set_rst,
447 + .get_err = n516_get_err,
448 + .get_rdy = n516_get_rdy,
449 + .set_stdby = n516_set_stdby,
450 + .met_wait_event = n516_wait_event,
451 + .met_wait_event_intr = n516_wait_event_intr,
452 + .get_panel_type = n516_get_panel_type,
453 + .cleanup = n516_cleanup,
456 +static int __init n516_init(void)
460 + /* Keep the metronome off, until its driver is loaded */
461 + ret = gpio_request(GPIO_DISPLAY_OFF, "Display off");
465 + gpio_direction_output(GPIO_DISPLAY_OFF, 1);
467 + /* before anything else, we request notification for any fb
468 + * creation events */
469 + fb_register_client(&n516_fb_notif);
471 + n516_device = platform_device_alloc("metronomefb", -1);
475 + /* the n516_board that will be seen by metronomefb is a copy */
476 + platform_device_add_data(n516_device, &n516_board,
477 + sizeof(n516_board));
479 + n516_presetup_fb();
483 +module_init(n516_init);
485 +MODULE_DESCRIPTION("board driver for n516 display");
486 +MODULE_AUTHOR("Yauhen Kharuzhy");
487 +MODULE_LICENSE("GPL");
488 diff --git a/arch/mips/jz4740/board-n516.c b/arch/mips/jz4740/board-n516.c
490 index 0000000..efb8747
492 +++ b/arch/mips/jz4740/board-n516.c
495 + * linux/arch/mips/jz4740/board-516.c
497 + * JZ4740 n516 board setup routines.
499 + * Copyright (c) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
501 + * This program is free software; you can redistribute it and/or modify
502 + * it under the terms of the GNU General Public License as published by
503 + * the Free Software Foundation; either version 2 of the License, or
504 + * (at your option) any later version.
507 +#include <linux/init.h>
508 +#include <linux/sched.h>
509 +#include <linux/ioport.h>
510 +#include <linux/mm.h>
511 +#include <linux/console.h>
512 +#include <linux/delay.h>
513 +#include <linux/i2c.h>
514 +#include <linux/platform_device.h>
515 +#include <linux/mtd/mtd.h>
516 +#include <linux/mmc/jz4740_mmc.h>
517 +#include <linux/mtd/jz4740_nand.h>
518 +#include <linux/leds.h>
520 +#include <linux/power_supply.h>
521 +#include <linux/power/gpio-charger.h>
523 +#include <linux/i2c.h>
524 +#include <linux/i2c-gpio.h>
526 +#include <asm/mach-jz4740/board-n516.h>
527 +#include <asm/mach-jz4740/platform.h>
531 +static long n516_panic_blink(long time)
533 + gpio_set_value(GPIO_LED_ENABLE, 1);
535 + gpio_set_value(GPIO_LED_ENABLE, 0);
541 +static void __init board_gpio_setup(void)
543 +/* jz_gpio_enable_pullup(JZ_GPIO_PORTD(23));
544 + jz_gpio_enable_pullup(JZ_GPIO_PORTD(24));*/
547 +static struct i2c_gpio_platform_data n516_i2c_pdata = {
548 + .sda_pin = JZ_GPIO_PORTD(23),
549 + .scl_pin = JZ_GPIO_PORTD(24),
554 +static struct platform_device n516_i2c_device = {
555 + .name = "i2c-gpio",
558 + .platform_data = &n516_i2c_pdata,
562 +static const struct i2c_board_info n516_i2c_board_info[] = {
573 +static struct jz4740_mmc_platform_data n516_mmc_pdata = {
574 + .gpio_card_detect = GPIO_SD_CD_N,
575 + .card_detect_active_low = 1,
576 + .gpio_read_only = -1,
577 + .gpio_power = GPIO_SD_VCC_EN_N,
578 + .power_active_low = 1,
581 +static struct gpio_led n516_leds[] = {
583 + .name = "n516:blue:power",
584 + .gpio = GPIO_LED_ENABLE,
585 + .default_state = LEDS_GPIO_DEFSTATE_ON,
586 + .default_trigger = "nand-disk",
590 +static struct gpio_led_platform_data n516_leds_pdata = {
592 + .num_leds = ARRAY_SIZE(n516_leds),
595 +static struct platform_device n516_leds_device = {
596 + .name = "leds-gpio",
599 + .platform_data = &n516_leds_pdata,
603 +static struct mtd_partition n516_partitions[] = {
604 + { .name = "NAND BOOT partition",
605 + .offset = 0 * 0x100000,
606 + .size = 4 * 0x100000,
608 + { .name = "NAND KERNEL partition",
609 + .offset = 4 * 0x100000,
610 + .size = 4 * 0x100000,
612 + { .name = "NAND ROOTFS partition",
613 + .offset = 8 * 0x100000,
614 + .size = 504 * 0x100000,
618 +static struct nand_ecclayout n516_ecclayout = {
621 + 6, 7, 8, 9, 10, 11, 12, 13, 14,
622 + 15, 16, 17, 18, 19, 20, 21, 22, 23,
623 + 24, 25, 26, 27, 28, 29, 30, 31, 32,
624 + 33, 34, 35, 36, 37, 38, 39, 40, 41,
638 +static struct jz_nand_platform_data n516_nand_pdata = {
639 + .ecc_layout = &n516_ecclayout,
640 + .partitions = n516_partitions,
641 + .num_partitions = ARRAY_SIZE(n516_partitions),
645 +static char *n516_batteries[] = {
649 +static struct gpio_charger_platform_data n516_charger_pdata = {
651 + .type = POWER_SUPPLY_TYPE_USB,
652 + .gpio = GPIO_USB_DETECT,
653 + .gpio_active_low = 1,
654 + .batteries = n516_batteries,
655 + .num_batteries = ARRAY_SIZE(n516_batteries),
658 +static struct platform_device n516_charger_device = {
659 + .name = "gpio-charger",
661 + .platform_data = &n516_charger_pdata,
665 +static struct platform_device *n516_devices[] __initdata = {
666 + &jz4740_nand_device,
668 + &jz4740_mmc_device,
669 + &jz4740_i2s_device,
670 + &jz4740_codec_device,
671 + &jz4740_rtc_device,
672 + &jz4740_usb_gdt_device,
674 + &n516_charger_device,
677 +struct jz4740_clock_board_data jz4740_clock_bdata = {
678 + .ext_rate = 12000000,
682 +extern int jz_gpiolib_init(void);
684 +static int n516_setup_platform(void)
686 + if (jz_gpiolib_init())
687 + panic("Failed to initalize jz gpio\n");
689 + jz4740_clock_init();
690 + board_gpio_setup();
692 + panic_blink = n516_panic_blink;
693 + i2c_register_board_info(0, n516_i2c_board_info, ARRAY_SIZE(n516_i2c_board_info));
694 + jz4740_mmc_device.dev.platform_data = &n516_mmc_pdata;
695 + jz4740_nand_device.dev.platform_data = &n516_nand_pdata;
697 + return platform_add_devices(n516_devices, ARRAY_SIZE(n516_devices));
699 +arch_initcall(n516_setup_platform);