Add support for the n516
SVN-Revision: 19987
This commit is contained in:
		| @@ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  *  linux/include/asm-mips/mach-jz4740/board-n516.h | ||||||
|  |  * | ||||||
|  |  *  JZ4730-based N516 board definition. | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef __ASM_JZ4740_N516_H__ | ||||||
|  | #define __ASM_JZ4740_N516_H__ | ||||||
|  |  | ||||||
|  | #include <asm/mach-jz4740/gpio.h> | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * GPIO | ||||||
|  |  */ | ||||||
|  | #define GPIO_SD_VCC_EN_N	JZ_GPIO_PORTD(17) | ||||||
|  | #define GPIO_SD_CD_N		JZ_GPIO_PORTD(7) | ||||||
|  | #define GPIO_SD_WP		JZ_GPIO_PORTD(15) | ||||||
|  | #define GPIO_USB_DETECT		JZ_GPIO_PORTD(19) | ||||||
|  | #define GPIO_CHARG_STAT_N	JZ_GPIO_PORTD(16) | ||||||
|  | #define GPIO_LED_ENABLE       	JZ_GPIO_PORTD(28) | ||||||
|  | #define GPIO_LPC_INT		JZ_GPIO_PORTD(14) | ||||||
|  | #define GPIO_HPHONE_DETECT	JZ_GPIO_PORTD(20) | ||||||
|  | #define GPIO_SPEAKER_ENABLE	JZ_GPIO_PORTD(21) | ||||||
|  |  | ||||||
|  | /* Display */ | ||||||
|  | #define GPIO_DISPLAY_RST_L	JZ_GPIO_PORTB(18) | ||||||
|  | #define GPIO_DISPLAY_RDY	JZ_GPIO_PORTB(17) | ||||||
|  | #define GPIO_DISPLAY_STBY	JZ_GPIO_PORTC(22) | ||||||
|  | #define GPIO_DISPLAY_ERR	JZ_GPIO_PORTC(23) | ||||||
|  | #define GPIO_DISPLAY_OFF_N	JZ_GPIO_PORTD(1) | ||||||
|  |  | ||||||
|  | #endif /* __ASM_JZ4740_N516_H__ */ | ||||||
| @@ -8,6 +8,11 @@ config JZ4740_QI_LB60 | |||||||
| 	select DMA_NONCOHERENT | 	select DMA_NONCOHERENT | ||||||
| 	select SOC_JZ4740 | 	select SOC_JZ4740 | ||||||
|  |  | ||||||
|  | config JZ4740_N516 | ||||||
|  | 	bool "Hanvon n516 eBook reader" | ||||||
|  | 	select DMA_NONCOHERENT | ||||||
|  | 	select SOC_JZ4740 | ||||||
|  |  | ||||||
| config JZ4740_N526 | config JZ4740_N526 | ||||||
| 	bool "Hanvon n526 eBook reader" | 	bool "Hanvon n526 eBook reader" | ||||||
| 	select DMA_NONCOHERENT | 	select DMA_NONCOHERENT | ||||||
|   | |||||||
| @@ -12,9 +12,10 @@ obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o | |||||||
| # board specific support | # board specific support | ||||||
|  |  | ||||||
| obj-$(CONFIG_JZ4740_QI_LB60)	+= board-qi_lb60.o | obj-$(CONFIG_JZ4740_QI_LB60)	+= board-qi_lb60.o | ||||||
|  | obj-$(CONFIG_JZ4740_N516)	+= board-n516.o board-n516-display.o | ||||||
| obj-$(CONFIG_JZ4740_N526)	+= board-n526.o | obj-$(CONFIG_JZ4740_N526)	+= board-n526.o | ||||||
|  |  | ||||||
| # PM support | # PM support | ||||||
|  |  | ||||||
| obj-$(CONFIG_PM)         +=pm.o | obj-$(CONFIG_PM)         += pm.o | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,395 @@ | |||||||
|  | /* | ||||||
|  |  * board-n516-display.c -- Platform device for N516 display | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com> | ||||||
|  |  * | ||||||
|  |  * This file is subject to the terms and conditions of the GNU General Public | ||||||
|  |  * License. See the file COPYING in the main directory of this archive for | ||||||
|  |  * more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/errno.h> | ||||||
|  | #include <linux/string.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/interrupt.h> | ||||||
|  | #include <linux/fb.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/platform_device.h> | ||||||
|  | #include <linux/irq.h> | ||||||
|  | #include <linux/gpio.h> | ||||||
|  | #include <linux/jz4740_fb.h> | ||||||
|  |  | ||||||
|  | #include <asm/mach-jz4740/platform.h> | ||||||
|  | #include <asm/mach-jz4740/board-n516.h> | ||||||
|  |  | ||||||
|  | #include <video/metronomefb.h> | ||||||
|  | #include <linux/console.h> | ||||||
|  |  | ||||||
|  | extern struct platform_device jz_lcd_device; | ||||||
|  |  | ||||||
|  | static struct fb_videomode n516_fb_modes[] = { | ||||||
|  | 	[0] = { | ||||||
|  | 		.name		= "Metronome 800x600", | ||||||
|  | 		.refresh	= 50, | ||||||
|  | 		.xres		= 400, | ||||||
|  | 		.yres		= 624, | ||||||
|  | 		.hsync_len	= 31, | ||||||
|  | 		.vsync_len	= 23, | ||||||
|  | 		.right_margin	= 31, | ||||||
|  | 		.left_margin	= 5, | ||||||
|  | 		.upper_margin	= 1, | ||||||
|  | 		.lower_margin	= 2, | ||||||
|  | 		.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct jz4740_fb_platform_data n516_fb_pdata = { | ||||||
|  | 	.num_modes	= ARRAY_SIZE(n516_fb_modes), | ||||||
|  | 	.modes		= n516_fb_modes, | ||||||
|  | 	.bpp		= 16, | ||||||
|  | 	.lcd_type	= JZ_LCD_TYPE_GENERIC_16_BIT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct n516_board_info { | ||||||
|  | 	uint8_t *metromem; | ||||||
|  | 	size_t wfm_size; | ||||||
|  | 	struct fb_info *host_fbinfo; /* the host LCD controller's fbi */ | ||||||
|  | 	unsigned int fw; | ||||||
|  | 	unsigned int fh; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct platform_device *n516_device; | ||||||
|  | static struct n516_board_info n516_board_info; | ||||||
|  |  | ||||||
|  | static int metronome_gpios[] = { | ||||||
|  | 	GPIO_DISPLAY_STBY, | ||||||
|  | 	GPIO_DISPLAY_RST_L, | ||||||
|  | 	GPIO_DISPLAY_RDY, | ||||||
|  | 	GPIO_DISPLAY_ERR, | ||||||
|  | /*	GPIO_DISPLAY_OFF,*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const char *metronome_gpio_names[] = { | ||||||
|  | 	"Metronome STDBY", | ||||||
|  | 	"Metronome RST", | ||||||
|  | 	"Metronome RDY", | ||||||
|  | 	"Metronome ERR", | ||||||
|  | /*	"Metronone OFF",*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int n516_enable_hostfb(bool enable) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	int blank = enable ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | ||||||
|  |  | ||||||
|  | 	acquire_console_sem(); | ||||||
|  | 	ret = fb_blank(n516_board_info.host_fbinfo, blank); | ||||||
|  | 	release_console_sem(); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_init_metronome_gpios(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) { | ||||||
|  | 		ret = gpio_request(metronome_gpios[i], metronome_gpio_names[i]); | ||||||
|  | 		if (ret) | ||||||
|  | 			goto err; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gpio_direction_output(GPIO_DISPLAY_OFF, 0); | ||||||
|  | 	gpio_direction_output(GPIO_DISPLAY_RST_L, 0); | ||||||
|  | 	gpio_direction_output(GPIO_DISPLAY_STBY, 0); | ||||||
|  | 	gpio_direction_input(GPIO_DISPLAY_RDY); | ||||||
|  | 	gpio_direction_input(GPIO_DISPLAY_ERR); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | err: | ||||||
|  | 	for (--i; i >= 0; --i) | ||||||
|  | 		gpio_free(metronome_gpios[i]); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_share_video_mem(struct fb_info *info) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); | ||||||
|  | 	dev_dbg(&n516_device->dev, "%s, info->var.xres = %u, info->var.yres = %u\n", __func__, info->var.xres, info->var.yres); | ||||||
|  | 	/* rough check if this is our desired fb and not something else */ | ||||||
|  | 	if ((info->var.xres != n516_fb_pdata.modes[0].xres) | ||||||
|  | 		|| (info->var.yres != n516_fb_pdata.modes[0].yres)) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	/* we've now been notified that we have our new fb */ | ||||||
|  | 	n516_board_info.metromem = info->screen_base; | ||||||
|  | 	n516_board_info.host_fbinfo = info; | ||||||
|  |  | ||||||
|  | 	n516_enable_hostfb(false); | ||||||
|  | 	/* try to refcount host drv since we are the consumer after this */ | ||||||
|  | 	if (!try_module_get(info->fbops->owner)) | ||||||
|  | 		return -ENODEV; | ||||||
|  |  | ||||||
|  | 	/* this _add binds metronomefb to n516. metronomefb refcounts n516 */ | ||||||
|  | 	ret = platform_device_add(n516_device); | ||||||
|  |  | ||||||
|  | 	if (ret) { | ||||||
|  | 		platform_device_put(n516_device); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* request our platform independent driver */ | ||||||
|  | 	request_module("metronomefb"); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_unshare_video_mem(struct fb_info *info) | ||||||
|  | { | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); | ||||||
|  |  | ||||||
|  | 	if (info != n516_board_info.host_fbinfo) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	module_put(n516_board_info.host_fbinfo->fbops->owner); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_fb_notifier_callback(struct notifier_block *self, | ||||||
|  | 				 unsigned long event, void *data) | ||||||
|  | { | ||||||
|  | 	struct fb_event *evdata = data; | ||||||
|  | 	struct fb_info *info = evdata->info; | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); | ||||||
|  |  | ||||||
|  | 	if (event == FB_EVENT_FB_REGISTERED) | ||||||
|  | 		return n516_share_video_mem(info); | ||||||
|  | 	else if (event == FB_EVENT_FB_UNREGISTERED) | ||||||
|  | 		return n516_unshare_video_mem(info); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct notifier_block n516_fb_notif = { | ||||||
|  | 	.notifier_call = n516_fb_notifier_callback, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* this gets called as part of our init. these steps must be done now so | ||||||
|  |  * that we can use set_pxa_fb_info */ | ||||||
|  | static void __init n516_presetup_fb(void) | ||||||
|  | { | ||||||
|  | 	int padding_size; | ||||||
|  | 	int totalsize; | ||||||
|  |  | ||||||
|  | 	/* the frame buffer is divided as follows: | ||||||
|  | 	command | CRC | padding | ||||||
|  | 	16kb waveform data | CRC | padding | ||||||
|  | 	image data | CRC | ||||||
|  | 	*/ | ||||||
|  |  | ||||||
|  | 	n516_board_info.fw = 800; | ||||||
|  | 	n516_board_info.fh = 624; | ||||||
|  |  | ||||||
|  | 	/* waveform must be 16k + 2 for checksum */ | ||||||
|  | 	n516_board_info.wfm_size = roundup(16*1024 + 2, n516_board_info.fw); | ||||||
|  |  | ||||||
|  | 	padding_size = PAGE_SIZE + (4 * n516_board_info.fw); | ||||||
|  |  | ||||||
|  | 	/* total is 1 cmd , 1 wfm, padding and image */ | ||||||
|  | 	totalsize = n516_board_info.fw + n516_board_info.wfm_size; | ||||||
|  | 	totalsize += padding_size + (n516_board_info.fw*n516_board_info.fh); | ||||||
|  |  | ||||||
|  | 	/* save this off because we're manipulating fw after this and | ||||||
|  | 	 * we'll need it when we're ready to setup the framebuffer */ | ||||||
|  |  | ||||||
|  | 	/* the reason we do this adjustment is because we want to acquire | ||||||
|  | 	 * more framebuffer memory without imposing custom awareness on the | ||||||
|  | 	 * underlying driver */ | ||||||
|  | 	n516_fb_pdata.modes[0].yres = DIV_ROUND_UP(totalsize, n516_board_info.fw); | ||||||
|  |  | ||||||
|  | 	jz4740_framebuffer_device.dev.platform_data = &n516_fb_pdata; | ||||||
|  | 	platform_device_register(&jz4740_framebuffer_device); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* this gets called by metronomefb as part of its init, in our case, we | ||||||
|  |  * have already completed initial framebuffer init in presetup_fb so we | ||||||
|  |  * can just setup the fb access pointers */ | ||||||
|  | static int n516_setup_fb(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	/* metromem was set up by the notifier in share_video_mem so now | ||||||
|  | 	 * we can use its value to calculate the other entries */ | ||||||
|  | 	par->metromem_cmd = (struct metromem_cmd *) n516_board_info.metromem; | ||||||
|  | 	par->metromem_wfm = n516_board_info.metromem + n516_board_info.fw; | ||||||
|  | 	par->metromem_img = par->metromem_wfm + n516_board_info.wfm_size; | ||||||
|  | 	par->metromem_img_csum = (u16 *) (par->metromem_img + (n516_board_info.fw * n516_board_info.fh)); | ||||||
|  | 	par->metromem_dma = n516_board_info.host_fbinfo->fix.smem_start; | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_get_panel_type(void) | ||||||
|  | { | ||||||
|  | 	return 5; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static irqreturn_t n516_handle_irq(int irq, void *dev_id) | ||||||
|  | { | ||||||
|  | 	struct metronomefb_par *par = dev_id; | ||||||
|  |  | ||||||
|  | 	dev_dbg(&par->pdev->dev, "Metronome IRQ! RDY=%d\n", 	gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	wake_up_all(&par->waitq); | ||||||
|  |  | ||||||
|  | 	return IRQ_HANDLED; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_power_ctl(struct metronomefb_par *par, int cmd) | ||||||
|  | { | ||||||
|  | 	switch (cmd) { | ||||||
|  | 	case METRONOME_POWER_OFF: | ||||||
|  | 		gpio_set_value(GPIO_DISPLAY_OFF, 1); | ||||||
|  | 		n516_enable_hostfb(false); | ||||||
|  | 		break; | ||||||
|  | 	case METRONOME_POWER_ON: | ||||||
|  | 		gpio_set_value(GPIO_DISPLAY_OFF, 0); | ||||||
|  | 		n516_enable_hostfb(true); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_get_rdy(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	return gpio_get_value(GPIO_DISPLAY_RDY); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_get_err(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	return gpio_get_value(GPIO_DISPLAY_ERR); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_setup_irq(struct fb_info *info) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER %s\n", __func__); | ||||||
|  |  | ||||||
|  | 	ret = request_irq(gpio_to_irq(GPIO_DISPLAY_RDY), n516_handle_irq, | ||||||
|  | 				IRQF_TRIGGER_RISING, | ||||||
|  | 				"n516", info->par); | ||||||
|  | 	if (ret) | ||||||
|  | 		dev_err(&n516_device->dev, "request_irq failed: %d\n", ret); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_set_rst(struct metronomefb_par *par, int state) | ||||||
|  | { | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	if (state) | ||||||
|  | 		gpio_set_value(GPIO_DISPLAY_RST_L, 1); | ||||||
|  | 	else | ||||||
|  | 		gpio_set_value(GPIO_DISPLAY_RST_L, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_set_stdby(struct metronomefb_par *par, int state) | ||||||
|  | { | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	if (state) | ||||||
|  | 		gpio_set_value(GPIO_DISPLAY_STBY, 1); | ||||||
|  | 	else | ||||||
|  | 		gpio_set_value(GPIO_DISPLAY_STBY, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_wait_event(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	unsigned long timeout = jiffies + HZ / 20; | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n", | ||||||
|  | 			__func__, gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	while (n516_get_rdy(par) && time_before(jiffies, timeout)) | ||||||
|  | 		schedule(); | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n", | ||||||
|  | 			__func__, gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	return wait_event_timeout(par->waitq, | ||||||
|  | 			n516_get_rdy(par), HZ * 2) ? 0 : -EIO; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_wait_event_intr(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	unsigned long timeout = jiffies + HZ/20; | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n", | ||||||
|  | 			__func__, gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	while (n516_get_rdy(par) && time_before(jiffies, timeout)) | ||||||
|  | 		schedule(); | ||||||
|  |  | ||||||
|  | 	dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n", | ||||||
|  | 			__func__, gpio_get_value(GPIO_DISPLAY_RDY)); | ||||||
|  | 	return wait_event_interruptible_timeout(par->waitq, | ||||||
|  | 					n516_get_rdy(par), HZ * 2) ? 0 : -EIO; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_cleanup(struct metronomefb_par *par) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	free_irq(gpio_to_irq(GPIO_DISPLAY_RDY), par); | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) | ||||||
|  | 		gpio_free(metronome_gpios[i]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct metronome_board n516_board __initdata = { | ||||||
|  | 	.owner			= THIS_MODULE, | ||||||
|  | 	.power_ctl		= n516_power_ctl, | ||||||
|  | 	.setup_irq		= n516_setup_irq, | ||||||
|  | 	.setup_io		= n516_init_metronome_gpios, | ||||||
|  | 	.setup_fb		= n516_setup_fb, | ||||||
|  | 	.set_rst		= n516_set_rst, | ||||||
|  | 	.get_err		= n516_get_err, | ||||||
|  | 	.get_rdy		= n516_get_rdy, | ||||||
|  | 	.set_stdby		= n516_set_stdby, | ||||||
|  | 	.met_wait_event		= n516_wait_event, | ||||||
|  | 	.met_wait_event_intr	= n516_wait_event_intr, | ||||||
|  | 	.get_panel_type		= n516_get_panel_type, | ||||||
|  | 	.cleanup		= n516_cleanup, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int __init n516_init(void) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	/* Keep the metronome off, until its driver is loaded */ | ||||||
|  | 	ret = gpio_request(GPIO_DISPLAY_OFF, "Display off"); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  |  | ||||||
|  | 	gpio_direction_output(GPIO_DISPLAY_OFF, 1); | ||||||
|  |  | ||||||
|  | 	/* before anything else, we request notification for any fb | ||||||
|  | 	 * creation events */ | ||||||
|  | 	fb_register_client(&n516_fb_notif); | ||||||
|  |  | ||||||
|  | 	n516_device = platform_device_alloc("metronomefb", -1); | ||||||
|  | 	if (!n516_device) | ||||||
|  | 		return -ENOMEM; | ||||||
|  |  | ||||||
|  | 	/* the n516_board that will be seen by metronomefb is a copy */ | ||||||
|  | 	platform_device_add_data(n516_device, &n516_board, | ||||||
|  | 					sizeof(n516_board)); | ||||||
|  |  | ||||||
|  | 	n516_presetup_fb(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | module_init(n516_init); | ||||||
|  |  | ||||||
|  | MODULE_DESCRIPTION("board driver for n516 display"); | ||||||
|  | MODULE_AUTHOR("Yauhen Kharuzhy"); | ||||||
|  | MODULE_LICENSE("GPL"); | ||||||
							
								
								
									
										201
									
								
								target/linux/xburst/files-2.6.32/arch/mips/jz4740/board-n516.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								target/linux/xburst/files-2.6.32/arch/mips/jz4740/board-n516.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | |||||||
|  | /* | ||||||
|  |  * linux/arch/mips/jz4740/board-516.c | ||||||
|  |  * | ||||||
|  |  * JZ4740 n516 board setup routines. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2009, Yauhen Kharuzhy <jekhor@gmail.com> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/ioport.h> | ||||||
|  | #include <linux/mm.h> | ||||||
|  | #include <linux/console.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/i2c.h> | ||||||
|  | #include <linux/platform_device.h> | ||||||
|  | #include <linux/mtd/mtd.h> | ||||||
|  | #include <linux/mmc/jz4740_mmc.h> | ||||||
|  | #include <linux/mtd/jz4740_nand.h> | ||||||
|  | #include <linux/leds.h> | ||||||
|  |  | ||||||
|  | #include <linux/power_supply.h> | ||||||
|  | #include <linux/power/gpio-charger.h> | ||||||
|  |  | ||||||
|  | #include <linux/i2c.h> | ||||||
|  | #include <linux/i2c-gpio.h> | ||||||
|  |  | ||||||
|  | #include <asm/mach-jz4740/board-n516.h> | ||||||
|  | #include <asm/mach-jz4740/platform.h> | ||||||
|  |  | ||||||
|  | #include "clock.h" | ||||||
|  |  | ||||||
|  | static long n516_panic_blink(long time) | ||||||
|  | { | ||||||
|  | 	gpio_set_value(GPIO_LED_ENABLE, 1); | ||||||
|  | 	mdelay(200); | ||||||
|  | 	gpio_set_value(GPIO_LED_ENABLE, 0); | ||||||
|  | 	mdelay(200); | ||||||
|  |  | ||||||
|  | 	return 400; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void __init board_gpio_setup(void) | ||||||
|  | { | ||||||
|  | /*	jz_gpio_enable_pullup(JZ_GPIO_PORTD(23)); | ||||||
|  | 	jz_gpio_enable_pullup(JZ_GPIO_PORTD(24));*/ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct i2c_gpio_platform_data n516_i2c_pdata = { | ||||||
|  | 	.sda_pin = JZ_GPIO_PORTD(23), | ||||||
|  | 	.scl_pin = JZ_GPIO_PORTD(24), | ||||||
|  | 	.udelay = 2, | ||||||
|  | 	.timeout = 3 * HZ, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct platform_device n516_i2c_device = { | ||||||
|  | 	.name = "i2c-gpio", | ||||||
|  | 	.id = -1, | ||||||
|  | 	.dev = { | ||||||
|  | 		.platform_data = &n516_i2c_pdata, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct i2c_board_info n516_i2c_board_info[] = { | ||||||
|  | 	{ | ||||||
|  | 		.type		= "LPC524", | ||||||
|  | 		.addr		= 0x54, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		.type		= "lm75a", | ||||||
|  | 		.addr		= 0x48, | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct jz4740_mmc_platform_data n516_mmc_pdata = { | ||||||
|  | 	.gpio_card_detect	= GPIO_SD_CD_N, | ||||||
|  | 	.card_detect_active_low = 1, | ||||||
|  | 	.gpio_read_only		= -1, | ||||||
|  | 	.gpio_power		= GPIO_SD_VCC_EN_N, | ||||||
|  | 	.power_active_low = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct gpio_led n516_leds[] = { | ||||||
|  | 	{ | ||||||
|  | 		.name = "n516:blue:power", | ||||||
|  | 		.gpio = GPIO_LED_ENABLE, | ||||||
|  | 		.default_state = LEDS_GPIO_DEFSTATE_ON, | ||||||
|  | 		.default_trigger = "nand-disk", | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct gpio_led_platform_data n516_leds_pdata = { | ||||||
|  | 	.leds = n516_leds, | ||||||
|  | 	.num_leds = ARRAY_SIZE(n516_leds), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct platform_device n516_leds_device = { | ||||||
|  | 	.name = "leds-gpio", | ||||||
|  | 	.id = -1, | ||||||
|  | 	.dev = { | ||||||
|  | 		.platform_data = &n516_leds_pdata, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct mtd_partition n516_partitions[] = { | ||||||
|  | 	{ .name = "NAND BOOT partition", | ||||||
|  | 	  .offset = 0 * 0x100000, | ||||||
|  | 	  .size = 4 * 0x100000, | ||||||
|  |  	}, | ||||||
|  | 	{ .name = "NAND KERNEL partition", | ||||||
|  | 	  .offset = 4 * 0x100000, | ||||||
|  | 	  .size = 4 * 0x100000, | ||||||
|  |  	}, | ||||||
|  | 	{ .name = "NAND ROOTFS partition", | ||||||
|  | 	  .offset = 8 * 0x100000, | ||||||
|  | 	  .size = 504 * 0x100000, | ||||||
|  |  	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct nand_ecclayout n516_ecclayout = { | ||||||
|  | 	.eccbytes = 36, | ||||||
|  | 	.eccpos = { | ||||||
|  | 		 6,  7,  8,  9, 10, 11, 12, 13, 14, | ||||||
|  | 		15, 16, 17, 18, 19, 20, 21, 22, 23, | ||||||
|  | 		24, 25, 26, 27, 28, 29, 30, 31, 32, | ||||||
|  | 		33, 34, 35, 36, 37, 38, 39, 40, 41, | ||||||
|  | 	}, | ||||||
|  | 	.oobfree = { | ||||||
|  | 		{.offset = 2, | ||||||
|  | 		 .length = 4}, | ||||||
|  | 		{.offset = 42, | ||||||
|  | 		 .length = 22}} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct jz_nand_platform_data n516_nand_pdata = { | ||||||
|  | 	.ecc_layout = &n516_ecclayout, | ||||||
|  | 	.partitions = n516_partitions, | ||||||
|  | 	.num_partitions = ARRAY_SIZE(n516_partitions), | ||||||
|  | 	.busy_gpio = 94, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static char *n516_batteries[] = { | ||||||
|  | 	"n516_battery", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct gpio_charger_platform_data n516_charger_pdata = { | ||||||
|  | 	.name = "usb", | ||||||
|  | 	.type = POWER_SUPPLY_TYPE_USB, | ||||||
|  | 	.gpio = GPIO_USB_DETECT, | ||||||
|  | 	.gpio_active_low = 1, | ||||||
|  | 	.batteries = n516_batteries, | ||||||
|  | 	.num_batteries = ARRAY_SIZE(n516_batteries), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct platform_device n516_charger_device = { | ||||||
|  | 	.name = "gpio-charger", | ||||||
|  | 	.dev = { | ||||||
|  | 		.platform_data = &n516_charger_pdata, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct platform_device *n516_devices[] __initdata = { | ||||||
|  | 	&jz4740_nand_device, | ||||||
|  | 	&n516_leds_device, | ||||||
|  | 	&jz4740_mmc_device, | ||||||
|  | 	&jz4740_i2s_device, | ||||||
|  | 	&jz4740_codec_device, | ||||||
|  | 	&jz4740_rtc_device, | ||||||
|  | 	&jz4740_usb_gdt_device, | ||||||
|  | 	&n516_i2c_device, | ||||||
|  | 	&n516_charger_device, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct jz4740_clock_board_data jz4740_clock_bdata = { | ||||||
|  | 	.ext_rate = 12000000, | ||||||
|  | 	.rtc_rate = 32768, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | extern int jz_gpiolib_init(void); | ||||||
|  |  | ||||||
|  | static int n516_setup_platform(void) | ||||||
|  | { | ||||||
|  | 	if (jz_gpiolib_init()) | ||||||
|  | 		panic("Failed to initalize jz gpio\n"); | ||||||
|  |  | ||||||
|  | 	jz4740_clock_init(); | ||||||
|  | 	board_gpio_setup(); | ||||||
|  |  | ||||||
|  | 	panic_blink = n516_panic_blink; | ||||||
|  | 	i2c_register_board_info(0, n516_i2c_board_info, ARRAY_SIZE(n516_i2c_board_info)); | ||||||
|  | 	jz4740_mmc_device.dev.platform_data = &n516_mmc_pdata; | ||||||
|  | 	jz4740_nand_device.dev.platform_data = &n516_nand_pdata; | ||||||
|  |  | ||||||
|  | 	return platform_add_devices(n516_devices, ARRAY_SIZE(n516_devices)); | ||||||
|  | } | ||||||
|  | arch_initcall(n516_setup_platform); | ||||||
							
								
								
									
										491
									
								
								target/linux/xburst/files-2.6.32/drivers/i2c/chips/n516-lpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								target/linux/xburst/files-2.6.32/drivers/i2c/chips/n516-lpc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,491 @@ | |||||||
|  | /* | ||||||
|  |  * board-n516-display.c -- Platform device for N516 display | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com> | ||||||
|  |  * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||||||
|  |  * | ||||||
|  |  * This file is subject to the terms and conditions of the GNU General Public | ||||||
|  |  * License. See the file COPYING in the main directory of this archive for | ||||||
|  |  * more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/version.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/fs.h> | ||||||
|  | #include <linux/interrupt.h> | ||||||
|  | #include <linux/irq.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/pm.h> | ||||||
|  | #include <linux/sysctl.h> | ||||||
|  | #include <linux/proc_fs.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/platform_device.h> | ||||||
|  | #include <linux/input.h> | ||||||
|  | #include <linux/power_supply.h> | ||||||
|  | #include <linux/suspend.h> | ||||||
|  |  | ||||||
|  | #include <linux/i2c.h> | ||||||
|  |  | ||||||
|  | #include <asm/mach-jz4740/irq.h> | ||||||
|  | #include <asm/mach-jz4740/gpio.h> | ||||||
|  | #include <asm/mach-jz4740/board-n516.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int batt_level=0; | ||||||
|  | module_param(batt_level, int, 0); | ||||||
|  |  | ||||||
|  | struct n516_lpc_chip { | ||||||
|  | 	struct i2c_client	*i2c_client; | ||||||
|  | 	struct input_dev	*input; | ||||||
|  | 	unsigned int		battery_level; | ||||||
|  | 	unsigned int		suspending:1, can_sleep:1; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct n516_lpc_chip *the_lpc; | ||||||
|  |  | ||||||
|  | struct i2c_device_id n516_lpc_i2c_ids[] = { | ||||||
|  | 	{"LPC524", 0}, | ||||||
|  | 	{}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | MODULE_DEVICE_TABLE(i2c, n516_lpc_i2c_ids); | ||||||
|  |  | ||||||
|  | static const unsigned short normal_i2c[] = {0x54, I2C_CLIENT_END}; | ||||||
|  |  | ||||||
|  | static const unsigned int n516_lpc_keymap[] = { | ||||||
|  | 	[0x01] = KEY_4, | ||||||
|  | 	[0x02] = KEY_3, | ||||||
|  | 	[0x03] = KEY_2, | ||||||
|  | 	[0x04] = KEY_1, | ||||||
|  | 	[0x05] = KEY_0, | ||||||
|  | 	[0x07] = KEY_9, | ||||||
|  | 	[0x08] = KEY_8, | ||||||
|  | 	[0x09] = KEY_7, | ||||||
|  | 	[0x0a] = KEY_6, | ||||||
|  | 	[0x0b] = KEY_5, | ||||||
|  | 	[0x0d] = KEY_PLAYPAUSE, | ||||||
|  | 	[0x0e] = KEY_MENU, | ||||||
|  | 	[0x0f] = KEY_SEARCH, | ||||||
|  | 	[0x10] = KEY_DIRECTION, | ||||||
|  | 	[0x11] = KEY_SPACE, | ||||||
|  | 	[0x13] = KEY_ENTER, | ||||||
|  | 	[0x14] = KEY_UP, | ||||||
|  | 	[0x15] = KEY_DOWN, | ||||||
|  | 	[0x16] = KEY_RIGHT, | ||||||
|  | 	[0x17] = KEY_LEFT, | ||||||
|  | 	[0x19] = KEY_PAGEDOWN, | ||||||
|  | 	[0x1a] = KEY_PAGEUP, | ||||||
|  | 	[0x1c] = KEY_POWER, | ||||||
|  | 	[0x1d] = KEY_ESC, | ||||||
|  | 	[0x1e] = KEY_SLEEP, | ||||||
|  | /*	[0x1f] = KEY_WAKEUP,*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const unsigned int batt_charge[] = {0, 7, 20, 45, 65, 80, 100}; | ||||||
|  | #define MAX_BAT_LEVEL (ARRAY_SIZE(batt_charge) - 1) | ||||||
|  |  | ||||||
|  | /* Insmod parameters */ | ||||||
|  | I2C_CLIENT_INSMOD_1(n516_lpc); | ||||||
|  |  | ||||||
|  | static inline int n516_bat_usb_connected(void) | ||||||
|  | { | ||||||
|  | 	return !gpio_get_value(GPIO_USB_DETECT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline int n516_bat_charging(void) | ||||||
|  | { | ||||||
|  | 	return !gpio_get_value(GPIO_CHARG_STAT_N); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_bat_get_status(struct power_supply *b) | ||||||
|  | { | ||||||
|  | 	if (n516_bat_usb_connected()) { | ||||||
|  | 		if (n516_bat_charging()) | ||||||
|  | 			return POWER_SUPPLY_STATUS_CHARGING; | ||||||
|  | 		else | ||||||
|  | 			return POWER_SUPPLY_STATUS_FULL; | ||||||
|  | 	} else { | ||||||
|  | 		return POWER_SUPPLY_STATUS_DISCHARGING; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_bat_get_charge(struct power_supply *b) | ||||||
|  | { | ||||||
|  | 	return batt_charge[the_lpc->battery_level]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_bat_get_property(struct power_supply *b, | ||||||
|  | 		enum power_supply_property psp, | ||||||
|  | 		union power_supply_propval *val) | ||||||
|  | { | ||||||
|  | 	switch (psp) { | ||||||
|  | 	case POWER_SUPPLY_PROP_STATUS: | ||||||
|  | 		val->intval = n516_bat_get_status(b); | ||||||
|  | 		break; | ||||||
|  | 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||||||
|  | 		val->intval = 100; | ||||||
|  | 		break; | ||||||
|  | 	case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: | ||||||
|  | 		val->intval = 0; | ||||||
|  | 		break; | ||||||
|  | 	case POWER_SUPPLY_PROP_CHARGE_NOW: | ||||||
|  | 		val->intval = n516_bat_get_charge(b); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_bat_power_changed(struct power_supply *p) | ||||||
|  | { | ||||||
|  | 	power_supply_changed(p); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static enum power_supply_property n516_bat_properties[] = { | ||||||
|  | 	POWER_SUPPLY_PROP_STATUS, | ||||||
|  | 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||||||
|  | 	POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, | ||||||
|  | 	POWER_SUPPLY_PROP_CHARGE_NOW, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct power_supply n516_battery = { | ||||||
|  | 	.name		= "n516-battery", | ||||||
|  | 	.get_property	= n516_bat_get_property, | ||||||
|  | 	.properties	= n516_bat_properties, | ||||||
|  | 	.num_properties	= ARRAY_SIZE(n516_bat_properties), | ||||||
|  | 	.external_power_changed = n516_bat_power_changed, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static irqreturn_t n516_bat_charge_irq(int irq, void *dev) | ||||||
|  | { | ||||||
|  | 	struct power_supply *psy = dev; | ||||||
|  |  | ||||||
|  | 	dev_dbg(psy->dev, "Battery charging IRQ\n"); | ||||||
|  |  | ||||||
|  | 	if (n516_bat_usb_connected() && !n516_bat_charging()) | ||||||
|  | 		the_lpc->battery_level = MAX_BAT_LEVEL; | ||||||
|  |  | ||||||
|  | 	power_supply_changed(psy); | ||||||
|  |  | ||||||
|  | 	return IRQ_HANDLED; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_lpc_set_normal_mode(struct n516_lpc_chip *chip) | ||||||
|  | { | ||||||
|  | 	struct i2c_client *client = chip->i2c_client; | ||||||
|  | 	unsigned char val = 0x02; | ||||||
|  | 	struct i2c_msg msg = {client->addr, client->flags, 1, &val}; | ||||||
|  | 	int ret = 0; | ||||||
|  |  | ||||||
|  | 	ret = i2c_transfer(client->adapter, &msg, 1); | ||||||
|  | 	return ret > 0 ? 0 : ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_key_event(struct n516_lpc_chip *chip, unsigned char keycode) | ||||||
|  | { | ||||||
|  | 	struct i2c_client *client = chip->i2c_client; | ||||||
|  | 	bool long_press = false; | ||||||
|  |  | ||||||
|  | 	if (keycode & 0x40) { | ||||||
|  | 		keycode &= ~0x40; | ||||||
|  | 		long_press = true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dev_dbg(&client->dev, "keycode: 0x%02x, long_press: 0x%02x\n", keycode, (unsigned int)long_press); | ||||||
|  |  | ||||||
|  | 	if (keycode >= ARRAY_SIZE(n516_lpc_keymap) || n516_lpc_keymap[keycode] == 0) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (long_press) | ||||||
|  | 		input_report_key(chip->input, KEY_LEFTALT, 1); | ||||||
|  |  | ||||||
|  | 	input_report_key(chip->input, n516_lpc_keymap[keycode], 1); | ||||||
|  | 	input_sync(chip->input); | ||||||
|  | 	input_report_key(chip->input, n516_lpc_keymap[keycode], 0); | ||||||
|  |  | ||||||
|  | 	if (long_press) | ||||||
|  | 		input_report_key(chip->input, KEY_LEFTALT, 0); | ||||||
|  | 	input_sync(chip->input); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void n516_battery_event(struct n516_lpc_chip *chip, unsigned char battery_level) | ||||||
|  | { | ||||||
|  | 	if (battery_level != chip->battery_level) { | ||||||
|  | 		chip->battery_level = battery_level; | ||||||
|  | 		power_supply_changed(&n516_battery); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static irqreturn_t n516_lpc_irq(int irq, void *devid) | ||||||
|  | { | ||||||
|  | 	struct n516_lpc_chip *chip = (struct n516_lpc_chip*)devid; | ||||||
|  | 	int ret; | ||||||
|  | 	unsigned char raw_msg; | ||||||
|  | 	struct i2c_client *client = chip->i2c_client; | ||||||
|  | 	struct i2c_msg msg = {client->addr, client->flags | I2C_M_RD, 1, &raw_msg}; | ||||||
|  |  | ||||||
|  | 	ret = i2c_transfer(client->adapter, &msg, 1); | ||||||
|  | 	if (ret != 1) { | ||||||
|  | 		dev_dbg(&client->dev, "I2C error: %d\n", ret); | ||||||
|  | 		return IRQ_HANDLED; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dev_dbg(&client->dev, "msg: 0x%02x\n", raw_msg); | ||||||
|  |  | ||||||
|  | 	if ((raw_msg & 0x40) < ARRAY_SIZE(n516_lpc_keymap)) { | ||||||
|  | 		n516_key_event(chip, raw_msg); | ||||||
|  | 	} else if ((raw_msg >= 0x81) && (raw_msg <= 0x87)) { | ||||||
|  | 		n516_battery_event(chip, raw_msg - 0x81); | ||||||
|  | 	} else { | ||||||
|  | 		n516_lpc_set_normal_mode(chip); | ||||||
|  | 		dev_warn(&client->dev, "Unkown message: %x\n", raw_msg); | ||||||
|  | 		ret = i2c_transfer(client->adapter, &msg, 1); | ||||||
|  | 		if (ret != 1) { | ||||||
|  | 			dev_dbg(&client->dev, "I2C error: %d\n", ret); | ||||||
|  | 		} else { | ||||||
|  | 			dev_warn(&client->dev, "Unkown message part 2: %x\n", raw_msg); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 	if (chip->suspending) | ||||||
|  | 		chip->can_sleep = 0; | ||||||
|  |  | ||||||
|  | 	printk("foobar\n"); | ||||||
|  |  | ||||||
|  | 	return IRQ_HANDLED; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void n516_lpc_power_off(void) | ||||||
|  | { | ||||||
|  | 	struct i2c_client *client = the_lpc->i2c_client; | ||||||
|  | 	unsigned char val = 0x01; | ||||||
|  | 	struct i2c_msg msg = {client->addr, client->flags, 1, &val}; | ||||||
|  |  | ||||||
|  | 	printk("Issue LPC POWEROFF command...\n"); | ||||||
|  | 	while (1) | ||||||
|  | 		i2c_transfer(client->adapter, &msg, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_lpc_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_lpc_suspend_notifier(struct notifier_block *nb, | ||||||
|  | 		                                unsigned long event, | ||||||
|  | 						void *dummy) | ||||||
|  | { | ||||||
|  | 	switch(event) { | ||||||
|  | 	case PM_SUSPEND_PREPARE: | ||||||
|  | 		the_lpc->suspending = 1; | ||||||
|  | 		the_lpc->can_sleep = 1; | ||||||
|  | 		break; | ||||||
|  | 	case PM_POST_SUSPEND: | ||||||
|  | 		the_lpc->suspending = 0; | ||||||
|  | 		the_lpc->can_sleep = 1; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return NOTIFY_DONE; | ||||||
|  | 	} | ||||||
|  | 	return NOTIFY_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct notifier_block n516_lpc_notif_block = { | ||||||
|  | 	.notifier_call = n516_lpc_suspend_notifier, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int n516_lpc_probe(struct i2c_client *client, const struct i2c_device_id *id) | ||||||
|  | { | ||||||
|  | 	struct n516_lpc_chip *chip; | ||||||
|  | 	struct input_dev *input; | ||||||
|  | 	int ret = 0; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||||||
|  | 	if (!chip) | ||||||
|  | 		return -ENOMEM; | ||||||
|  |  | ||||||
|  | 	the_lpc = chip; | ||||||
|  | 	chip->i2c_client = client; | ||||||
|  | 	if ((batt_level > 0) && (batt_level < ARRAY_SIZE(batt_charge))) | ||||||
|  | 		chip->battery_level = batt_level; | ||||||
|  | 	i2c_set_clientdata(client, chip); | ||||||
|  |  | ||||||
|  | 	ret = gpio_request(GPIO_LPC_INT, "LPC interrupt request"); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(&client->dev, "Unable to reguest LPC INT GPIO\n"); | ||||||
|  | 		goto err_gpio_req_lpcint; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ret = gpio_request(GPIO_CHARG_STAT_N, "LPC interrupt request"); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(&client->dev, "Unable to reguest CHARG STAT GPIO\n"); | ||||||
|  | 		goto err_gpio_req_chargstat; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n516_lpc_set_normal_mode(chip); | ||||||
|  |  | ||||||
|  | 	input = input_allocate_device(); | ||||||
|  | 	if (!input) { | ||||||
|  | 		dev_err(&client->dev, "Unable to allocate input device\n"); | ||||||
|  | 		ret = -ENOMEM; | ||||||
|  | 		goto err_input_alloc; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	chip->input = input; | ||||||
|  |  | ||||||
|  | 	__set_bit(EV_KEY, input->evbit); | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(n516_lpc_keymap); i++) | ||||||
|  | 		__set_bit(n516_lpc_keymap[i], input->keybit); | ||||||
|  |  | ||||||
|  | 	__set_bit(KEY_LEFTALT, input->keybit); | ||||||
|  |  | ||||||
|  | 	input->name = "n516-keys"; | ||||||
|  | 	input->phys = "n516-keys/input0"; | ||||||
|  | 	input->dev.parent = &client->dev; | ||||||
|  | 	input->id.bustype = BUS_I2C; | ||||||
|  | 	input->id.vendor = 0x0001; | ||||||
|  | 	input->id.product = 0x0001; | ||||||
|  | 	input->id.version = 0x0100; | ||||||
|  |  | ||||||
|  | 	ret = input_register_device(input); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		dev_err(&client->dev, "Unable to register input device\n"); | ||||||
|  | 		goto err_input_register; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ret = power_supply_register(NULL, &n516_battery); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(&client->dev, "Unable to register N516 battery\n"); | ||||||
|  | 		goto err_bat_reg; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (n516_bat_usb_connected() && !n516_bat_charging()) | ||||||
|  | 		the_lpc->battery_level = MAX_BAT_LEVEL; | ||||||
|  |  | ||||||
|  | 	ret = request_threaded_irq(gpio_to_irq(GPIO_LPC_INT), NULL, n516_lpc_irq, | ||||||
|  | 			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lpc", chip); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(&client->dev, "request_irq failed: %d\n", ret); | ||||||
|  | 		goto err_request_lpc_irq; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ret = request_irq(gpio_to_irq(GPIO_CHARG_STAT_N), n516_bat_charge_irq, | ||||||
|  | 				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "battery charging", &n516_battery); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(&client->dev, "Unable to claim battery charging IRQ\n"); | ||||||
|  | 		goto err_request_chrg_irq; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pm_power_off = n516_lpc_power_off; | ||||||
|  | 	ret = register_pm_notifier(&n516_lpc_notif_block); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(&client->dev, "Unable to register PM notify block\n"); | ||||||
|  | 		goto err_reg_pm_notifier; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	device_init_wakeup(&client->dev, 1); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  |  | ||||||
|  | 	unregister_pm_notifier(&n516_lpc_notif_block); | ||||||
|  | err_reg_pm_notifier: | ||||||
|  | 	free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery); | ||||||
|  | err_request_chrg_irq: | ||||||
|  | 	free_irq(gpio_to_irq(GPIO_LPC_INT), chip); | ||||||
|  | err_request_lpc_irq: | ||||||
|  | 	power_supply_unregister(&n516_battery); | ||||||
|  | err_bat_reg: | ||||||
|  | 	input_unregister_device(input); | ||||||
|  | err_input_register: | ||||||
|  | 	input_free_device(input); | ||||||
|  | err_input_alloc: | ||||||
|  | 	gpio_free(GPIO_CHARG_STAT_N); | ||||||
|  | err_gpio_req_chargstat: | ||||||
|  | 	gpio_free(GPIO_LPC_INT); | ||||||
|  | err_gpio_req_lpcint: | ||||||
|  | 	i2c_set_clientdata(client, NULL); | ||||||
|  | 	kfree(chip); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_lpc_remove(struct i2c_client *client) | ||||||
|  | { | ||||||
|  | 	struct n516_lpc_chip *chip = i2c_get_clientdata(client); | ||||||
|  |  | ||||||
|  | 	unregister_pm_notifier(&n516_lpc_notif_block); | ||||||
|  | 	pm_power_off = NULL; | ||||||
|  | 	free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery); | ||||||
|  | 	free_irq(gpio_to_irq(GPIO_LPC_INT), chip); | ||||||
|  | 	power_supply_unregister(&n516_battery); | ||||||
|  | 	input_unregister_device(chip->input); | ||||||
|  | 	gpio_free(GPIO_CHARG_STAT_N); | ||||||
|  | 	gpio_free(GPIO_LPC_INT); | ||||||
|  | 	i2c_set_clientdata(client, NULL); | ||||||
|  | 	kfree(chip); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #if CONFIG_PM | ||||||
|  | static int n516_lpc_suspend(struct i2c_client *client, pm_message_t msg) | ||||||
|  | { | ||||||
|  | 	if (!the_lpc->can_sleep) | ||||||
|  | 		return -EBUSY; | ||||||
|  |  | ||||||
|  | 	if (device_may_wakeup(&client->dev)) | ||||||
|  | 		enable_irq_wake(gpio_to_irq(GPIO_LPC_INT)); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int n516_lpc_resume(struct i2c_client *client) | ||||||
|  | { | ||||||
|  | 	if (device_may_wakeup(&client->dev)) | ||||||
|  | 		disable_irq_wake(gpio_to_irq(GPIO_LPC_INT)); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | #define n516_lpc_suspend NULL | ||||||
|  | #define n516_lpc_resume NULL | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static struct i2c_driver n516_lpc_driver = { | ||||||
|  | 	.class		= I2C_CLASS_HWMON, | ||||||
|  | 	.driver		= { | ||||||
|  | 		.name	= "n516-keys", | ||||||
|  | 		.owner	= THIS_MODULE, | ||||||
|  | 	}, | ||||||
|  | 	.probe		= n516_lpc_probe, | ||||||
|  | 	.remove		= __devexit_p(n516_lpc_remove), | ||||||
|  | 	.detect		= n516_lpc_detect, | ||||||
|  | 	.id_table	= n516_lpc_i2c_ids, | ||||||
|  | 	.address_data	= &addr_data, | ||||||
|  | 	.suspend	= n516_lpc_suspend, | ||||||
|  | 	.resume		= n516_lpc_resume, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int n516_lpc_init(void) | ||||||
|  | { | ||||||
|  | 	return i2c_add_driver(&n516_lpc_driver); | ||||||
|  | } | ||||||
|  | module_init(n516_lpc_init); | ||||||
|  |  | ||||||
|  | static void n516_lpc_exit(void) | ||||||
|  | { | ||||||
|  | 	i2c_del_driver(&n516_lpc_driver); | ||||||
|  | } | ||||||
|  | module_exit(n516_lpc_exit); | ||||||
|  |  | ||||||
|  | MODULE_AUTHOR("Yauhen Kharuzhy"); | ||||||
|  | MODULE_LICENSE("GPL"); | ||||||
|  | MODULE_DESCRIPTION("Keys and power controller driver for N516"); | ||||||
|  | MODULE_ALIAS("platform:n516-keys"); | ||||||
							
								
								
									
										1229
									
								
								target/linux/xburst/files-2.6.32/drivers/video/metronomefb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1229
									
								
								target/linux/xburst/files-2.6.32/drivers/video/metronomefb.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										67
									
								
								target/linux/xburst/files-2.6.32/include/video/metronomefb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								target/linux/xburst/files-2.6.32/include/video/metronomefb.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | /* | ||||||
|  |  * metronomefb.h - definitions for the metronome framebuffer driver | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2008 by Jaya Kumar | ||||||
|  |  * | ||||||
|  |  * This file is subject to the terms and conditions of the GNU General Public | ||||||
|  |  * License. See the file COPYING in the main directory of this archive for | ||||||
|  |  * more details. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _LINUX_METRONOMEFB_H_ | ||||||
|  | #define _LINUX_METRONOMEFB_H_ | ||||||
|  |  | ||||||
|  | /* command structure used by metronome controller */ | ||||||
|  | struct metromem_cmd { | ||||||
|  | 	u16 opcode; | ||||||
|  | 	u16 args[((64-2)/2)]; | ||||||
|  | 	u16 csum; | ||||||
|  | } __attribute__(packed); | ||||||
|  |  | ||||||
|  | /* struct used by metronome. board specific stuff comes from *board */ | ||||||
|  | struct metronomefb_par { | ||||||
|  | 	struct metromem_cmd *metromem_cmd; | ||||||
|  | 	unsigned char *metromem_wfm; | ||||||
|  | 	unsigned char *metromem_img; | ||||||
|  | 	u16 *metromem_img_csum; | ||||||
|  | 	u16 *csum_table; | ||||||
|  | 	dma_addr_t metromem_dma; | ||||||
|  | 	const struct firmware *firmware; | ||||||
|  | 	struct fb_info *info; | ||||||
|  | 	struct metronome_board *board; | ||||||
|  | 	struct platform_device *pdev; | ||||||
|  | 	wait_queue_head_t waitq; | ||||||
|  | 	u8 frame_count; | ||||||
|  | 	int extra_size; | ||||||
|  | 	int current_wf_mode; | ||||||
|  | 	int current_wf_temp; | ||||||
|  | 	unsigned int manual_refresh_threshold; | ||||||
|  | 	unsigned int partial_autorefresh_interval; | ||||||
|  | 	int dt; | ||||||
|  | 	u32 *fxbuckets; | ||||||
|  | 	u32 *fybuckets; | ||||||
|  | 	struct mutex lock; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define METRONOME_POWER_OFF	0 | ||||||
|  | #define METRONOME_POWER_ON	1 | ||||||
|  |  | ||||||
|  | /* board specific routines and data */ | ||||||
|  | struct metronome_board { | ||||||
|  | 	struct module *owner; /* the platform device */ | ||||||
|  | 	void (*power_ctl)(struct metronomefb_par *, int); | ||||||
|  | 	void (*set_rst)(struct metronomefb_par *, int); | ||||||
|  | 	void (*set_stdby)(struct metronomefb_par *, int); | ||||||
|  | 	int (*get_err)(struct metronomefb_par *); | ||||||
|  | 	int (*get_rdy)(struct metronomefb_par *); | ||||||
|  | 	void (*cleanup)(struct metronomefb_par *); | ||||||
|  | 	int (*met_wait_event)(struct metronomefb_par *); | ||||||
|  | 	int (*met_wait_event_intr)(struct metronomefb_par *); | ||||||
|  | 	int (*setup_irq)(struct fb_info *); | ||||||
|  | 	int (*setup_fb)(struct metronomefb_par *); | ||||||
|  | 	int (*setup_io)(struct metronomefb_par *); | ||||||
|  | 	int (*get_panel_type)(void); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										25
									
								
								target/linux/xburst/n516/config-2.6.32
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								target/linux/xburst/n516/config-2.6.32
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | CONFIG_FB_DEFERRED_IO=y | ||||||
|  | CONFIG_FB_JZ4740=y | ||||||
|  | CONFIG_FB_METRONOME=m | ||||||
|  | CONFIG_FB_SYS_FOPS=m | ||||||
|  | # CONFIG_FRAMEBUFFER_CONSOLE is not set | ||||||
|  | CONFIG_HWMON=y | ||||||
|  | # CONFIG_HWMON_DEBUG_CHIP is not set | ||||||
|  | CONFIG_I2C=y | ||||||
|  | CONFIG_I2C_ALGOBIT=y | ||||||
|  | CONFIG_I2C_BOARDINFO=y | ||||||
|  | CONFIG_I2C_GPIO=y | ||||||
|  | CONFIG_JZ4740_N516=y | ||||||
|  | # CONFIG_KEYBOARD_GPIO is not set | ||||||
|  | CONFIG_LEDS_GPIO=y | ||||||
|  | CONFIG_MTD_CMDLINE_PARTS=y | ||||||
|  | CONFIG_N516_LPC=y | ||||||
|  | CONFIG_NEW_LEDS=y | ||||||
|  | # CONFIG_REGULATOR_FIXED_VOLTAGE is not set | ||||||
|  | # CONFIG_REGULATOR_LP3971 is not set | ||||||
|  | # CONFIG_REGULATOR_MAX1586 is not set | ||||||
|  | # CONFIG_REGULATOR_TPS65023 is not set | ||||||
|  | # CONFIG_REGULATOR_TPS6507X is not set | ||||||
|  | CONFIG_SENSORS_LM75=y | ||||||
|  | # CONFIG_USB_ARCH_HAS_HCD is not set | ||||||
|  | # CONFIG_USB_ARCH_HAS_OHCI is not set | ||||||
							
								
								
									
										2
									
								
								target/linux/xburst/n516/target.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								target/linux/xburst/n516/target.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | BOARDNAME:=Hanvon N516 e-book reader | ||||||
|  | #DEFAULT_PACKAGES += uboot-xburst-n516 | ||||||
| @@ -10,10 +10,18 @@ Subject: [PATCH] /opt/Projects/openwrt/target/linux/xburst/patches-2.6.31/800-n5 | |||||||
| 
 | 
 | ||||||
| --- a/drivers/i2c/chips/Kconfig
 | --- a/drivers/i2c/chips/Kconfig
 | ||||||
| +++ b/drivers/i2c/chips/Kconfig
 | +++ b/drivers/i2c/chips/Kconfig
 | ||||||
| @@ -26,4 +26,13 @@ config SENSORS_TSL2550
 | @@ -26,4 +26,21 @@ config SENSORS_TSL2550
 | ||||||
|  	  This driver can also be built as a module.  If so, the module |  	  This driver can also be built as a module.  If so, the module | ||||||
|  	  will be called tsl2550. |  	  will be called tsl2550. | ||||||
|   |   | ||||||
|  | +config N516_LPC
 | ||||||
|  | +	tristate "N516 keys & power controller"
 | ||||||
|  | +	depends on I2C
 | ||||||
|  | +	depends on INPUT
 | ||||||
|  | +	depends on POWER_SUPPLY
 | ||||||
|  | +	help
 | ||||||
|  | +	  N516 keyboard & power controller driver
 | ||||||
|  | +
 | ||||||
| +config N526_LPC
 | +config N526_LPC
 | ||||||
| +	tristate "N526 LPC934 coprocessor"
 | +	tristate "N526 LPC934 coprocessor"
 | ||||||
| +	depends on JZ4740_N526
 | +	depends on JZ4740_N526
 | ||||||
| @@ -26,10 +34,11 @@ Subject: [PATCH] /opt/Projects/openwrt/target/linux/xburst/patches-2.6.31/800-n5 | |||||||
|  endmenu |  endmenu | ||||||
| --- a/drivers/i2c/chips/Makefile
 | --- a/drivers/i2c/chips/Makefile
 | ||||||
| +++ b/drivers/i2c/chips/Makefile
 | +++ b/drivers/i2c/chips/Makefile
 | ||||||
| @@ -12,6 +12,7 @@
 | @@ -12,6 +12,8 @@
 | ||||||
|   |   | ||||||
|  obj-$(CONFIG_DS1682)		+= ds1682.o |  obj-$(CONFIG_DS1682)		+= ds1682.o | ||||||
|  obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o |  obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o | ||||||
|  | +obj-$(CONFIG_N516_LPC)		+= n516-lpc.o
 | ||||||
| +obj-$(CONFIG_N526_LPC)		+= n526-lpc.o
 | +obj-$(CONFIG_N526_LPC)		+= n526-lpc.o
 | ||||||
|   |   | ||||||
|  ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) |  ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) | ||||||
		Reference in New Issue
	
	Block a user
	 Lars-Peter Clausen
					Lars-Peter Clausen