diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2008-10-09 21:33:02 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-10-09 21:33:02 +0100 |
commit | 3f30a09a612bac2b531a206c2a58a292dd7ff182 (patch) | |
tree | 62741c2f78aeb3009c66dbcf014ebff2e034e597 /drivers/video | |
parent | 9e165acf1b9e37af7c0fa39399b43d0bd8600039 (diff) | |
parent | fda50a1c49ad7483eaa29a268d560422c413933f (diff) |
Merge branch 'pxa-all' into devel
Conflicts:
arch/arm/mach-pxa/Kconfig
arch/arm/mach-pxa/corgi.c
arch/arm/mach-pxa/include/mach/hardware.h
arch/arm/mach-pxa/spitz.c
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 31 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/am200epd.c | 295 | ||||
-rw-r--r-- | drivers/video/backlight/Kconfig | 15 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/backlight/corgi_lcd.c | 641 | ||||
-rw-r--r-- | drivers/video/backlight/lcd.c | 18 | ||||
-rw-r--r-- | drivers/video/backlight/tdo24m.c | 396 | ||||
-rw-r--r-- | drivers/video/fbmem.c | 1 | ||||
-rw-r--r-- | drivers/video/metronomefb.c | 288 |
10 files changed, 1237 insertions, 451 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 70d135e0cc4..d85a74c64b5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -172,11 +172,6 @@ config FB_DEFERRED_IO bool depends on FB -config FB_METRONOME - tristate - depends on FB - depends on FB_DEFERRED_IO - config FB_HECUBA tristate depends on FB @@ -1974,19 +1969,6 @@ config FB_XILINX framebuffer. ML300 carries a 640*480 LCD display on the board, ML403 uses a standard DB15 VGA connector. -config FB_AM200EPD - tristate "AM-200 E-Ink EPD devkit support" - depends on FB && ARCH_PXA && MMU - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO - select FB_METRONOME - help - This enables support for the Metronome display controller used on - the E-Ink AM-200 EPD devkit. - config FB_COBALT tristate "Cobalt server LCD frame buffer support" depends on FB && MIPS_COBALT @@ -2041,6 +2023,19 @@ config XEN_FBDEV_FRONTEND frame buffer driver. It communicates with a back-end in another domain. +config FB_METRONOME + tristate "E-Ink Metronome/8track controller support" + depends on FB + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + help + This driver implements support for the E-Ink Metronome + controller. The pre-release name for this device was 8track + and could also have been called by some vendors as PVI-nnnn. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a6b55297a7f..ad0330bf9be 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o # Hardware specific drivers go first obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o -obj-$(CONFIG_FB_AM200EPD) += am200epd.o obj-$(CONFIG_FB_ARC) += arcfb.o obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o diff --git a/drivers/video/am200epd.c b/drivers/video/am200epd.c deleted file mode 100644 index 0c35b8b0160..00000000000 --- a/drivers/video/am200epd.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * linux/drivers/video/am200epd.c -- Platform device for AM200 EPD kit - * - * Copyright (C) 2008, 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. - * - * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. - * - * This work was made possible by help and equipment support from E-Ink - * Corporation. http://support.eink.com/community - * - * This driver is written to be used with the Metronome display controller. - * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600 - * Vizplex EPD on a Gumstix board using the Lyre interface board. - * - */ - -#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/list.h> -#include <linux/uaccess.h> -#include <linux/irq.h> - -#include <video/metronomefb.h> - -#include <mach/pxa-regs.h> - -/* register offsets for gpio control */ -#define LED_GPIO_PIN 51 -#define STDBY_GPIO_PIN 48 -#define RST_GPIO_PIN 49 -#define RDY_GPIO_PIN 32 -#define ERR_GPIO_PIN 17 -#define PCBPWR_GPIO_PIN 16 - -#define AF_SEL_GPIO_N 0x3 -#define GAFR0_U_OFFSET(pin) ((pin - 16) * 2) -#define GAFR1_L_OFFSET(pin) ((pin - 32) * 2) -#define GAFR1_U_OFFSET(pin) ((pin - 48) * 2) -#define GPDR1_OFFSET(pin) (pin - 32) -#define GPCR1_OFFSET(pin) (pin - 32) -#define GPSR1_OFFSET(pin) (pin - 32) -#define GPCR0_OFFSET(pin) (pin) -#define GPSR0_OFFSET(pin) (pin) - -static void am200_set_gpio_output(int pin, int val) -{ - u8 index; - - index = pin >> 4; - - switch (index) { - case 1: - if (val) - GPSR0 |= (1 << GPSR0_OFFSET(pin)); - else - GPCR0 |= (1 << GPCR0_OFFSET(pin)); - break; - case 2: - break; - case 3: - if (val) - GPSR1 |= (1 << GPSR1_OFFSET(pin)); - else - GPCR1 |= (1 << GPCR1_OFFSET(pin)); - break; - default: - printk(KERN_ERR "unimplemented\n"); - } -} - -static void __devinit am200_init_gpio_pin(int pin, int dir) -{ - u8 index; - /* dir 0 is output, 1 is input - - do 2 things here: - - set gpio alternate function to standard gpio - - set gpio direction to input or output */ - - index = pin >> 4; - switch (index) { - case 1: - GAFR0_U &= ~(AF_SEL_GPIO_N << GAFR0_U_OFFSET(pin)); - - if (dir) - GPDR0 &= ~(1 << pin); - else - GPDR0 |= (1 << pin); - break; - case 2: - GAFR1_L &= ~(AF_SEL_GPIO_N << GAFR1_L_OFFSET(pin)); - - if (dir) - GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); - else - GPDR1 |= (1 << GPDR1_OFFSET(pin)); - break; - case 3: - GAFR1_U &= ~(AF_SEL_GPIO_N << GAFR1_U_OFFSET(pin)); - - if (dir) - GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); - else - GPDR1 |= (1 << GPDR1_OFFSET(pin)); - break; - default: - printk(KERN_ERR "unimplemented\n"); - } -} - -static void am200_init_gpio_regs(struct metronomefb_par *par) -{ - am200_init_gpio_pin(LED_GPIO_PIN, 0); - am200_set_gpio_output(LED_GPIO_PIN, 0); - - am200_init_gpio_pin(STDBY_GPIO_PIN, 0); - am200_set_gpio_output(STDBY_GPIO_PIN, 0); - - am200_init_gpio_pin(RST_GPIO_PIN, 0); - am200_set_gpio_output(RST_GPIO_PIN, 0); - - am200_init_gpio_pin(RDY_GPIO_PIN, 1); - - am200_init_gpio_pin(ERR_GPIO_PIN, 1); - - am200_init_gpio_pin(PCBPWR_GPIO_PIN, 0); - am200_set_gpio_output(PCBPWR_GPIO_PIN, 0); -} - -static void am200_disable_lcd_controller(struct metronomefb_par *par) -{ - LCSR = 0xffffffff; /* Clear LCD Status Register */ - LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ - - /* we reset and just wait for things to settle */ - msleep(200); -} - -static void am200_enable_lcd_controller(struct metronomefb_par *par) -{ - LCSR = 0xffffffff; - FDADR0 = par->metromem_desc_dma; - LCCR0 |= LCCR0_ENB; -} - -static void am200_init_lcdc_regs(struct metronomefb_par *par) -{ - /* here we do: - - disable the lcd controller - - setup lcd control registers - - setup dma descriptor - - reenable lcd controller - */ - - /* disable the lcd controller */ - am200_disable_lcd_controller(par); - - /* setup lcd control registers */ - LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS - | LCCR0_QDM | LCCR0_BM | LCCR0_OUM; - - LCCR1 = (par->info->var.xres/2 - 1) /* pixels per line */ - | (27 << 10) /* hsync pulse width - 1 */ - | (33 << 16) /* eol pixel count */ - | (33 << 24); /* bol pixel count */ - - LCCR2 = (par->info->var.yres - 1) /* lines per panel */ - | (24 << 10) /* vsync pulse width - 1 */ - | (2 << 16) /* eof pixel count */ - | (0 << 24); /* bof pixel count */ - - LCCR3 = 2 /* pixel clock divisor */ - | (24 << 8) /* AC Bias pin freq */ - | LCCR3_16BPP /* BPP */ - | LCCR3_PCP; /* PCP falling edge */ - -} - -static void am200_post_dma_setup(struct metronomefb_par *par) -{ - par->metromem_desc->mFDADR0 = par->metromem_desc_dma; - par->metromem_desc->mFSADR0 = par->metromem_dma; - par->metromem_desc->mFIDR0 = 0; - par->metromem_desc->mLDCMD0 = par->info->var.xres - * par->info->var.yres; - am200_enable_lcd_controller(par); -} - -static void am200_free_irq(struct fb_info *info) -{ - free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); -} - -static irqreturn_t am200_handle_irq(int irq, void *dev_id) -{ - struct fb_info *info = dev_id; - struct metronomefb_par *par = info->par; - - wake_up_interruptible(&par->waitq); - return IRQ_HANDLED; -} - -static int am200_setup_irq(struct fb_info *info) -{ - int retval; - - retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), am200_handle_irq, - IRQF_DISABLED, "AM200", info); - if (retval) { - printk(KERN_ERR "am200epd: request_irq failed: %d\n", retval); - return retval; - } - - return set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQ_TYPE_EDGE_FALLING); -} - -static void am200_set_rst(struct metronomefb_par *par, int state) -{ - am200_set_gpio_output(RST_GPIO_PIN, state); -} - -static void am200_set_stdby(struct metronomefb_par *par, int state) -{ - am200_set_gpio_output(STDBY_GPIO_PIN, state); -} - -static int am200_wait_event(struct metronomefb_par *par) -{ - return wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); -} - -static int am200_wait_event_intr(struct metronomefb_par *par) -{ - return wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ); -} - -static struct metronome_board am200_board = { - .owner = THIS_MODULE, - .free_irq = am200_free_irq, - .setup_irq = am200_setup_irq, - .init_gpio_regs = am200_init_gpio_regs, - .init_lcdc_regs = am200_init_lcdc_regs, - .post_dma_setup = am200_post_dma_setup, - .set_rst = am200_set_rst, - .set_stdby = am200_set_stdby, - .met_wait_event = am200_wait_event, - .met_wait_event_intr = am200_wait_event_intr, -}; - -static struct platform_device *am200_device; - -static int __init am200_init(void) -{ - int ret; - - /* request our platform independent driver */ - request_module("metronomefb"); - - am200_device = platform_device_alloc("metronomefb", -1); - if (!am200_device) - return -ENOMEM; - - platform_device_add_data(am200_device, &am200_board, - sizeof(am200_board)); - - /* this _add binds metronomefb to am200. metronomefb refcounts am200 */ - ret = platform_device_add(am200_device); - - if (ret) - platform_device_put(am200_device); - - return ret; -} - -static void __exit am200_exit(void) -{ - platform_device_unregister(am200_device); -} - -module_init(am200_init); -module_exit(am200_exit); - -MODULE_DESCRIPTION("board driver for am200 metronome epd kit"); -MODULE_AUTHOR("Jaya Kumar"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 452b770d8cc..c72a1356295 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -24,6 +24,13 @@ config LCD_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +config LCD_CORGI + tristate "LCD Panel support for SHARP corgi/spitz model" + depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL + help + Say y here to support the LCD panels usually found on SHARP + corgi (C7x0) and spitz (Cxx00) models. + config LCD_LTV350QV tristate "Samsung LTV350QV LCD Panel" depends on LCD_CLASS_DEVICE && SPI_MASTER @@ -44,6 +51,14 @@ config LCD_ILI9320 If you have a panel based on the ILI9320 controller chip then say y to include a power driver for it. +config LCD_TDO24M + tristate "Toppoly TDO24M LCD Panels support" + depends on LCD_CLASS_DEVICE && SPI_MASTER + default n + help + If you have a Toppoly TDO24M series LCD panel, say y here to + include the support for it. + config LCD_VGG2432A4 tristate "VGG2432A4 LCM device support" depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b405aace803..3ec551eb472 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,10 +1,12 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o +obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c new file mode 100644 index 00000000000..2afd47eefe7 --- /dev/null +++ b/drivers/video/backlight/corgi_lcd.c @@ -0,0 +1,641 @@ +/* + * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models) + * + * Copyright (c) 2004-2006 Richard Purdie + * + * Based on Sharp's 2.4 Backlight Driver + * + * Copyright (c) 2008 Marvell International Ltd. + * Converted to SPI device based LCD/Backlight device driver + * by Eric Miao <eric.miao@marvell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/fb.h> +#include <linux/lcd.h> +#include <linux/spi/spi.h> +#include <linux/spi/corgi_lcd.h> +#include <asm/mach/sharpsl_param.h> + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +/* Register Addresses */ +#define RESCTL_ADRS 0x00 +#define PHACTRL_ADRS 0x01 +#define DUTYCTRL_ADRS 0x02 +#define POWERREG0_ADRS 0x03 +#define POWERREG1_ADRS 0x04 +#define GPOR3_ADRS 0x05 +#define PICTRL_ADRS 0x06 +#define POLCTRL_ADRS 0x07 + +/* Register Bit Definitions */ +#define RESCTL_QVGA 0x01 +#define RESCTL_VGA 0x00 + +#define POWER1_VW_ON 0x01 /* VW Supply FET ON */ +#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */ +#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */ + +#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */ +#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */ +#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */ + +#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */ +#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */ +#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */ +#define POWER0_COM_ON 0x08 /* COM Power Supply ON */ +#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */ + +#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */ +#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */ +#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */ + +#define PICTRL_INIT_STATE 0x01 +#define PICTRL_INIOFF 0x02 +#define PICTRL_POWER_DOWN 0x04 +#define PICTRL_COM_SIGNAL_OFF 0x08 +#define PICTRL_DAC_SIGNAL_OFF 0x10 + +#define POLCTRL_SYNC_POL_FALL 0x01 +#define POLCTRL_EN_POL_FALL 0x02 +#define POLCTRL_DATA_POL_FALL 0x04 +#define POLCTRL_SYNC_ACT_H 0x08 +#define POLCTRL_EN_ACT_L 0x10 + +#define POLCTRL_SYNC_POL_RISE 0x00 +#define POLCTRL_EN_POL_RISE 0x00 +#define POLCTRL_DATA_POL_RISE 0x00 +#define POLCTRL_SYNC_ACT_L 0x00 +#define POLCTRL_EN_ACT_H 0x00 + +#define PHACTRL_PHASE_MANUAL 0x01 +#define DEFAULT_PHAD_QVGA (9) +#define DEFAULT_COMADJ (125) + +struct corgi_lcd { + struct spi_device *spi_dev; + struct lcd_device *lcd_dev; + struct backlight_device *bl_dev; + + int limit_mask; + int intensity; + int power; + int mode; + char buf[2]; + + int gpio_backlight_on; + int gpio_backlight_cont; + int gpio_backlight_cont_inverted; + + void (*kick_battery)(void); +}; + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); + +static struct corgi_lcd *the_corgi_lcd; +static unsigned long corgibl_flags; +#define CORGIBL_SUSPENDED 0x01 +#define CORGIBL_BATTLOW 0x02 + +/* + * This is only a psuedo I2C interface. We can't use the standard kernel + * routines as the interface is write only. We just assume the data is acked... + */ +static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data) +{ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data); + udelay(10); +} + +static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data) +{ + lcdtg_ssp_i2c_send(lcd, data); + lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, data); +} + +static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base); +} + +static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); +} + +static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd, + uint8_t base, uint8_t data) +{ + int i; + for (i = 0; i < 8; i++) { + if (data & 0x80) + lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT); + else + lcdtg_i2c_send_bit(lcd, base); + data <<= 1; + } +} + +static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_i2c_send_bit(lcd, base); +} + +static void lcdtg_set_common_voltage(struct corgi_lcd *lcd, + uint8_t base_data, uint8_t data) +{ + /* Set Common Voltage to M62332FP via I2C */ + lcdtg_i2c_send_start(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x9c); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x00); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, data); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_stop(lcd, base_data); +} + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data) +{ + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .cs_change = 1, + .tx_buf = lcd->buf, + }; + + lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi_dev, &msg); +} + +/* Set Phase Adjust */ +static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode) +{ + int adj; + + switch(mode) { + case CORGI_LCD_MODE_VGA: + /* Setting for VGA */ + adj = sharpsl_param.phadadj; + adj = (adj < 0) ? PHACTRL_PHASE_MANUAL : + PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1); + break; + case CORGI_LCD_MODE_QVGA: + default: + /* Setting for QVGA */ + adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL; + break; + } + + corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj); +} + +static void corgi_lcd_power_on(struct corgi_lcd *lcd) +{ + int comadj; + + /* Initialize Internal Logic & Port */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_POWER_DOWN | PICTRL_INIOFF | + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF | + PICTRL_DAC_SIGNAL_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); + + /* VDD(+8V), SVSS(-4V) ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + mdelay(3); + + /* DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* INIB = H, INI = L */ + /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF); + + /* Set Common Voltage */ + comadj = sharpsl_param.comadj; + if (comadj < 0) + comadj = DEFAULT_COMADJ; + + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_OFF, comadj); + + /* VCC5 ON, DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_ON); + + /* GVSS(-8V) ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + mdelay(2); + + /* COM SIGNAL ON (PICTL[3] = L) */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE); + + /* COM ON, DAC ON, VCC5_ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_ON | POWER0_VCC5_ON); + + /* VW ON, GVSS ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* Signals output enable */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0); + + /* Set Phase Adjust */ + lcdtg_set_phadadj(lcd, lcd->mode); + + /* Initialize for Input Signals from ATI */ + corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS, + POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE | + POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L | + POLCTRL_EN_ACT_H); + udelay(1000); + + switch (lcd->mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } +} + +static void corgi_lcd_power_off(struct corgi_lcd *lcd) +{ + /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */ + msleep(34); + + /* (1)VW OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* (2)COM OFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF); + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON); + + /* (3)Set Common Voltage Bias 0V */ + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_ON, 0); + + /* (4)GVSS OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + + /* (5)VCC5 OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (6)Set PDWN, INIOFF, DACOFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF | + PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF); + + /* (7)DAC OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (8)VDD OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); +} + +static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + int mode = CORGI_LCD_MODE_QVGA; + + if (m->xres == 640 || m->xres == 480) + mode = CORGI_LCD_MODE_VGA; + + if (lcd->mode == mode) + return 0; + + lcdtg_set_phadadj(lcd, mode); + + switch (mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } + + lcd->mode = mode; + return 0; +} + +static int corgi_lcd_set_power(struct lcd_device *ld, int power) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + corgi_lcd_power_on(lcd); + + if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + corgi_lcd_power_off(lcd); + + lcd->power = power; + return 0; +} + +static int corgi_lcd_get_power(struct lcd_device *ld) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + return lcd->power; +} + +static struct lcd_ops corgi_lcd_ops = { + .get_power = corgi_lcd_get_power, + .set_power = corgi_lcd_set_power, + .set_mode = corgi_lcd_set_mode, +}; + +static int corgi_bl_get_intensity(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + + return lcd->intensity; +} + +static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) +{ + int cont; + + if (intensity > 0x10) + intensity += 0x10; + + corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity); + + /* Bit 5 via GPIO_BACKLIGHT_CONT */ + cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted; + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_set_value(lcd->gpio_backlight_cont, cont); + + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_set_value(lcd->gpio_backlight_on, intensity); + + if (lcd->kick_battery) + lcd->kick_battery(); + + lcd->intensity = intensity; + return 0; +} + +static int corgi_bl_update_status(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + if (corgibl_flags & CORGIBL_SUSPENDED) + intensity = 0; + if (corgibl_flags & CORGIBL_BATTLOW) + intensity &= lcd->limit_mask; + + return corgi_bl_set_intensity(lcd, intensity); +} + +void corgibl_limit_intensity(int limit) +{ + if (limit) + corgibl_flags |= CORGIBL_BATTLOW; + else + corgibl_flags &= ~CORGIBL_BATTLOW; + + backlight_update_status(the_corgi_lcd->bl_dev); +} +EXPORT_SYMBOL(corgibl_limit_intensity); + +static struct backlight_ops corgi_bl_ops = { + .get_brightness = corgi_bl_get_intensity, + .update_status = corgi_bl_update_status, +}; + +#ifdef CONFIG_PM +static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgibl_flags |= CORGIBL_SUSPENDED; + corgi_bl_set_intensity(lcd, 0); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + return 0; +} + +static int corgi_lcd_resume(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgibl_flags &= ~CORGIBL_SUSPENDED; + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + return 0; +} +#else +#define corgi_lcd_suspend NULL +#define corgi_lcd_resume NULL +#endif + +static int setup_gpio_backlight(struct corgi_lcd *lcd, + struct corgi_lcd_platform_data *pdata) +{ + struct spi_device *spi = lcd->spi_dev; + int err; + + lcd->gpio_backlight_on = -1; + lcd->gpio_backlight_cont = -1; + + if (gpio_is_valid(pdata->gpio_backlight_on)) { + err = gpio_request(pdata->gpio_backlight_on, "BL_ON"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_on\n", pdata->gpio_backlight_on); + return err; + } + + lcd->gpio_backlight_on = pdata->gpio_backlight_on; + gpio_direction_output(lcd->gpio_backlight_on, 0); + } + + if (gpio_is_valid(pdata->gpio_backlight_cont)) { + err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_cont\n", pdata->gpio_backlight_cont); + goto err_free_backlight_on; + } + + lcd->gpio_backlight_cont = pdata->gpio_backlight_cont; + + /* spitz and akita use both GPIOs for backlight, and + * have inverted polarity of GPIO_BACKLIGHT_CONT + */ + if (gpio_is_valid(lcd->gpio_backlight_on)) { + lcd->gpio_backlight_cont_inverted = 1; + gpio_direction_output(lcd->gpio_backlight_cont, 1); + } else { + lcd->gpio_backlight_cont_inverted = 0; + gpio_direction_output(lcd->gpio_backlight_cont, 0); + } + } + return 0; + +err_free_backlight_on: + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + return err; +} + +static int __devinit corgi_lcd_probe(struct spi_device *spi) +{ + struct corgi_lcd_platform_data *pdata = spi->dev.platform_data; + struct corgi_lcd *lcd; + int ret = 0; + + if (pdata == NULL) { + dev_err(&spi->dev, "platform data not available\n"); + return -EINVAL; + } + + lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL); + if (!lcd) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + lcd->spi_dev = spi; + + lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev, + lcd, &corgi_lcd_ops); + if (IS_ERR(lcd->lcd_dev)) { + ret = PTR_ERR(lcd->lcd_dev); + goto err_free_lcd; + } + lcd->power = FB_BLANK_POWERDOWN; + lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA; + + lcd->bl_dev = backlight_device_register("corgi_bl", &spi->dev, + lcd, &corgi_bl_ops); + if (IS_ERR(lcd->bl_dev)) { + ret = PTR_ERR(lcd->bl_dev); + goto err_unregister_lcd; + } + lcd->bl_dev->props.max_brightness = pdata->max_intensity; + lcd->bl_dev->props.brightness = pdata->default_intensity; + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + + ret = setup_gpio_backlight(lcd, pdata); + if (ret) + goto err_unregister_bl; + + lcd->kick_battery = pdata->kick_battery; + + dev_set_drvdata(&spi->dev, lcd); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + + lcd->limit_mask = pdata->limit_mask; + the_corgi_lcd = lcd; + return 0; + +err_unregister_bl: + backlight_device_unregister(lcd->bl_dev); +err_unregister_lcd: + lcd_device_unregister(lcd->lcd_dev); +err_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit corgi_lcd_remove(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + lcd->bl_dev->props.brightness = 0; + backlight_update_status(lcd->bl_dev); + backlight_device_unregister(lcd->bl_dev); + + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_free(lcd->gpio_backlight_cont); + + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->lcd_dev); + kfree(lcd); + + return 0; +} + +static struct spi_driver corgi_lcd_driver = { + .driver = { + .name = "corgi-lcd", + .owner = THIS_MODULE, + }, + .probe = corgi_lcd_probe, + .remove = __devexit_p(corgi_lcd_remove), + .suspend = corgi_lcd_suspend, + .resume = corgi_lcd_resume, +}; + +static int __init corgi_lcd_init(void) +{ + return spi_register_driver(&corgi_lcd_driver); +} +module_init(corgi_lcd_init); + +static void __exit corgi_lcd_exit(void) +{ + spi_unregister_driver(&corgi_lcd_driver); +} +module_exit(corgi_lcd_exit); + +MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_LICENSE("GPL"); |