diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-05-31 12:59:19 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-05-31 12:59:19 +0900 |
commit | 8fa76f7e61ef4e5bc97207143ea4e198b22487bc (patch) | |
tree | 266c42b6687e68e4febb72d8c031e5facd899a1c /drivers/video | |
parent | a41a7b91772da2c77ac0da74285fd8ebd86a85ba (diff) | |
parent | 67a3e12b05e055c0415c556a315a3d3eb637e29e (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/video')
31 files changed, 3048 insertions, 314 deletions
diff --git a/drivers/video/arcfb.c b/drivers/video/arcfb.c index 8d406fb689c..f3d7440f007 100644 --- a/drivers/video/arcfb.c +++ b/drivers/video/arcfb.c @@ -80,7 +80,7 @@ struct arcfb_par { spinlock_t lock; }; -static struct fb_fix_screeninfo arcfb_fix __initdata = { +static struct fb_fix_screeninfo arcfb_fix __devinitdata = { .id = "arcfb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_MONO01, @@ -90,7 +90,7 @@ static struct fb_fix_screeninfo arcfb_fix __initdata = { .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo arcfb_var __initdata = { +static struct fb_var_screeninfo arcfb_var __devinitdata = { .xres = 128, .yres = 64, .xres_virtual = 128, @@ -588,7 +588,7 @@ err: return retval; } -static int arcfb_remove(struct platform_device *dev) +static int __devexit arcfb_remove(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); @@ -602,7 +602,7 @@ static int arcfb_remove(struct platform_device *dev) static struct platform_driver arcfb_driver = { .probe = arcfb_probe, - .remove = arcfb_remove, + .remove = __devexit_p(arcfb_remove), .driver = { .name = "arcfb", }, diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 29d72851f85..f8d69ad3683 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -1820,10 +1820,6 @@ struct atyclk { #define ATYIO_FEATW 0x41545903 /* ATY\03 */ #endif -#ifndef FBIO_WAITFORVSYNC -#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) -#endif - static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) { struct atyfb_par *par = (struct atyfb_par *) info->par; diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index 68d2518fada..38ffc3fbcbe 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -222,6 +222,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev) data->port = __check_device(pdata, name); if (data->port < 0) { dev_err(&pdev->dev, "wrong platform data is assigned"); + kfree(data); return -EINVAL; } @@ -266,6 +267,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev) backlight_update_status(bl); return 0; out: + backlight_device_unregister(bl); kfree(data); return ret; } diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index c025c84601b..e54a337227e 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -8,12 +8,13 @@ menuconfig BACKLIGHT_LCD_SUPPORT Enable this to be able to choose the drivers for controlling the backlight and the LCD panel on some platforms, for example on PDAs. +if BACKLIGHT_LCD_SUPPORT + # # LCD # config LCD_CLASS_DEVICE tristate "Lowlevel LCD controls" - depends on BACKLIGHT_LCD_SUPPORT default m help This framework adds support for low-level control of LCD. @@ -24,31 +25,32 @@ 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. +if LCD_CLASS_DEVICE + config LCD_CORGI tristate "LCD Panel support for SHARP corgi/spitz model" - depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL + depends on 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_L4F00242T03 tristate "Epson L4F00242T03 LCD" - depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO + depends on SPI_MASTER && GENERIC_GPIO help SPI driver for Epson L4F00242T03. This provides basic support for init and powering the LCD up/down through a sysfs interface. config LCD_LMS283GF05 tristate "Samsung LMS283GF05 LCD" - depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO + depends on SPI_MASTER && GENERIC_GPIO help SPI driver for Samsung LMS283GF05. This provides basic support for powering the LCD up/down through a sysfs interface. config LCD_LTV350QV tristate "Samsung LTV350QV LCD Panel" - depends on LCD_CLASS_DEVICE && SPI_MASTER - default n + depends on SPI_MASTER help If you have a Samsung LTV350QV LCD panel, say y to include a power control driver for it. The panel starts up in power @@ -59,60 +61,61 @@ config LCD_LTV350QV config LCD_ILI9320 tristate - depends on LCD_CLASS_DEVICE && BACKLIGHT_LCD_SUPPORT - default n help 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 and TDO35S LCD Panels support" - depends on LCD_CLASS_DEVICE && SPI_MASTER - default n + depends on SPI_MASTER help If you have a Toppoly TDO24M/TDO35S 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 + depends on SPI_MASTER select LCD_ILI9320 - default n help If you have a VGG2432A4 panel based on the ILI9320 controller chip then say y to include a power driver for it. config LCD_PLATFORM tristate "Platform LCD controls" - depends on LCD_CLASS_DEVICE help This driver provides a platform-device registered LCD power control interface. config LCD_TOSA tristate "Sharp SL-6000 LCD Driver" - depends on LCD_CLASS_DEVICE && SPI - depends on MACH_TOSA - default n + depends on SPI && MACH_TOSA help If you have an Sharp SL-6000 Zaurus say Y to enable a driver for its LCD. config LCD_HP700 tristate "HP Jornada 700 series LCD Driver" - depends on LCD_CLASS_DEVICE depends on SA1100_JORNADA720_SSP && !PREEMPT default y help If you have an HP Jornada 700 series handheld (710/720/728) say Y to enable LCD control driver. +config LCD_S6E63M0 + tristate "S6E63M0 AMOLED LCD Driver" + depends on SPI && BACKLIGHT_CLASS_DEVICE + default n + help + If you have an S6E63M0 LCD Panel, say Y to enable its + LCD control driver. + +endif # LCD_CLASS_DEVICE + # # Backlight # config BACKLIGHT_CLASS_DEVICE tristate "Lowlevel Backlight controls" - depends on BACKLIGHT_LCD_SUPPORT default m help This framework adds support for low-level control of the LCD @@ -121,9 +124,11 @@ config BACKLIGHT_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +if BACKLIGHT_CLASS_DEVICE + config BACKLIGHT_ATMEL_LCDC bool "Atmel LCDC Contrast-as-Backlight control" - depends on BACKLIGHT_CLASS_DEVICE && FB_ATMEL + depends on FB_ATMEL default y if MACH_SAM9261EK || MACH_SAM9G10EK || MACH_SAM9263EK help This provides a backlight control internal to the Atmel LCDC @@ -136,8 +141,7 @@ config BACKLIGHT_ATMEL_LCDC config BACKLIGHT_ATMEL_PWM tristate "Atmel PWM backlight control" - depends on BACKLIGHT_CLASS_DEVICE && ATMEL_PWM - default n + depends on ATMEL_PWM help Say Y here if you want to use the PWM peripheral in Atmel AT91 and AVR32 devices. This driver will need additional platform data to know @@ -146,9 +150,18 @@ config BACKLIGHT_ATMEL_PWM To compile this driver as a module, choose M here: the module will be called atmel-pwm-bl. +config BACKLIGHT_EP93XX + tristate "Cirrus EP93xx Backlight Driver" + depends on FB_EP93XX + help + If you have a LCD backlight connected to the BRIGHT output of + the EP93xx, say Y here to enable this driver. + + To compile this driver as a module, choose M here: the module will + be called ep93xx_bl. + config BACKLIGHT_GENERIC tristate "Generic (aka Sharp Corgi) Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE default y help Say y to enable the generic platform backlight driver previously @@ -157,7 +170,7 @@ config BACKLIGHT_GENERIC config BACKLIGHT_LOCOMO tristate "Sharp LOCOMO LCD/Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && SHARP_LOCOMO + depends on SHARP_LOCOMO default y help If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to @@ -165,7 +178,7 @@ config BACKLIGHT_LOCOMO config BACKLIGHT_OMAP1 tristate "OMAP1 PWL-based LCD Backlight" - depends on BACKLIGHT_CLASS_DEVICE && ARCH_OMAP1 + depends on ARCH_OMAP1 default y help This driver controls the LCD backlight level and power for @@ -174,7 +187,7 @@ config BACKLIGHT_OMAP1 config BACKLIGHT_HP680 tristate "HP Jornada 680 Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && SH_HP6XX + depends on SH_HP6XX default y help If you have a HP Jornada 680, say y to enable the @@ -182,7 +195,6 @@ config BACKLIGHT_HP680 config BACKLIGHT_HP700 tristate "HP Jornada 700 series Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE depends on SA1100_JORNADA720_SSP && !PREEMPT default y help @@ -191,76 +203,70 @@ config BACKLIGHT_HP700 config BACKLIGHT_PROGEAR tristate "Frontpath ProGear Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && PCI && X86 - default n + depends on PCI && X86 help If you have a Frontpath ProGear say Y to enable the backlight driver. config BACKLIGHT_CARILLO_RANCH tristate "Intel Carillo Ranch Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578 - default n + depends on LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578 help If you have a Intel LE80578 (Carillo Ranch) say Y to enable the backlight driver. config BACKLIGHT_PWM tristate "Generic PWM based Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && HAVE_PWM + depends on HAVE_PWM help If you have a LCD backlight adjustable by PWM, say Y to enable this driver. config BACKLIGHT_DA903X tristate "Backlight Driver for DA9030/DA9034 using WLED" - depends on BACKLIGHT_CLASS_DEVICE && PMIC_DA903X + depends on PMIC_DA903X help If you have a LCD backlight connected to the WLED output of DA9030 or DA9034 WLED output, say Y here to enable this driver. config BACKLIGHT_MAX8925 tristate "Backlight driver for MAX8925" - depends on BACKLIGHT_CLASS_DEVICE && MFD_MAX8925 + depends on MFD_MAX8925 help If you have a LCD backlight connected to the WLED output of MAX8925 WLED output, say Y here to enable this driver. config BACKLIGHT_MBP_NVIDIA tristate "MacBook Pro Nvidia Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && X86 - default n + depends on X86 help If you have an Apple Macbook Pro with Nvidia graphics hardware say Y to enable a driver for its backlight config BACKLIGHT_TOSA tristate "Sharp SL-6000 Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && I2C - depends on MACH_TOSA && LCD_TOSA - default n + depends on I2C && MACH_TOSA && LCD_TOSA help If you have an Sharp SL-6000 Zaurus say Y to enable a driver for its backlight config BACKLIGHT_SAHARA tristate "Tabletkiosk Sahara Touch-iT Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && X86 - default n + depends on X86 help If you have a Tabletkiosk Sahara Touch-iT, say y to enable the backlight driver. config BACKLIGHT_WM831X tristate "WM831x PMIC Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && MFD_WM831X + depends on MFD_WM831X help If you have a backlight driven by the ISINK and DCDC of a WM831x PMIC say y to enable the backlight driver for it. config BACKLIGHT_ADX tristate "Avionic Design Xanthos Backlight Driver" - depends on BACKLIGHT_CLASS_DEVICE && ARCH_PXA_ADX + depends on ARCH_PXA_ADX default y help Say Y to enable the backlight driver on Avionic Design Xanthos-based @@ -268,7 +274,7 @@ config BACKLIGHT_ADX config BACKLIGHT_ADP5520 tristate "Backlight Driver for ADP5520/ADP5501 using WLED" - depends on BACKLIGHT_CLASS_DEVICE && PMIC_ADP5520 + depends on PMIC_ADP5520 help If you have a LCD backlight connected to the BST/BL_SNK output of ADP5520 or ADP5501, say Y here to enable this driver. @@ -276,9 +282,31 @@ config BACKLIGHT_ADP5520 To compile this driver as a module, choose M here: the module will be called adp5520_bl. +config BACKLIGHT_ADP8860 + tristate "Backlight Driver for ADP8860/ADP8861/ADP8863 using WLED" + depends on BACKLIGHT_CLASS_DEVICE && I2C + select NEW_LEDS + select LEDS_CLASS + help + If you have a LCD backlight connected to the ADP8860, ADP8861 or + ADP8863 say Y here to enable this driver. + + To compile this driver as a module, choose M here: the module will + be called adp8860_bl. + config BACKLIGHT_88PM860X tristate "Backlight Driver for 88PM8606 using WLED" - depends on BACKLIGHT_CLASS_DEVICE && MFD_88PM860X + depends on MFD_88PM860X help Say Y to enable the backlight driver for Marvell 88PM8606. +config BACKLIGHT_PCF50633 + tristate "Backlight driver for NXP PCF50633 MFD" + depends on BACKLIGHT_CLASS_DEVICE && MFD_PCF50633 + help + If you have a backlight driven by a NXP PCF50633 MFD, say Y here to + enable its driver. + +endif # BACKLIGHT_CLASS_DEVICE + +endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 09d1f14d625..44c0f81ad85 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -11,9 +11,11 @@ obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o +obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o +obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o @@ -30,5 +32,7 @@ obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o +obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c new file mode 100644 index 00000000000..921ca37398f --- /dev/null +++ b/drivers/video/backlight/adp8860_bl.c @@ -0,0 +1,838 @@ +/* + * Backlight driver for Analog Devices ADP8860 Backlight Devices + * + * Copyright 2009-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/leds.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include <linux/i2c/adp8860.h> +#define ADP8860_EXT_FEATURES +#define ADP8860_USE_LEDS + +#define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */ +#define ADP8860_MDCR 0x01 /* Device mode and status */ +#define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */ +#define ADP8860_INTR_EN 0x03 /* Interrupts enable */ +#define ADP8860_CFGR 0x04 /* Configuration register */ +#define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */ +#define ADP8860_BLOFF 0x06 /* Backlight off timeout */ +#define ADP8860_BLDIM 0x07 /* Backlight dim timeout */ +#define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */ +#define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */ +#define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */ +#define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */ +#define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */ +#define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */ +#define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */ +#define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */ +#define ADP8860_ISCC 0x10 /* Independent sink current control register */ +#define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */ +#define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */ +#define ADP8860_ISCF 0x13 /* Independent sink current fade register */ +#define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */ +#define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */ +#define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */ +#define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */ +#define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */ +#define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */ +#define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */ +#define ADP8860_CCFG 0x1B /* Comparator configuration */ +#define ADP8860_CCFG2 0x1C /* Second comparator configuration */ +#define ADP8860_L2_TRP 0x1D /* L2 comparator reference */ +#define ADP8860_L2_HYS 0x1E /* L2 hysteresis */ +#define ADP8860_L3_TRP 0x1F /* L3 comparator reference */ +#define ADP8860_L3_HYS 0x20 /* L3 hysteresis */ +#define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */ +#define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */ +#define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */ +#define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */ + +#define ADP8860_MANUFID 0x0 /* Analog Devices ADP8860 Manufacturer ID */ +#define ADP8861_MANUFID 0x4 /* Analog Devices ADP8861 Manufacturer ID */ +#define ADP8863_MANUFID 0x2 /* Analog Devices ADP8863 Manufacturer ID */ + +#define ADP8860_DEVID(x) ((x) & 0xF) +#define ADP8860_MANID(x) ((x) >> 4) + +/* MDCR Device mode and status */ +#define INT_CFG (1 << 6) +#define NSTBY (1 << 5) +#define DIM_EN (1 << 4) +#define GDWN_DIS (1 << 3) +#define SIS_EN (1 << 2) +#define CMP_AUTOEN (1 << 1) +#define BLEN (1 << 0) + +/* ADP8860_CCFG Main ALS comparator level enable */ +#define L3_EN (1 << 1) +#define L2_EN (1 << 0) + +#define CFGR_BLV_SHIFT 3 +#define CFGR_BLV_MASK 0x3 +#define ADP8860_FLAG_LED_MASK 0xFF + +#define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) +#define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) +#define ALS_CCFG_VAL(filt) ((0x7 & filt) << 5) + +enum { + adp8860, + adp8861, + adp8863 +}; + +struct adp8860_led { + struct led_classdev cdev; + struct work_struct work; + struct i2c_client *client; + enum led_brightness new_brightness; + int id; + int flags; +}; + +struct adp8860_bl { + struct i2c_client *client; + struct backlight_device *bl; + struct adp8860_led *led; + struct adp8860_backlight_platform_data *pdata; + struct mutex lock; + unsigned long cached_daylight_max; + int id; + int revid; + int current_brightness; + unsigned en_ambl_sens:1; + unsigned gdwn_dis:1; +}; + +static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + dev_err(&client->dev, "failed reading at 0x%02x\n", reg); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +static int adp8860_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ + struct adp8860_bl *data = i2c_get_clientdata(client); + uint8_t reg_val; + int ret; + + mutex_lock(&data->lock); + + ret = adp8860_read(client, reg, ®_val); + + if (!ret && ((reg_val & bit_mask) == 0)) { + reg_val |= bit_mask; + ret = adp8860_write(client, reg, reg_val); + } + + mutex_unlock(&data->lock); + return ret; +} + +static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ + struct adp8860_bl *data = i2c_get_clientdata(client); + uint8_t reg_val; + int ret; + + mutex_lock(&data->lock); + + ret = adp8860_read(client, reg, ®_val); + + if (!ret && (reg_val & bit_mask)) { + reg_val &= ~bit_mask; + ret = adp8860_write(client, reg, reg_val); + } + + mutex_unlock(&data->lock); + return ret; +} + +/* + * Independent sink / LED + */ +#if defined(ADP8860_USE_LEDS) +static void adp8860_led_work(struct work_struct *work) +{ + struct adp8860_led *led = container_of(work, struct adp8860_led, work); + adp8860_write(led->client, ADP8860_ISC1 - led->id + 1, + led->new_brightness >> 1); +} + +static void adp8860_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct adp8860_led *led; + + led = container_of(led_cdev, struct adp8860_led, cdev); + led->new_brightness = value; + schedule_work(&led->work); +} + +static int adp8860_led_setup(struct adp8860_led *led) +{ + struct i2c_client *client = led->client; + int ret = 0; + + ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0); + ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1)); + + if (led->id > 4) + ret |= adp8860_set_bits(client, ADP8860_ISCT1, + (led->flags & 0x3) << ((led->id - 5) * 2)); + else + ret |= adp8860_set_bits(client, ADP8860_ISCT2, + (led->flags & 0x3) << ((led->id - 1) * 2)); + + return ret; +} + +static int __devinit adp8860_led_probe(struct i2c_client *client) +{ + struct adp8860_backlight_platform_data *pdata = + client->dev.platform_data; + struct adp8860_bl *data = i2c_get_clientdata(client); + struct adp8860_led *led, *led_dat; + struct led_info *cur_led; + int ret, i; + + led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); + if (led == NULL) { + dev_err(&client->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law); + ret = adp8860_write(client, ADP8860_ISCT1, + (pdata->led_on_time & 0x3) << 6); + ret |= adp8860_write(client, ADP8860_ISCF, + FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); + + if (ret) { + dev_err(&client->dev, "failed to write\n"); + goto err_free; + } + + for (i = 0; i < pdata->num_leds; ++i) { + cur_led = &pdata->leds[i]; + led_dat = &led[i]; + + led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK; + + if (led_dat->id > 7 || led_dat->id < 1) { + dev_err(&client->dev, "Invalid LED ID %d\n", + led_dat->id); + goto err; + } + + if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { + dev_err(&client->dev, "LED %d used by Backlight\n", + led_dat->id); + goto err; + } + + led_dat->cdev.name = cur_led->name; + led_dat->cdev.default_trigger = cur_led->default_trigger; + led_dat->cdev.brightness_set = adp8860_led_set; + led_dat->cdev.brightness = LED_OFF; + led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; + led_dat->client = client; + led_dat->new_brightness = LED_OFF; + INIT_WORK(&led_dat->work, adp8860_led_work); + + ret = led_classdev_register(&client->dev, &led_dat->cdev); + if (ret) { + dev_err(&client->dev, "failed to register LED %d\n", + led_dat->id); + goto err; + } + + ret = adp8860_led_setup(led_dat); + if (ret) { + dev_err(&client->dev, "failed to write\n"); + i++; + goto err; + } + } + + data->led = led; + + return 0; + + err: + for (i = i - 1; i >= 0; --i) { + led_classdev_unregister(&led[i].cdev); + cancel_work_sync(&led[i].work); + } + + err_free: + kfree(led); + + return ret; +} + +static int __devexit adp8860_led_remove(struct i2c_client *client) +{ + struct adp8860_backlight_platform_data *pdata = + client->dev.platform_data; + struct adp8860_bl *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&data->led[i].cdev); + cancel_work_sync(&data->led[i].work); + } + + kfree(data->led); + return 0; +} +#else +static int __devinit adp8860_led_probe(struct i2c_client *client) +{ + return 0; +} + +static int __devexit adp8860_led_remove(struct i2c_client *client) +{ + return 0; +} +#endif + +static int adp8860_bl_set(struct backlight_device *bl, int brightness) +{ + struct adp8860_bl *data = bl_get_data(bl); + struct i2c_client *client = data->client; + int ret = 0; + + if (data->en_ambl_sens) { + if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) { + /* Disable Ambient Light auto adjust */ + ret |= adp8860_clr_bits(client, ADP8860_MDCR, + CMP_AUTOEN); + ret |= adp8860_write(client, ADP8860_BLMX1, brightness); + } else { + /* + * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust + * restore daylight l1 sysfs brightness + */ + ret |= adp8860_write(client, ADP8860_BLMX1, + data->cached_daylight_max); + ret |= adp8860_set_bits(client, ADP8860_MDCR, + CMP_AUTOEN); + } + } else + ret |= adp8860_write(client, ADP8860_BLMX1, brightness); + + if (data->current_brightness && brightness == 0) + ret |= adp8860_set_bits(client, + ADP8860_MDCR, DIM_EN); + else if (data->current_brightness == 0 && brightness) + ret |= adp8860_clr_bits(client, + ADP8860_MDCR, DIM_EN); + + if (!ret) + data->current_brightness = brightness; + + return ret; +} + +static int adp8860_bl_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return adp8860_bl_set(bl, brightness); +} + +static int adp8860_bl_get_brightness(struct backlight_device *bl) +{ + struct adp8860_bl *data = bl_get_data(bl); + + return data->current_brightness; +} + +static const struct backlight_ops adp8860_bl_ops = { + .update_status = adp8860_bl_update_status, + .get_brightness = adp8860_bl_get_brightness, +}; + +static int adp8860_bl_setup(struct backlight_device *bl) +{ + struct adp8860_bl *data = bl_get_data(bl); + struct i2c_client *client = data->client; + struct adp8860_backlight_platform_data *pdata = data->pdata; + int ret = 0; + + ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign); + ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max); + ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim); + + if (data->en_ambl_sens) { + data->cached_daylight_max = pdata->l1_daylight_max; + ret |= adp8860_write(client, ADP8860_BLMX2, + pdata->l2_office_max); + ret |= adp8860_write(client, ADP8860_BLDM2, + pdata->l2_office_dim); + ret |= adp8860_write(client, ADP8860_BLMX3, + pdata->l3_dark_max); + ret |= adp8860_write(client, ADP8860_BLDM3, + pdata->l3_dark_dim); + + ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip); + ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst); + ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip); + ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst); + ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN | + ALS_CCFG_VAL(pdata->abml_filt)); + } + + ret |= adp8860_write(client, ADP8860_CFGR, + BL_CFGR_VAL(pdata->bl_fade_law, 0)); + + ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in, + pdata->bl_fade_out)); + + ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY | + (data->gdwn_dis ? GDWN_DIS : 0)); + + return ret; +} + +static ssize_t adp8860_show(struct device *dev, char *buf, int reg) +{ + struct adp8860_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp8860_read(data->client, reg, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + return sprintf(buf, "%u\n", reg_val); +} + +static ssize_t adp8860_store(struct device *dev, const char *buf, + size_t count, int reg) +{ + struct adp8860_bl *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&data->lock); + adp8860_write(data->client, reg, val); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8860_show(dev, buf, ADP8860_BLMX3); +} + +static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8860_store(dev, buf, count, ADP8860_BLMX3); +} + +static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show, + adp8860_bl_l3_dark_max_store); + +static ssize_t adp8860_bl_l2_office_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8860_show(dev, buf, ADP8860_BLMX2); +} + +static ssize_t adp8860_bl_l2_office_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8860_store(dev, buf, count, ADP8860_BLMX2); +} +static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show, + adp8860_bl_l2_office_max_store); + +static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8860_show(dev, buf, ADP8860_BLMX1); +} + +static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adp8860_bl *data = dev_get_drvdata(dev); + + strict_strtoul(buf, 10, &data->cached_daylight_max); + return adp8860_store(dev, buf, count, ADP8860_BLMX1); +} +static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, + adp8860_bl_l1_daylight_max_store); + +static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8860_show(dev, buf, ADP8860_BLDM3); +} + +static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8860_store(dev, buf, count, ADP8860_BLDM3); +} +static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show, + adp8860_bl_l3_dark_dim_store); + +static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8860_show(dev, buf, ADP8860_BLDM2); +} + +static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8860_store(dev, buf, count, ADP8860_BLDM2); +} +static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show, + adp8860_bl_l2_office_dim_store); + +static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8860_show(dev, buf, ADP8860_BLDM1); +} + +static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8860_store(dev, buf, count, ADP8860_BLDM1); +} +static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show, + adp8860_bl_l1_daylight_dim_store); + +#ifdef ADP8860_EXT_FEATURES +static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adp8860_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + uint16_t ret_val; + + mutex_lock(&data->lock); + error = adp8860_read(data->client, ADP8860_PH1LEVL, ®_val); + ret_val = reg_val; + error |= adp8860_read(data->client, ADP8860_PH1LEVH, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + /* Return 13-bit conversion value for the first light sensor */ + ret_val += (reg_val & 0x1F) << 8; + + return sprintf(buf, "%u\n", ret_val); +} +static DEVICE_ATTR(ambient_light_level, 0444, + adp8860_bl_ambient_light_level_show, NULL); + +static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adp8860_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp8860_read(data->client, ADP8860_CFGR, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + return sprintf(buf, "%u\n", + ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); +} + +static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adp8860_bl *data = dev_get_drvdata(dev); + unsigned long val; + uint8_t reg_val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + if (val == 0) { + /* Enable automatic ambient light sensing */ + adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); + } else if ((val > 0) && (val < 6)) { + /* Disable automatic ambient light sensing */ + adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); + + /* Set user supplied ambient light zone */ + mutex_lock(&data->lock); + adp8860_read(data->client, ADP8860_CFGR, ®_val); + reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); + reg_val |= val << CFGR_BLV_SHIFT; + adp8860_write(data->client, ADP8860_CFGR, reg_val); + mutex_unlock(&data->lock); + } + + return count; +} +static DEVICE_ATTR(ambient_light_zone, 0664, + adp8860_bl_ambient_light_zone_show, + adp8860_bl_ambient_light_zone_store); +#endif + +static struct attribute *adp8860_bl_attributes[] = { + &dev_attr_l3_dark_max.attr, + &dev_attr_l3_dark_dim.attr, + &dev_attr_l2_office_max.attr, + &dev_attr_l2_office_dim.attr, + &dev_attr_l1_daylight_max.attr, + &dev_attr_l1_daylight_dim.attr, +#ifdef ADP8860_EXT_FEATURES + &dev_attr_ambient_light_level.attr, + &dev_attr_ambient_light_zone.attr, +#endif + NULL +}; + +static const struct attribute_group adp8860_bl_attr_group = { + .attrs = adp8860_bl_attributes, +}; + +static int __devinit adp8860_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct backlight_device *bl; + struct adp8860_bl *data; + struct adp8860_backlight_platform_data *pdata = + client->dev.platform_data; + struct backlight_properties props; + uint8_t reg_val; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + ret = adp8860_read(client, ADP8860_MFDVID, ®_val); + if (ret < 0) + goto out2; + + switch (ADP8860_MANID(reg_val)) { + case ADP8863_MANUFID: + data->gdwn_dis = !!pdata->gdwn_dis; + case ADP8860_MANUFID: + data->en_ambl_sens = !!pdata->en_ambl_sens; + break; + case ADP8861_MANUFID: + data->gdwn_dis = !!pdata->gdwn_dis; + break; + default: + dev_err(&client->dev, "failed to probe\n"); + ret = -ENODEV; + goto out2; + } + + /* It's confirmed that the DEVID field is actually a REVID */ + + data->revid = ADP8860_DEVID(reg_val); + data->client = client; + data->pdata = pdata; + data->id = id->driver_data; + data->current_brightness = 0; + i2c_set_clientdata(client, data); + + memset(&props, 0, sizeof(props)); + props.max_brightness = ADP8860_MAX_BRIGHTNESS; + + mutex_init(&data->lock); + + bl = backlight_device_register(dev_driver_string(&client->dev), + &client->dev, data, &adp8860_bl_ops, &props); + if (IS_ERR(bl)) { + dev_err(&client->dev, "failed to register backlight\n"); + ret = PTR_ERR(bl); + goto out2; + } + + bl->props.max_brightness = + bl->props.brightness = ADP8860_MAX_BRIGHTNESS; + + data->bl = bl; + + if (data->en_ambl_sens) + ret = sysfs_create_group(&bl->dev.kobj, + &adp8860_bl_attr_group); + + if (ret) { + dev_err(&client->dev, "failed to register sysfs\n"); + goto out1; + } + + ret = adp8860_bl_setup(bl); + if (ret) { + ret = -EIO; + goto out; + } + + backlight_update_status(bl); + + dev_info(&client->dev, "%s Rev.%d Backlight\n", + client->name, data->revid); + + if (pdata->num_leds) + adp8860_led_probe(client); + + return 0; + +out: + if (data->en_ambl_sens) + sysfs_remove_group(&data->bl->dev.kobj, + &adp8860_bl_attr_group); +out1: + backlight_device_unregister(bl); +out2: + i2c_set_clientdata(client, NULL); + kfree(data); + + return ret; +} + +static int __devexit adp8860_remove(struct i2c_client *client) +{ + struct adp8860_bl *data = i2c_get_clientdata(client); + + adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); + + if (data->led) + adp8860_led_remove(client); + + if (data->en_ambl_sens) + sysfs_remove_group(&data->bl->dev.kobj, + &adp8860_bl_attr_group); + + backlight_device_unregister(data->bl); + i2c_set_clientdata(client, NULL); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); + + return 0; +} + +static int adp8860_i2c_resume(struct i2c_client *client) +{ + adp8860_set_bits(client, ADP8860_MDCR, NSTBY); + + return 0; +} +#else +#define adp8860_i2c_suspend NULL +#define adp8860_i2c_resume NULL +#endif + +static const struct i2c_device_id adp8860_id[] = { + { "adp8860", adp8860 }, + { "adp8861", adp8861 }, + { "adp8863", adp8863 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp8860_id); + +static struct i2c_driver adp8860_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = adp8860_probe, + .remove = __devexit_p(adp8860_remove), + .suspend = adp8860_i2c_suspend, + .resume = adp8860_i2c_resume, + .id_table = adp8860_id, +}; + +static int __init adp8860_init(void) +{ + return i2c_add_driver(&adp8860_driver); +} +module_init(adp8860_init); + +static void __exit adp8860_exit(void) +{ + i2c_del_driver(&adp8860_driver); +} +module_exit(adp8860_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP8860 Backlight driver"); +MODULE_ALIAS("i2c:adp8860-backlight"); diff --git a/drivers/video/backlight/adx_bl.c b/drivers/video/backlight/adx_bl.c index 7f4a7c30a98..fe9af129c5d 100644 --- a/drivers/video/backlight/adx_bl.c +++ b/drivers/video/backlight/adx_bl.c @@ -107,8 +107,8 @@ static int __devinit adx_backlight_probe(struct platform_device *pdev) props.max_brightness = 0xff; bldev = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, bl, &adx_backlight_ops, &props); - if (!bldev) { - ret = -ENOMEM; + if (IS_ERR(bldev)) { + ret = PTR_ERR(bldev); goto out; } diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c new file mode 100644 index 00000000000..b0cc4918480 --- /dev/null +++ b/drivers/video/backlight/ep93xx_bl.c @@ -0,0 +1,160 @@ +/* + * Driver for the Cirrus EP93xx lcd backlight + * + * Copyright (c) 2010 H Hartley Sweeten <hsweeten@visionengravers.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. + * + * This driver controls the pulse width modulated brightness control output, + * BRIGHT, on the Cirrus EP9307, EP9312, and EP9315 processors. + */ + + +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/fb.h> +#include <linux/backlight.h> + +#include <mach/hardware.h> + +#define EP93XX_RASTER_REG(x) (EP93XX_RASTER_BASE + (x)) +#define EP93XX_RASTER_BRIGHTNESS EP93XX_RASTER_REG(0x20) + +#define EP93XX_MAX_COUNT 255 +#define EP93XX_MAX_BRIGHT 255 +#define EP93XX_DEF_BRIGHT 128 + +struct ep93xxbl { + void __iomem *mmio; + int brightness; +}; + +static int ep93xxbl_set(struct backlight_device *bl, int brightness) +{ + struct ep93xxbl *ep93xxbl = bl_get_data(bl); + + __raw_writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio); + + ep93xxbl->brightness = brightness; + + return 0; +} + +static int ep93xxbl_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return ep93xxbl_set(bl, brightness); +} + +static int ep93xxbl_get_brightness(struct backlight_device *bl) +{ + struct ep93xxbl *ep93xxbl = bl_get_data(bl); + + return ep93xxbl->brightness; +} + +static const struct backlight_ops ep93xxbl_ops = { + .update_status = ep93xxbl_update_status, + .get_brightness = ep93xxbl_get_brightness, +}; + +static int __init ep93xxbl_probe(struct platform_device *dev) +{ + struct ep93xxbl *ep93xxbl; + struct backlight_device *bl; + struct backlight_properties props; + + ep93xxbl = devm_kzalloc(&dev->dev, sizeof(*ep93xxbl), GFP_KERNEL); + if (!ep93xxbl) + return -ENOMEM; + + /* + * This register is located in the range already ioremap'ed by + * the framebuffer driver. A MFD driver seems a bit of overkill + * to handle this so use the static I/O mapping; this address + * is already virtual. + * + * NOTE: No locking is required; the framebuffer does not touch + * this register. + */ + ep93xxbl->mmio = EP93XX_RASTER_BRIGHTNESS; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = EP93XX_MAX_BRIGHT; + bl = backlight_device_register(dev->name, &dev->dev, ep93xxbl, + &ep93xxbl_ops, &props); + if (IS_ERR(bl)) + return PTR_ERR(bl); + + bl->props.brightness = EP93XX_DEF_BRIGHT; + + platform_set_drvdata(dev, bl); + + ep93xxbl_update_status(bl); + + return 0; +} + +static int ep93xxbl_remove(struct platform_device *dev) +{ + struct backlight_device *bl = platform_get_drvdata(dev); + + backlight_device_unregister(bl); + platform_set_drvdata(dev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int ep93xxbl_suspend(struct platform_device *dev, pm_message_t state) +{ + struct backlight_device *bl = platform_get_drvdata(dev); + + return ep93xxbl_set(bl, 0); +} + +static int ep93xxbl_resume(struct platform_device *dev) +{ + struct backlight_device *bl = platform_get_drvdata(dev); + + backlight_update_status(bl); + return 0; +} +#else +#define ep93xxbl_suspend NULL +#define ep93xxbl_resume NULL +#endif + +static struct platform_driver ep93xxbl_driver = { + .driver = { + .name = "ep93xx-bl", + .owner = THIS_MODULE, + }, + .probe = ep93xxbl_probe, + .remove = __devexit_p(ep93xxbl_remove), + .suspend = ep93xxbl_suspend, + .resume = ep93xxbl_resume, +}; + +static int __init ep93xxbl_init(void) +{ + return platform_driver_register(&ep93xxbl_driver); +} +module_init(ep93xxbl_init); + +static void __exit ep93xxbl_exit(void) +{ + platform_driver_unregister(&ep93xxbl_driver); +} +module_exit(ep93xxbl_exit); + +MODULE_DESCRIPTION("EP93xx Backlight Driver"); +MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-bl"); diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index bcdb12c93ef..9093ef0fa86 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -125,8 +125,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) if (priv == NULL) { dev_err(&spi->dev, "No memory for this device.\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } dev_set_drvdata(&spi->dev, priv); @@ -139,7 +138,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) if (ret) { dev_err(&spi->dev, "Unable to get the lcd l4f00242t03 reset gpio.\n"); - return ret; + goto err; } ret = gpio_direction_output(pdata->reset_gpio, 1); @@ -151,7 +150,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) if (ret) { dev_err(&spi->dev, "Unable to get the lcd l4f00242t03 data en gpio.\n"); - return ret; + goto err2; } ret = gpio_direction_output(pdata->data_enable_gpio, 0); @@ -222,9 +221,9 @@ static int __devexit l4f00242t03_remove(struct spi_device *spi) gpio_free(pdata->reset_gpio); if (priv->io_reg) - regulator_put(priv->core_reg); - if (priv->core_reg) regulator_put(priv->io_reg); + if (priv->core_reg) + regulator_put(priv->core_reg); kfree(priv); diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index b5accc957ad..b2b2c7ba1f6 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -162,6 +162,7 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) backlight_update_status(bl); return 0; out: + backlight_device_unregister(bl); kfree(data); return ret; } diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 1b5d3fe6bbb..9fb533f6373 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -141,7 +141,7 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { .callback = mbp_dmi_match, .ident = "MacBook 1,1", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"), }, .driver_data = (void *)&intel_chipset_data, @@ -184,6 +184,42 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, { .callback = mbp_dmi_match, + .ident = "MacBookPro 1,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 1,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,2"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 2,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,1"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 2,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, .ident = "MacBookPro 3,1", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), @@ -238,6 +274,15 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, { .callback = mbp_dmi_match, + .ident = "MacBook 6,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook6,1"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, .ident = "MacBookAir 2,1", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c new file mode 100644 index 00000000000..3c424f7efdc --- /dev/null +++ b/drivers/video/backlight/pcf50633-backlight.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * PCF50633 backlight device driver + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <linux/mfd/pcf50633/core.h> +#include <linux/mfd/pcf50633/backlight.h> + +struct pcf50633_bl { + struct pcf50633 *pcf; + struct backlight_device *bl; + + unsigned int brightness; + unsigned int brightness_limit; +}; + +/* + * pcf50633_bl_set_brightness_limit + * + * Update the brightness limit for the pc50633 backlight. The actual brightness + * will not go above the limit. This is useful to limit power drain for example + * on low battery. + * + * @dev: Pointer to a pcf50633 device + * @limit: The brightness limit. Valid values are 0-63 + */ +int pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit) +{ + struct pcf50633_bl *pcf_bl = platform_get_drvdata(pcf->bl_pdev); + + if (!pcf_bl) + return -ENODEV; + + pcf_bl->brightness_limit = limit & 0x3f; + backlight_update_status(pcf_bl->bl); + + return 0; +} + +static int pcf50633_bl_update_status(struct backlight_device *bl) +{ + struct pcf50633_bl *pcf_bl = bl_get_data(bl); + unsigned int new_brightness; + + + if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK) || + bl->props.power != FB_BLANK_UNBLANK) + new_brightness = 0; + else if (bl->props.brightness < pcf_bl->brightness_limit) + new_brightness = bl->props.brightness; + else + new_brightness = pcf_bl->brightness_limit; + + + if (pcf_bl->brightness == new_brightness) + return 0; + + if (new_brightness) { + pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDOUT, + new_brightness); + if (!pcf_bl->brightness) + pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 1); + } else { + pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 0); + } + + pcf_bl->brightness = new_brightness; + + return 0; +} + +static int pcf50633_bl_get_brightness(struct backlight_device *bl) +{ + struct pcf50633_bl *pcf_bl = bl_get_data(bl); + return pcf_bl->brightness; +} + +static const struct backlight_ops pcf50633_bl_ops = { + .get_brightness = pcf50633_bl_get_brightness, + .update_status = pcf50633_bl_update_status, + .options = BL_CORE_SUSPENDRESUME, +}; + +static int __devinit pcf50633_bl_probe(struct platform_device *pdev) +{ + int ret; + struct pcf50633_bl *pcf_bl; + struct device *parent = pdev->dev.parent; + struct pcf50633_platform_data *pcf50633_data = parent->platform_data; + struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data; + struct backlight_properties bl_props; + + pcf_bl = kzalloc(sizeof(*pcf_bl), GFP_KERNEL); + if (!pcf_bl) + return -ENOMEM; + + bl_props.max_brightness = 0x3f; + bl_props.power = FB_BLANK_UNBLANK; + + if (pdata) { + bl_props.brightness = pdata->default_brightness; + pcf_bl->brightness_limit = pdata->default_brightness_limit; + } else { + bl_props.brightness = 0x3f; + pcf_bl->brightness_limit = 0x3f; + } + + pcf_bl->pcf = dev_to_pcf50633(pdev->dev.parent); + + pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl, + &pcf50633_bl_ops, &bl_props); + + if (IS_ERR(pcf_bl->bl)) { + ret = PTR_ERR(pcf_bl->bl); + goto err_free; + } + + platform_set_drvdata(pdev, pcf_bl); + + pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDDIM, pdata->ramp_time); + + /* Should be different from bl_props.brightness, so we do not exit + * update_status early the first time it's called */ + pcf_bl->brightness = pcf_bl->bl->props.brightness + 1; + + backlight_update_status(pcf_bl->bl); + + return 0; + +err_free: + kfree(pcf_bl); + + return ret; +} + +static int __devexit pcf50633_bl_remove(struct platform_device *pdev) +{ + struct pcf50633_bl *pcf_bl = platform_get_drvdata(pdev); + + backlight_device_unregister(pcf_bl->bl); + + platform_set_drvdata(pdev, NULL); + + kfree(pcf_bl); + + return 0; +} + +static struct platform_driver pcf50633_bl_driver = { + .probe = pcf50633_bl_probe, + .remove = __devexit_p(pcf50633_bl_remove), + .driver = { + .name = "pcf50633-backlight", + }, +}; + +static int __init pcf50633_bl_init(void) +{ + return platform_driver_register(&pcf50633_bl_driver); +} +module_init(pcf50633_bl_init); + +static void __exit pcf50633_bl_exit(void) +{ + platform_driver_unregister(&pcf50633_bl_driver); +} +module_exit(pcf50633_bl_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("PCF50633 backlight driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcf50633-backlight"); diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c new file mode 100644 index 00000000000..a3128c9cb7a --- /dev/null +++ b/drivers/video/backlight/s6e63m0.c @@ -0,0 +1,920 @@ +/* + * S6E63M0 AMOLED LCD panel driver. + * + * Author: InKi Dae <inki.dae@samsung.com> + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/wait.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/lcd.h> +#include <linux/backlight.h> + +#include "s6e63m0_gamma.h" + +#define SLEEPMSEC 0x1000 +#define ENDDEF 0x2000 +#define DEFMASK 0xFF00 +#define COMMAND_ONLY 0xFE +#define DATA_ONLY 0xFF + +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 10 + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +struct s6e63m0 { + struct device *dev; + struct spi_device *spi; + unsigned int power; + unsigned int current_brightness; + unsigned int gamma_mode; + unsigned int gamma_table_count; + struct lcd_device *ld; + struct backlight_device *bd; + struct lcd_platform_data *lcd_pd; +}; + +static const unsigned short SEQ_PANEL_CONDITION_SET[] = { + 0xF8, 0x01, + DATA_ONLY, 0x27, + DATA_ONLY, 0x27, + DATA_ONLY, 0x07, + DATA_ONLY, 0x07, + DATA_ONLY, 0x54, + DATA_ONLY, 0x9f, + DATA_ONLY, 0x63, + DATA_ONLY, 0x86, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x33, + DATA_ONLY, 0x0d, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = { + 0xf2, 0x02, + DATA_ONLY, 0x03, + DATA_ONLY, 0x1c, + DATA_ONLY, 0x10, + DATA_ONLY, 0x10, + + 0xf7, 0x03, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_GAMMA_SETTING[] = { + 0xfa, 0x00, + DATA_ONLY, 0x18, + DATA_ONLY, 0x08, + DATA_ONLY, 0x24, + DATA_ONLY, 0x64, + DATA_ONLY, 0x56, + DATA_ONLY, 0x33, + DATA_ONLY, 0xb6, + DATA_ONLY, 0xba, + DATA_ONLY, 0xa8, + DATA_ONLY, 0xac, + DATA_ONLY, 0xb1, + DATA_ONLY, 0x9d, + DATA_ONLY, 0xc1, + DATA_ONLY, 0xc1, + DATA_ONLY, 0xb7, + DATA_ONLY, 0x00, + DATA_ONLY, 0x9c, + DATA_ONLY, 0x00, + DATA_ONLY, 0x9f, + DATA_ONLY, 0x00, + DATA_ONLY, 0xd6, + + 0xfa, 0x01, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_ETC_CONDITION_SET[] = { + 0xf6, 0x00, + DATA_ONLY, 0x8c, + DATA_ONLY, 0x07, + + 0xb3, 0xc, + + 0xb5, 0x2c, + DATA_ONLY, 0x12, + DATA_ONLY, 0x0c, + DATA_ONLY, 0x0a, + DATA_ONLY, 0x10, + DATA_ONLY, 0x0e, + DATA_ONLY, 0x17, + DATA_ONLY, 0x13, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x2a, + DATA_ONLY, 0x24, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x1b, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x17, + + DATA_ONLY, 0x2b, + DATA_ONLY, 0x26, + DATA_ONLY, 0x22, + DATA_ONLY, 0x20, + DATA_ONLY, 0x3a, + DATA_ONLY, 0x34, + DATA_ONLY, 0x30, + DATA_ONLY, 0x2c, + DATA_ONLY, 0x29, + DATA_ONLY, 0x26, + DATA_ONLY, 0x25, + DATA_ONLY, 0x23, + DATA_ONLY, 0x21, + DATA_ONLY, 0x20, + DATA_ONLY, 0x1e, + DATA_ONLY, 0x1e, + + 0xb6, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x11, + DATA_ONLY, 0x22, + DATA_ONLY, 0x33, + DATA_ONLY, 0x44, + DATA_ONLY, 0x44, + DATA_ONLY, 0x44, + + DATA_ONLY, 0x55, + DATA_ONLY, 0x55, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + + 0xb7, 0x2c, + DATA_ONLY, 0x12, + DATA_ONLY, 0x0c, + DATA_ONLY, 0x0a, + DATA_ONLY, 0x10, + DATA_ONLY, 0x0e, + DATA_ONLY, 0x17, + DATA_ONLY, 0x13, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x2a, + DATA_ONLY, 0x24, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x1b, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x17, + + DATA_ONLY, 0x2b, + DATA_ONLY, 0x26, + DATA_ONLY, 0x22, + DATA_ONLY, 0x20, + DATA_ONLY, 0x3a, + DATA_ONLY, 0x34, + DATA_ONLY, 0x30, + DATA_ONLY, 0x2c, + DATA_ONLY, 0x29, + DATA_ONLY, 0x26, + DATA_ONLY, 0x25, + DATA_ONLY, 0x23, + DATA_ONLY, 0x21, + DATA_ONLY, 0x20, + DATA_ONLY, 0x1e, + DATA_ONLY, 0x1e, + + 0xb8, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x11, + DATA_ONLY, 0x22, + DATA_ONLY, 0x33, + DATA_ONLY, 0x44, + DATA_ONLY, 0x44, + DATA_ONLY, 0x44, + + DATA_ONLY, 0x55, + DATA_ONLY, 0x55, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + + 0xb9, 0x2c, + DATA_ONLY, 0x12, + DATA_ONLY, 0x0c, + DATA_ONLY, 0x0a, + DATA_ONLY, 0x10, + DATA_ONLY, 0x0e, + DATA_ONLY, 0x17, + DATA_ONLY, 0x13, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x2a, + DATA_ONLY, 0x24, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x1b, + DATA_ONLY, 0x1a, + DATA_ONLY, 0x17, + + DATA_ONLY, 0x2b, + DATA_ONLY, 0x26, + DATA_ONLY, 0x22, + DATA_ONLY, 0x20, + DATA_ONLY, 0x3a, + DATA_ONLY, 0x34, + DATA_ONLY, 0x30, + DATA_ONLY, 0x2c, + DATA_ONLY, 0x29, + DATA_ONLY, 0x26, + DATA_ONLY, 0x25, + DATA_ONLY, 0x23, + DATA_ONLY, 0x21, + DATA_ONLY, 0x20, + DATA_ONLY, 0x1e, + DATA_ONLY, 0x1e, + + 0xba, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x11, + DATA_ONLY, 0x22, + DATA_ONLY, 0x33, + DATA_ONLY, 0x44, + DATA_ONLY, 0x44, + DATA_ONLY, 0x44, + + DATA_ONLY, 0x55, + DATA_ONLY, 0x55, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + DATA_ONLY, 0x66, + + 0xc1, 0x4d, + DATA_ONLY, 0x96, + DATA_ONLY, 0x1d, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x01, + DATA_ONLY, 0xdf, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x03, + DATA_ONLY, 0x1f, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x00, + DATA_ONLY, 0x03, + DATA_ONLY, 0x06, + DATA_ONLY, 0x09, + DATA_ONLY, 0x0d, + DATA_ONLY, 0x0f, + DATA_ONLY, 0x12, + DATA_ONLY, 0x15, + DATA_ONLY, 0x18, + + 0xb2, 0x10, + DATA_ONLY, 0x10, + DATA_ONLY, 0x0b, + DATA_ONLY, 0x05, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_ACL_ON[] = { + /* ACL on */ + 0xc0, 0x01, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_ACL_OFF[] = { + /* ACL off */ + 0xc0, 0x00, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_ELVSS_ON[] = { + /* ELVSS on */ + 0xb1, 0x0b, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_ELVSS_OFF[] = { + /* ELVSS off */ + 0xb1, 0x0a, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_STAND_BY_OFF[] = { + 0x11, COMMAND_ONLY, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_STAND_BY_ON[] = { + 0x10, COMMAND_ONLY, + + ENDDEF, 0x0000 +}; + +static const unsigned short SEQ_DISPLAY_ON[] = { + 0x29, COMMAND_ONLY, + + ENDDEF, 0x0000 +}; + + +static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data) +{ + u16 buf[1]; + struct spi_message msg; + + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr << 8) | data; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address, + unsigned char command) +{ + int ret = 0; + + if (address != DATA_ONLY) + ret = s6e63m0_spi_write_byte(lcd, 0x0, address); + if (command != COMMAND_ONLY) + ret = s6e63m0_spi_write_byte(lcd, 0x1, command); + + return ret; +} + +static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd, + const unsigned short *wbuf) +{ + int ret = 0, i = 0; + + while ((wbuf[i] & DEFMASK) != ENDDEF) { + if ((wbuf[i] & DEFMASK) != SLEEPMSEC) { + ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]); + if (ret) + break; + } else + udelay(wbuf[i+1]*1000); + i += 2; + } + + return ret; +} + +static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma) +{ + unsigned int i = 0; + int ret = 0; + + /* disable gamma table updating. */ + ret = s6e63m0_spi_write(lcd, 0xfa, 0x00); + if (ret) { + dev_err(lcd->dev, "failed to disable gamma table updating.\n"); + goto gamma_err; + } + + for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) { + ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]); + if (ret) { + dev_err(lcd->dev, "failed to set gamma table.\n"); + goto gamma_err; + } + } + + /* update gamma table. */ + ret = s6e63m0_spi_write(lcd, 0xfa, 0x01); + if (ret) + dev_err(lcd->dev, "failed to update gamma table.\n"); + +gamma_err: + return ret; +} + +static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma) +{ + int ret = 0; + + ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); + + return ret; +} + + +static int s6e63m0_ldi_init(struct s6e63m0 *lcd) +{ + int ret, i; + const unsigned short *init_seq[] = { + SEQ_PANEL_CONDITION_SET, + SEQ_DISPLAY_CONDITION_SET, + SEQ_GAMMA_SETTING, + SEQ_ETC_CONDITION_SET, + SEQ_ACL_ON, + SEQ_ELVSS_ON, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int s6e63m0_ldi_enable(struct s6e63m0 *lcd) +{ + int ret = 0, i; + const unsigned short *enable_seq[] = { + SEQ_STAND_BY_OFF, + SEQ_DISPLAY_ON, + }; + + for (i = 0; i < ARRAY_SIZE(enable_seq); i++) { + ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int s6e63m0_ldi_disable(struct s6e63m0 *lcd) +{ + int ret; + + ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON); + + return ret; +} + +static int s6e63m0_power_on(struct s6e63m0 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + struct backlight_device *bd = NULL; + + pd = lcd->lcd_pd; + if (!pd) { + dev_err(lcd->dev, "platform data is NULL.\n"); + return -EFAULT; + } + + bd = lcd->bd; + if (!bd) { + dev_err(lcd->dev, "backlight device is NULL.\n"); + return -EFAULT; + } + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EFAULT; + } else { + pd->power_on(lcd->ld, 1); + mdelay(pd->power_on_delay); + } + + if (!pd->reset) { + dev_err(lcd->dev, "reset is NULL.\n"); + return -EFAULT; + } else { + pd->reset(lcd->ld); + mdelay(pd->reset_delay); + } + + ret = s6e63m0_ldi_init(lcd); + if (ret) { + dev_err(lcd->dev, "failed to initialize ldi.\n"); + return ret; + } + + ret = s6e63m0_ldi_enable(lcd); + if (ret) { + dev_err(lcd->dev, "failed to enable ldi.\n"); + return ret; + } + + /* set brightness to current value after power on or resume. */ + ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness); + if (ret) { + dev_err(lcd->dev, "lcd gamma setting failed.\n"); + return ret; + } + + return 0; +} + +static int s6e63m0_power_off(struct s6e63m0 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + + pd = lcd->lcd_pd; + if (!pd) { + dev_err(lcd->dev, "platform data is NULL.\n"); + return -EFAULT; + } + + ret = s6e63m0_ldi_disable(lcd); + if (ret) { + dev_err(lcd->dev, "lcd setting failed.\n"); + return -EIO; + } + + mdelay(pd->power_off_delay); + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EFAULT; + } else + pd->power_on(lcd->ld, 0); + + return 0; +} + +static int s6e63m0_power(struct s6e63m0 *lcd, int power) +{ + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = s6e63m0_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = s6e63m0_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + +static int s6e63m0_set_power(struct lcd_device *ld, int power) +{ + struct s6e63m0 *lcd = lcd_get_data(ld); + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + return s6e63m0_power(lcd, power); +} + +static int s6e63m0_get_power(struct lcd_device *ld) +{ + struct s6e63m0 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int s6e63m0_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int s6e63m0_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct s6e63m0 *lcd = bl_get_data(bd); + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(&bd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static struct lcd_ops s6e63m0_lcd_ops = { + .set_power = s6e63m0_set_power, + .get_power = s6e63m0_get_power, +}; + +static const struct backlight_ops s6e63m0_backlight_ops = { + .get_brightness = s6e63m0_get_brightness, + .update_status = s6e63m0_set_brightness, +}; + +static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s6e63m0 *lcd = dev_get_drvdata(dev); + char temp[10]; + + switch (lcd->gamma_mode) { + case 0: + sprintf(temp, "2.2 mode\n"); + strcat(buf, temp); + break; + case 1: + sprintf(temp, "1.9 mode\n"); + strcat(buf, temp); + break; + case 2: + sprintf(temp, "1.7 mode\n"); + strcat(buf, temp); + break; + default: + dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n"); + break; + } + + return strlen(buf); +} + +static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct s6e63m0 *lcd = dev_get_drvdata(dev); + struct backlight_device *bd = NULL; + int brightness, rc; + + rc = strict_strtoul(buf, 0, (unsigned long *)&lcd->gamma_mode); + if (rc < 0) + return rc; + + bd = lcd->bd; + + brightness = bd->props.brightness; + + switch (lcd->gamma_mode) { + case 0: + _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]); + break; + case 1: + _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]); + break; + case 2: + _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]); + break; + default: + dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n"); + _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]); + break; + } + return len; +} + +static DEVICE_ATTR(gamma_mode, 0644, + s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode); + +static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s6e63m0 *lcd = dev_get_drvdata(dev); + char temp[3]; + + sprintf(temp, "%d\n", lcd->gamma_table_count); + strcpy(buf, temp); + + return strlen(buf); +} +static DEVICE_ATTR(gamma_table, 0644, + s6e63m0_sysfs_show_gamma_table, NULL); + +static int __init s6e63m0_probe(struct spi_device *spi) +{ + int ret = 0; + struct s6e63m0 *lcd = NULL; + struct lcd_device *ld = NULL; + struct backlight_device *bd = NULL; + + lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */ + spi->bits_per_word = 9; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed.\n"); + goto out_free_lcd; + } + + lcd->spi = spi; + lcd->dev = &spi->dev; + + lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data; + if (!lcd->lcd_pd) { + dev_err(&spi->dev, "platform data is NULL.\n"); + goto out_free_lcd; + } + + ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + goto out_free_lcd; + } + + lcd->ld = ld; + + bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd, + &s6e63m0_backlight_ops, NULL); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto out_lcd_unregister; + } + + bd->props.max_brightness = MAX_BRIGHTNESS; + bd->props.brightness = MAX_BRIGHTNESS; + lcd->bd = bd; + + /* + * it gets gamma table count available so it gets user + * know that. + */ + lcd->gamma_table_count = + sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int)); + + ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode); + if (ret < 0) + dev_err(&(spi->dev), "failed to add sysfs entries\n"); + + ret = device_create_file(&(spi->dev), &dev_attr_gamma_table); + if (ret < 0) + dev_err(&(spi->dev), "failed to add sysfs entries\n"); + + /* + * if lcd panel was on from bootloader like u-boot then + * do not lcd on. + */ + if (!lcd->lcd_pd->lcd_enabled) { + /* + * if lcd panel was off from bootloader then + * current lcd status is powerdown and then + * it enables lcd panel. + */ + lcd->power = FB_BLANK_POWERDOWN; + + s6e63m0_power(lcd, FB_BLANK_UNBLANK); + } else + lcd->power = FB_BLANK_UNBLANK; + + dev_set_drvdata(&spi->dev, lcd); + + dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n"); + + return 0; + +out_lcd_unregister: + lcd_device_unregister(ld); +out_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit s6e63m0_remove(struct spi_device *spi) +{ + struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + + s6e63m0_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->ld); + kfree(lcd); + + return 0; +} + +#if defined(CONFIG_PM) +unsigned int before_power; + +static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg) +{ + int ret = 0; + struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); + + before_power = lcd->power; + + /* + * when lcd panel is suspend, lcd panel becomes off + * regardless of status. + */ + ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN); + + return ret; +} + +static int s6e63m0_resume(struct spi_device *spi) +{ + int ret = 0; + struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + + /* + * after suspended, if lcd panel status is FB_BLANK_UNBLANK + * (at that time, before_power is FB_BLANK_UNBLANK) then + * it changes that status to FB_BLANK_POWERDOWN to get lcd on. + */ + if (before_power == FB_BLANK_UNBLANK) + lcd->power = FB_BLANK_POWERDOWN; + + dev_dbg(&spi->dev, "before_power = %d\n", before_power); + + ret = s6e63m0_power(lcd, before_power); + + return ret; +} +#else +#define s6e63m0_suspend NULL +#define s6e63m0_resume NULL +#endif + +/* Power down all displays on reboot, poweroff or halt. */ +static void s6e63m0_shutdown(struct spi_device *spi) +{ + struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + + s6e63m0_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver s6e63m0_driver = { + .driver = { + .name = "s6e63m0", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = s6e63m0_probe, + .remove = __devexit_p(s6e63m0_remove), + .shutdown = s6e63m0_shutdown, + .suspend = s6e63m0_suspend, + .resume = s6e63m0_resume, +}; + +static int __init s6e63m0_init(void) +{ + return spi_register_driver(&s6e63m0_driver); +} + +static void __exit s6e63m0_exit(void) +{ + spi_unregister_driver(&s6e63m0_driver); +} + +module_init(s6e63m0_init); +module_exit(s6e63m0_exit); + +MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("S6E63M0 LCD Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/backlight/s6e63m0_gamma.h b/drivers/video/backlight/s6e63m0_gamma.h new file mode 100644 index 00000000000..2c44bdb0696 --- /dev/null +++ b/drivers/video/backlight/s6e63m0_gamma.h @@ -0,0 +1,266 @@ +/* linux/drivers/video/samsung/s6e63m0_brightness.h + * + * Gamma level definitions. + * + * Copyright (c) 2009 Samsung Electronics + * InKi Dae <inki.dae@samsung.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. +*/ + +#ifndef _S6E63M0_BRIGHTNESS_H +#define _S6E63M0_BRIGHTNESS_H + +#define MAX_GAMMA_LEVEL 11 +#define GAMMA_TABLE_COUNT 21 + +/* gamma value: 2.2 */ +static const unsigned int s6e63m0_22_300[] = { + 0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6, + 0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0, + 0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb +}; + +static const unsigned int s6e63m0_22_280[] = { + 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6, + 0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1, + 0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 +}; + +static const unsigned int s6e63m0_22_260[] = { + 0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6, + 0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2, + 0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 + +}; + +static const unsigned int s6e63m0_22_240[] = { + 0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9, + 0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3, + 0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA + +}; +static const unsigned int s6e63m0_22_220[] = { + 0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8, + 0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4, + 0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 +}; + +static const unsigned int s6e63m0_22_200[] = { + 0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA, + 0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6, + 0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA +}; + +static const unsigned int s6e63m0_22_170[] = { + 0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB, + 0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8, + 0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB +}; + +static const unsigned int s6e63m0_22_140[] = { + 0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC, + 0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9, + 0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E +}; + +static const unsigned int s6e63m0_22_110[] = { + 0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF, + 0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC, + 0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D +}; + +static const unsigned int s6e63m0_22_90[] = { + 0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0, + 0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF, + 0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 +}; + +static const unsigned int s6e63m0_22_30[] = { + 0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8, + 0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7, + 0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 +}; + +/* gamma value: 1.9 */ +static const unsigned int s6e63m0_19_300[] = { + 0x18, 0x08, 0x24, 0x61, 0x5F, 0x39, 0xBA, + 0xBD, 0xAD, 0xB1, 0xB6, 0xA5, 0xC4, 0xC5, + 0xBC, 0x00, 0xA0, 0x00, 0xA4, 0x00, 0xDB +}; + +static const unsigned int s6e63m0_19_280[] = { + 0x18, 0x08, 0x24, 0x61, 0x60, 0x39, 0xBB, + 0xBE, 0xAD, 0xB2, 0xB6, 0xA6, 0xC5, 0xC7, + 0xBD, 0x00, 0x9B, 0x00, 0x9E, 0x00, 0xD5 +}; + +static const unsigned int s6e63m0_19_260[] = { + 0x18, 0x08, 0x24, 0x63, 0x61, 0x3B, 0xBA, + 0xBE, 0xAC, 0xB3, 0xB8, 0xA7, 0xC6, 0xC8, + 0xBD, 0x00, 0x96, 0x00, 0x98, 0x00, 0xCF +}; + +static const unsigned int s6e63m0_19_240[] = { + 0x18, 0x08, 0x24, 0x67, 0x64, 0x3F, 0xBB, + 0xBE, 0xAD, 0xB3, 0xB9, 0xA7, 0xC8, 0xC9, + 0xBE, 0x00, 0x90, 0x00, 0x92, 0x00, 0xC8 +}; + +static const unsigned int s6e63m0_19_220[] = { + 0x18, 0x08, 0x24, 0x68, 0x64, 0x40, 0xBC, + 0xBF, 0xAF, 0xB4, 0xBA, 0xA9, 0xC8, 0xCA, + 0xBE, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0xC0 +}; + +static const unsigned int s6e63m0_19_200[] = { + 0x18, 0x08, 0x24, 0x68, 0x64, 0x3F, 0xBE, + 0xC0, 0xB0, 0xB6, 0xBB, 0xAB, 0xC8, 0xCB, + 0xBF, 0x00, 0x85, 0x00, 0x86, 0x00, 0xB8 +}; + +static const unsigned int s6e63m0_19_170[] = { + 0x18, 0x08, 0x24, 0x69, 0x64, 0x40, 0xBF, + 0xC1, 0xB0, 0xB9, 0xBE, 0xAD, 0xCB, 0xCD, + 0xC2, 0x00, 0x7A, 0x00, 0x7B, 0x00, 0xAA +}; + +static const unsigned int s6e63m0_19_140[] = { + 0x18, 0x08, 0x24, 0x6E, 0x65, 0x45, 0xC0, + 0xC3, 0xB2, 0xBA, 0xBE, 0xAE, 0xCD, 0xD0, + 0xC4, 0x00, 0x70, 0x00, 0x70, 0x00, 0x9C +}; + +static const unsigned int s6e63m0_19_110[] = { + 0x18, 0x08, 0x24, 0x6F, 0x65, 0x46, 0xC2, + 0xC4, 0xB3, 0xBF, 0xC2, 0xB2, 0xCF, 0xD1, + 0xC6, 0x00, 0x64, 0x00, 0x64, 0x00, 0x8D +}; + +static const unsigned int s6e63m0_19_90[] = { + 0x18, 0x08, 0x24, 0x74, 0x60, 0x4A, 0xC3, + 0xC6, 0xB5, 0xBF, 0xC3, 0xB2, 0xD2, 0xD3, + 0xC8, 0x00, 0x5B, 0x00, 0x5B, 0x00, 0x81 +}; + +static const unsigned int s6e63m0_19_30[] = { + 0x18, 0x08, 0x24, 0x84, 0x45, 0x4F, 0xCA, + 0xCB, 0xBC, 0xC9, 0xCB, 0xBC, 0xDA, 0xDA, + 0xD0, 0x00, 0x35, 0x00, 0x34, 0x00, 0x4E +}; + +/* gamma value: 1.7 */ +static const unsigned int s6e63m0_17_300[] = { + 0x18, 0x08, 0x24, 0x70, 0x70, 0x4F, 0xBF, + 0xC2, 0xB2, 0xB8, 0xBC, 0xAC, 0xCB, 0xCD, + 0xC3, 0x00, 0xA0, 0x00, 0xA4, 0x00, 0xDB +}; + +static const unsigned int s6e63m0_17_280[] = { + 0x18, 0x08, 0x24, 0x71, 0x71, 0x50, 0xBF, + 0xC2, 0xB2, 0xBA, 0xBE, 0xAE, 0xCB, 0xCD, + 0xC3, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 +}; + +static const unsigned int s6e63m0_17_260[] = { + 0x18, 0x08, 0x24, 0x72, 0x72, 0x50, 0xC0, + 0xC3, 0xB4, 0xB9, 0xBE, 0xAE, 0xCC, 0xCF, + 0xC4, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 +}; + +static const unsigned int s6e63m0_17_240[] = { + 0x18, 0x08, 0x24, 0x71, 0x72, 0x4F, 0xC2, + 0xC4, 0xB5, 0xBB, 0xBF, 0xB0, 0xCC, 0xCF, + 0xC3, 0x00, 0x91, 0x00, 0x95, 0x00, 0xCA +}; + +static const unsigned int s6e63m0_17_220[] = { + 0x18, 0x08, 0x24, 0x71, 0x73, 0x4F, 0xC2, + 0xC5, 0xB5, 0xBD, 0xC0, 0xB2, 0xCD, 0xD1, + 0xC5, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 +}; + +static const unsigned int s6e63m0_17_200[] = { + 0x18, 0x08, 0x24, 0x72, 0x75, 0x51, 0xC2, + 0xC6, 0xB5, 0xBF, 0xC1, 0xB3, 0xCE, 0xD1, + 0xC6, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA +}; + +static const unsigned int s6e63m0_17_170[] = { + 0x18, 0x08, 0x24, 0x75, 0x77, 0x54, 0xC3, + 0xC7, 0xB7, 0xC0, 0xC3, 0xB4, 0xD1, 0xD3, + 0xC9, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB +}; + +static const unsigned int s6e63m0_17_140[] = { + 0x18, 0x08, 0x24, 0x7B, 0x77, 0x58, 0xC3, + 0xC8, 0xB8, 0xC2, 0xC6, 0xB6, 0xD3, 0xD4, + 0xCA, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E +}; + +static const unsigned int s6e63m0_17_110[] = { + 0x18, 0x08, 0x24, 0x81, 0x7B, 0x5D, 0xC6, + 0xCA, 0xBB, 0xC3, 0xC7, 0xB8, 0xD6, 0xD8, + 0xCD, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D +}; + +static const unsigned int s6e63m0_17_90[] = { + 0x18, 0x08, 0x24, 0x82, 0x7A, 0x5B, 0xC8, + 0xCB, 0xBD, 0xC5, 0xCA, 0xBA, 0xD6, 0xD8, + 0xCE, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 +}; + +static const unsigned int s6e63m0_17_30[] = { + 0x18, 0x08, 0x24, 0x8F, 0x73, 0x63, 0xD1, + 0xD0, 0xC5, 0xCC, 0xD1, 0xC2, 0xDE, 0xE0, + 0xD6, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 +}; + +struct s6e63m0_gamma { + unsigned int *gamma_22_table[MAX_GAMMA_LEVEL]; + unsigned int *gamma_19_table[MAX_GAMMA_LEVEL]; + unsigned int *gamma_17_table[MAX_GAMMA_LEVEL]; +}; + +static struct s6e63m0_gamma gamma_table = { + .gamma_22_table[0] = (unsigned int *)&s6e63m0_22_30, + .gamma_22_table[1] = (unsigned int *)&s6e63m0_22_90, + .gamma_22_table[2] = (unsigned int *)&s6e63m0_22_110, + .gamma_22_table[3] = (unsigned int *)&s6e63m0_22_140, + .gamma_22_table[4] = (unsigned int *)&s6e63m0_22_170, + .gamma_22_table[5] = (unsigned int *)&s6e63m0_22_200, + .gamma_22_table[6] = (unsigned int *)&s6e63m0_22_220, + .gamma_22_table[7] = (unsigned int *)&s6e63m0_22_240, + .gamma_22_table[8] = (unsigned int *)&s6e63m0_22_260, + .gamma_22_table[9] = (unsigned int *)&s6e63m0_22_280, + .gamma_22_table[10] = (unsigned int *)&s6e63m0_22_300, + + .gamma_19_table[0] = (unsigned int *)&s6e63m0_19_30, + .gamma_19_table[1] = (unsigned int *)&s6e63m0_19_90, + .gamma_19_table[2] = (unsigned int *)&s6e63m0_19_110, + .gamma_19_table[3] = (unsigned int *)&s6e63m0_19_140, + .gamma_19_table[4] = (unsigned int *)&s6e63m0_19_170, + .gamma_19_table[5] = (unsigned int *)&s6e63m0_19_200, + .gamma_19_table[6] = (unsigned int *)&s6e63m0_19_220, + .gamma_19_table[7] = (unsigned int *)&s6e63m0_19_240, + .gamma_19_table[8] = (unsigned int *)&s6e63m0_19_260, + .gamma_19_table[9] = (unsigned int *)&s6e63m0_19_280, + .gamma_19_table[10] = (unsigned int *)&s6e63m0_19_300, + + .gamma_17_table[0] = (unsigned int *)&s6e63m0_17_30, + .gamma_17_table[1] = (unsigned int *)&s6e63m0_17_90, + .gamma_17_table[2] = (unsigned int *)&s6e63m0_17_110, + .gamma_17_table[3] = (unsigned int *)&s6e63m0_17_140, + .gamma_17_table[4] = (unsigned int *)&s6e63m0_17_170, + .gamma_17_table[5] = (unsigned int *)&s6e63m0_17_200, + .gamma_17_table[6] = (unsigned int *)&s6e63m0_17_220, + .gamma_17_table[7] = (unsigned int *)&s6e63m0_17_240, + .gamma_17_table[8] = (unsigned int *)&s6e63m0_17_260, + .gamma_17_table[9] = (unsigned int *)&s6e63m0_17_280, + .gamma_17_table[10] = (unsigned int *)&s6e63m0_17_300, +}; + +#endif + diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 23b2a8c0dbf..b020ba7f1cf 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -501,7 +501,9 @@ static irqreturn_t bfin_bf54x_irq_error(int irq, void *dev_id) static int __devinit bfin_bf54x_probe(struct platform_device *pdev) { +#ifndef NO_BL_SUPPORT struct backlight_properties props; +#endif struct bfin_bf54xfb_info *info; struct fb_info *fbinfo; int ret; @@ -654,7 +656,8 @@ static int __devinit bfin_bf54x_probe(struct platform_device *pdev) printk(KERN_ERR DRIVER_NAME ": unable to register backlight.\n"); ret = -EINVAL; - goto out9; + unregister_framebuffer(fbinfo); + goto out8; } lcd_dev = lcd_device_register(DRIVER_NAME, &pdev->dev, NULL, &bfin_lcd_ops); @@ -663,8 +666,6 @@ static int __devinit bfin_bf54x_probe(struct platform_device *pdev) return 0; -out9: - unregister_framebuffer(fbinfo); out8: free_irq(info->irq, info); out7: diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c index 2baac7cc142..c8e1f04941b 100644 --- a/drivers/video/bfin-lq035q1-fb.c +++ b/drivers/video/bfin-lq035q1-fb.c @@ -61,47 +61,13 @@ #define LCD_X_RES 320 /* Horizontal Resolution */ #define LCD_Y_RES 240 /* Vertical Resolution */ #define DMA_BUS_SIZE 16 +#define U_LINE 4 /* Blanking Lines */ -#define USE_RGB565_16_BIT_PPI - -#ifdef USE_RGB565_16_BIT_PPI -#define LCD_BPP 16 /* Bit Per Pixel */ -#define CLOCKS_PER_PIX 1 -#define CPLD_PIPELINE_DELAY_COR 0 /* NO CPLB */ -#endif /* Interface 16/18-bit TFT over an 8-bit wide PPI using a small Programmable Logic Device (CPLD) * http://blackfin.uclinux.org/gf/project/stamp/frs/?action=FrsReleaseBrowse&frs_package_id=165 */ -#ifdef USE_RGB565_8_BIT_PPI -#define LCD_BPP 16 /* Bit Per Pixel */ -#define CLOCKS_PER_PIX 2 -#define CPLD_PIPELINE_DELAY_COR 3 /* RGB565 */ -#endif - -#ifdef USE_RGB888_8_BIT_PPI -#define LCD_BPP 24 /* Bit Per Pixel */ -#define CLOCKS_PER_PIX 3 -#define CPLD_PIPELINE_DELAY_COR 5 /* RGB888 */ -#endif - - /* - * HS and VS timing parameters (all in number of PPI clk ticks) - */ - -#define U_LINE 4 /* Blanking Lines */ - -#define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */ -#define H_PERIOD (336 * CLOCKS_PER_PIX) /* HS period */ -#define H_PULSE (2 * CLOCKS_PER_PIX) /* HS pulse width */ -#define H_START (7 * CLOCKS_PER_PIX + CPLD_PIPELINE_DELAY_COR) /* first valid pixel */ - -#define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */ -#define V_PULSE (2 * CLOCKS_PER_PIX) /* VS pulse width (1-5 H_PERIODs) */ -#define V_PERIOD (H_PERIOD * V_LINES) /* VS period */ - -#define ACTIVE_VIDEO_MEM_OFFSET ((U_LINE / 2) * LCD_X_RES * (LCD_BPP / 8)) #define BFIN_LCD_NBR_PALETTE_ENTRIES 256 @@ -110,12 +76,6 @@ #define PPI_PORT_CFG_01 0x10 #define PPI_POLS_1 0x8000 -#if (CLOCKS_PER_PIX > 1) -#define PPI_PMODE (DLEN_8 | PACK_EN) -#else -#define PPI_PMODE (DLEN_16) -#endif - #define LQ035_INDEX 0x74 #define LQ035_DATA 0x76 @@ -139,6 +99,15 @@ struct bfin_lq035q1fb_info { int irq; spinlock_t lock; /* lock */ u32 pseudo_pal[16]; + + u32 lcd_bpp; + u32 h_actpix; + u32 h_period; + u32 h_pulse; + u32 h_start; + u32 v_lines; + u32 v_pulse; + u32 v_period; }; static int nocursor; @@ -234,16 +203,69 @@ static int lq035q1_backlight(struct bfin_lq035q1fb_info *info, unsigned arg) return 0; } +static int bfin_lq035q1_calc_timing(struct bfin_lq035q1fb_info *fbi) +{ + unsigned long clocks_per_pix, cpld_pipeline_delay_cor; + + /* + * Interface 16/18-bit TFT over an 8-bit wide PPI using a small + * Programmable Logic Device (CPLD) + * http://blackfin.uclinux.org/gf/project/stamp/frs/?action=FrsReleaseBrowse&frs_package_id=165 + */ + + switch (fbi->disp_info->ppi_mode) { + case USE_RGB565_16_BIT_PPI: + fbi->lcd_bpp = 16; + clocks_per_pix = 1; + cpld_pipeline_delay_cor = 0; + break; + case USE_RGB565_8_BIT_PPI: + fbi->lcd_bpp = 16; + clocks_per_pix = 2; + cpld_pipeline_delay_cor = 3; + break; + case USE_RGB888_8_BIT_PPI: + fbi->lcd_bpp = 24; + clocks_per_pix = 3; + cpld_pipeline_delay_cor = 5; + break; + default: + return -EINVAL; + } + + /* + * HS and VS timing parameters (all in number of PPI clk ticks) + */ + + fbi->h_actpix = (LCD_X_RES * clocks_per_pix); /* active horizontal pixel */ + fbi->h_period = (336 * clocks_per_pix); /* HS period */ + fbi->h_pulse = (2 * clocks_per_pix); /* HS pulse width */ + fbi->h_start = (7 * clocks_per_pix + cpld_pipeline_delay_cor); /* first valid pixel */ + + fbi->v_lines = (LCD_Y_RES + U_LINE); /* total vertical lines */ + fbi->v_pulse = (2 * clocks_per_pix); /* VS pulse width (1-5 H_PERIODs) */ + fbi->v_period = (fbi->h_period * fbi->v_lines); /* VS period */ + + return 0; +} + static void bfin_lq035q1_config_ppi(struct bfin_lq035q1fb_info *fbi) { - bfin_write_PPI_DELAY(H_START); - bfin_write_PPI_COUNT(H_ACTPIX - 1); - bfin_write_PPI_FRAME(V_LINES); + unsigned ppi_pmode; + + if (fbi->disp_info->ppi_mode == USE_RGB565_16_BIT_PPI) + ppi_pmode = DLEN_16; + else + ppi_pmode = (DLEN_8 | PACK_EN); + + bfin_write_PPI_DELAY(fbi->h_start); + bfin_write_PPI_COUNT(fbi->h_actpix - 1); + bfin_write_PPI_FRAME(fbi->v_lines); bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */ PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */ PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */ - PPI_PMODE | /* 8/16 bit data length / PACK_EN? */ + ppi_pmode | /* 8/16 bit data length / PACK_EN? */ PPI_POLS_1); /* faling edge syncs POLS */ } @@ -272,19 +294,19 @@ static void bfin_lq035q1_stop_timers(void) } -static void bfin_lq035q1_init_timers(void) +static void bfin_lq035q1_init_timers(struct bfin_lq035q1fb_info *fbi) { bfin_lq035q1_stop_timers(); - set_gptimer_period(TIMER_HSYNC_id, H_PERIOD); - set_gptimer_pwidth(TIMER_HSYNC_id, H_PULSE); + set_gptimer_period(TIMER_HSYNC_id, fbi->h_period); + set_gptimer_pwidth(TIMER_HSYNC_id, fbi->h_pulse); set_gptimer_config(TIMER_HSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT | TIMER_TIN_SEL | TIMER_CLK_SEL| TIMER_EMU_RUN); - set_gptimer_period(TIMER_VSYNC_id, V_PERIOD); - set_gptimer_pwidth(TIMER_VSYNC_id, V_PULSE); + set_gptimer_period(TIMER_VSYNC_id, fbi->v_period); + set_gptimer_pwidth(TIMER_VSYNC_id, fbi->v_pulse); set_gptimer_config(TIMER_VSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT | TIMER_TIN_SEL | TIMER_CLK_SEL | TIMER_EMU_RUN); @@ -294,21 +316,21 @@ static void bfin_lq035q1_init_timers(void) static void bfin_lq035q1_config_dma(struct bfin_lq035q1fb_info *fbi) { + set_dma_config(CH_PPI, set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO, INTR_DISABLE, DIMENSION_2D, DATA_SIZE_16, DMA_NOSYNC_KEEP_DMA_BUF)); - set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE); + set_dma_x_count(CH_PPI, (LCD_X_RES * fbi->lcd_bpp) / DMA_BUS_SIZE); set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8); - set_dma_y_count(CH_PPI, V_LINES); + set_dma_y_count(CH_PPI, fbi->v_lines); set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8); set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer); } -#if (CLOCKS_PER_PIX == 1) static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, P_PPI0_D4, P_PPI0_D5, @@ -316,22 +338,27 @@ static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, 0}; -#else -static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + +static const u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, 0}; -#endif -static inline void bfin_lq035q1_free_ports(void) +static inline void bfin_lq035q1_free_ports(unsigned ppi16) { - peripheral_free_list(ppi0_req_16); + if (ppi16) + peripheral_free_list(ppi0_req_16); + else + peripheral_free_list(ppi0_req_8); + if (ANOMALY_05000400) gpio_free(P_IDENT(P_PPI0_FS3)); } -static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev) +static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev, + unsigned ppi16) { + int ret; /* ANOMALY_05000400 - PPI Does Not Start Properly In Specific Mode: * Drive PPI_FS3 Low */ @@ -342,7 +369,12 @@ static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev) gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); } - if (peripheral_request_list(ppi0_req_16, DRIVER_NAME)) { + if (ppi16) + ret = peripheral_request_list(ppi0_req_16, DRIVER_NAME); + else + ret = peripheral_request_list(ppi0_req_8, DRIVER_NAME); + + if (ret) { dev_err(&pdev->dev, "requesting peripherals failed\n"); return -EFAULT; } @@ -364,7 +396,7 @@ static int bfin_lq035q1_fb_open(struct fb_info *info, int user) bfin_lq035q1_config_dma(fbi); bfin_lq035q1_config_ppi(fbi); - bfin_lq035q1_init_timers(); + bfin_lq035q1_init_timers(fbi); /* start dma */ enable_dma(CH_PPI); @@ -402,12 +434,9 @@ static int bfin_lq035q1_fb_release(struct fb_info *info, int user) static int bfin_lq035q1_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - switch (var->bits_per_pixel) { -#if (LCD_BPP == 24) - case 24:/* TRUECOLOUR, 16m */ -#else - case 16:/* DIRECTCOLOUR, 64k */ -#endif + struct bfin_lq035q1fb_info *fbi = info->par; + + if (var->bits_per_pixel == fbi->lcd_bpp) { var->red.offset = info->var.red.offset; var->green.offset = info->var.green.offset; var->blue.offset = info->var.blue.offset; @@ -420,8 +449,7 @@ static int bfin_lq035q1_fb_check_var(struct fb_var_screeninfo *var, var->red.msb_right = 0; var->green.msb_right = 0; var->blue.msb_right = 0; - break; - default: + } else { pr_debug("%s: depth not supported: %u BPP\n", __func__, var->bits_per_pixel); return -EINVAL; @@ -528,6 +556,7 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) { struct bfin_lq035q1fb_info *info; struct fb_info *fbinfo; + u32 active_video_mem_offset; int ret; ret = request_dma(CH_PPI, DRIVER_NAME"_CH_PPI"); @@ -550,6 +579,12 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fbinfo); + ret = bfin_lq035q1_calc_timing(info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed PPI Mode\n"); + goto out3; + } + strcpy(fbinfo->fix.id, DRIVER_NAME); fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; @@ -571,46 +606,48 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) fbinfo->var.xres_virtual = LCD_X_RES; fbinfo->var.yres = LCD_Y_RES; fbinfo->var.yres_virtual = LCD_Y_RES; - fbinfo->var.bits_per_pixel = LCD_BPP; + fbinfo->var.bits_per_pixel = info->lcd_bpp; if (info->disp_info->mode & LQ035_BGR) { -#if (LCD_BPP == 24) - fbinfo->var.red.offset = 0; - fbinfo->var.green.offset = 8; - fbinfo->var.blue.offset = 16; -#else - fbinfo->var.red.offset = 0; - fbinfo->var.green.offset = 5; - fbinfo->var.blue.offset = 11; -#endif + if (info->lcd_bpp == 24) { + fbinfo->var.red.offset = 0; + fbinfo->var.green.offset = 8; + fbinfo->var.blue.offset = 16; + } else { + fbinfo->var.red.offset = 0; + fbinfo->var.green.offset = 5; + fbinfo->var.blue.offset = 11; + } } else { -#if (LCD_BPP == 24) - fbinfo->var.red.offset = 16; - fbinfo->var.green.offset = 8; - fbinfo->var.blue.offset = 0; -#else - fbinfo->var.red.offset = 11; - fbinfo->var.green.offset = 5; - fbinfo->var.blue.offset = 0; -#endif + if (info->lcd_bpp == 24) { + fbinfo->var.red.offset = 16; + fbinfo->var.green.offset = 8; + fbinfo->var.blue.offset = 0; + } else { + fbinfo->var.red.offset = 11; + fbinfo->var.green.offset = 5; + fbinfo->var.blue.offset = 0; + } } fbinfo->var.transp.offset = 0; -#if (LCD_BPP == 24) - fbinfo->var.red.length = 8; - fbinfo->var.green.length = 8; - fbinfo->var.blue.length = 8; -#else - fbinfo->var.red.length = 5; - fbinfo->var.green.length = 6; - fbinfo->var.blue.length = 5; -#endif + if (info->lcd_bpp == 24) { + fbinfo->var.red.length = 8; + fbinfo->var.green.length = 8; + fbinfo->var.blue.length = 8; + } else { + fbinfo->var.red.length = 5; + fbinfo->var.green.length = 6; + fbinfo->var.blue.length = 5; + } fbinfo->var.transp.length = 0; - fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8 - + ACTIVE_VIDEO_MEM_OFFSET; + active_video_mem_offset = ((U_LINE / 2) * LCD_X_RES * (info->lcd_bpp / 8)); + + fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * info->lcd_bpp / 8 + + active_video_mem_offset; fbinfo->fix.line_length = fbinfo->var.xres_virtual * fbinfo->var.bits_per_pixel / 8; @@ -629,8 +666,8 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) goto out3; } - fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET; - fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET; + fbinfo->screen_base = (void *)info->fb_buffer + active_video_mem_offset; + fbinfo->fix.smem_start = (int)info->fb_buffer + active_video_mem_offset; fbinfo->fbops = &bfin_lq035q1_fb_ops; @@ -643,7 +680,8 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) goto out4; } - ret = bfin_lq035q1_request_ports(pdev); + ret = bfin_lq035q1_request_ports(pdev, + info->disp_info->ppi_mode == USE_RGB565_16_BIT_PPI); if (ret) { dev_err(&pdev->dev, "couldn't request gpio port\n"); goto out6; @@ -693,7 +731,7 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "%dx%d %d-bit RGB FrameBuffer initialized\n", - LCD_X_RES, LCD_Y_RES, LCD_BPP); + LCD_X_RES, LCD_Y_RES, info->lcd_bpp); return 0; @@ -705,7 +743,8 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) out8: free_irq(info->irq, info); out7: - bfin_lq035q1_free_ports(); + bfin_lq035q1_free_ports(info->disp_info->ppi_mode == + USE_RGB565_16_BIT_PPI); out6: fb_dealloc_cmap(&fbinfo->cmap); out4: @@ -742,7 +781,8 @@ static int __devexit bfin_lq035q1_remove(struct platform_device *pdev) fb_dealloc_cmap(&fbinfo->cmap); - bfin_lq035q1_free_ports(); + bfin_lq035q1_free_ports(info->disp_info->ppi_mode == + USE_RGB565_16_BIT_PPI); platform_set_drvdata(pdev, NULL); framebuffer_release(fbinfo); @@ -781,7 +821,7 @@ static int bfin_lq035q1_resume(struct device *dev) bfin_lq035q1_config_dma(info); bfin_lq035q1_config_ppi(info); - bfin_lq035q1_init_timers(); + bfin_lq035q1_init_timers(info); /* start dma */ enable_dma(CH_PPI); diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c index c2ec3dcd4e9..7a50272eaab 100644 --- a/drivers/video/bfin-t350mcqb-fb.c +++ b/drivers/video/bfin-t350mcqb-fb.c @@ -420,7 +420,9 @@ static irqreturn_t bfin_t350mcqb_irq_error(int irq, void *dev_id) static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev) { +#ifndef NO_BL_SUPPORT struct backlight_properties props; +#endif struct bfin_t350mcqbfb_info *info; struct fb_info *fbinfo; int ret; @@ -550,7 +552,8 @@ static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev) printk(KERN_ERR DRIVER_NAME ": unable to register backlight.\n"); ret = -EINVAL; - goto out9; + unregister_framebuffer(fbinfo); + goto out8; } lcd_dev = lcd_device_register(DRIVER_NAME, NULL, &bfin_lcd_ops); @@ -559,8 +562,6 @@ static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev) return 0; -out9: - unregister_framebuffer(fbinfo); out8: free_irq(info->irq, info); out7: diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 8d244ba0f60..cad7d45c8ba 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -36,7 +36,9 @@ #define DRIVER_NAME "da8xx_lcdc" /* LCD Status Register */ +#define LCD_END_OF_FRAME1 BIT(9) #define LCD_END_OF_FRAME0 BIT(8) +#define LCD_PL_LOAD_DONE BIT(6) #define LCD_FIFO_UNDERFLOW BIT(5) #define LCD_SYNC_LOST BIT(2) @@ -58,11 +60,13 @@ #define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) #define PALETTE_AND_DATA 0x00 #define PALETTE_ONLY 0x01 +#define DATA_ONLY 0x02 #define LCD_MONO_8BIT_MODE BIT(9) #define LCD_RASTER_ORDER BIT(8) #define LCD_TFT_MODE BIT(7) #define LCD_UNDERFLOW_INT_ENA BIT(6) +#define LCD_PL_ENABLE BIT(4) #define LCD_MONOCHROME_MODE BIT(1) #define LCD_RASTER_ENABLE BIT(0) #define LCD_TFT_ALT_ENABLE BIT(23) @@ -87,6 +91,10 @@ #define LCD_DMA_CTRL_REG 0x40 #define LCD_DMA_FRM_BUF_BASE_ADDR_0_REG 0x44 #define LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG 0x48 +#define LCD_DMA_FRM_BUF_BASE_ADDR_1_REG 0x4C +#define LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG 0x50 + +#define LCD_NUM_BUFFERS 2 #define WSI_TIMEOUT 50 #define PALETTE_SIZE 256 @@ -111,13 +119,20 @@ static inline void lcdc_write(unsigned int val, unsigned int addr) struct da8xx_fb_par { resource_size_t p_palette_base; unsigned char *v_palette_base; + dma_addr_t vram_phys; + unsigned long vram_size; + void *vram_virt; + unsigned int dma_start; + unsigned int dma_end; struct clk *lcdc_clk; int irq; unsigned short pseudo_palette[16]; - unsigned int databuf_sz; unsigned int palette_sz; unsigned int pxl_clk; int blank; + wait_queue_head_t vsync_wait; + int vsync_flag; + int vsync_timeout; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif @@ -148,9 +163,9 @@ static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = { .type = FB_TYPE_PACKED_PIXELS, .type_aux = 0, .visual = FB_VISUAL_PSEUDOCOLOR, - .xpanstep = 1, + .xpanstep = 0, .ypanstep = 1, - .ywrapstep = 1, + .ywrapstep = 0, .accel = FB_ACCEL_NONE }; @@ -221,22 +236,48 @@ static inline void lcd_disable_raster(void) static void lcd_blit(int load_mode, struct da8xx_fb_par *par) { - u32 tmp = par->p_palette_base + par->databuf_sz - 4; - u32 reg; + u32 start; + u32 end; + u32 reg_ras; + u32 reg_dma; + + /* init reg to clear PLM (loading mode) fields */ + reg_ras = lcdc_read(LCD_RASTER_CTRL_REG); + reg_ras &= ~(3 << 20); + + reg_dma = lcdc_read(LCD_DMA_CTRL_REG); + + if (load_mode == LOAD_DATA) { + start = par->dma_start; + end = par->dma_end; + + reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY); + reg_dma |= LCD_END_OF_FRAME_INT_ENA; + reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE; + + lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(end, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_1_REG); + lcdc_write(end, LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG); + } else if (load_mode == LOAD_PALETTE) { + start = par->p_palette_base; + end = start + par->palette_sz - 1; + + reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); + reg_ras |= LCD_PL_ENABLE; + + lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(end, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + } - /* Update the databuf in the hw. */ - lcdc_write(par->p_palette_base, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); - lcdc_write(tmp, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + lcdc_write(reg_dma, LCD_DMA_CTRL_REG); + lcdc_write(reg_ras, LCD_RASTER_CTRL_REG); - /* Start the DMA. */ - reg = lcdc_read(LCD_RASTER_CTRL_REG); - reg &= ~(3 << 20); - if (load_mode == LOAD_DATA) - reg |= LCD_PALETTE_LOAD_MODE(PALETTE_AND_DATA); - else if (load_mode == LOAD_PALETTE) - reg |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); - - lcdc_write(reg, LCD_RASTER_CTRL_REG); + /* + * The Raster enable bit must be set after all other control fields are + * set. + */ + lcd_enable_raster(); } /* Configure the Burst Size of DMA */ @@ -368,12 +409,8 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, u32 bpp, u32 raster_order) { - u32 bpl, reg; + u32 reg; - /* Disable Dual Frame Buffer. */ - reg = lcdc_read(LCD_DMA_CTRL_REG); - lcdc_write(reg & ~LCD_DUAL_FRAME_BUFFER_ENABLE, - LCD_DMA_CTRL_REG); /* Set the Panel Width */ /* Pixels per line = (PPL + 1)*16 */ /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ @@ -410,9 +447,6 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, return -EINVAL; } - bpl = width * bpp / 8; - par->databuf_sz = height * bpl + par->palette_sz; - return 0; } @@ -421,8 +455,9 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, struct fb_info *info) { struct da8xx_fb_par *par = info->par; - unsigned short *palette = (unsigned short *)par->v_palette_base; + unsigned short *palette = (unsigned short *) par->v_palette_base; u_short pal; + int update_hw = 0; if (regno > 255) return 1; @@ -439,8 +474,10 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, pal |= (green & 0x00f0); pal |= (blue & 0x000f); - palette[regno] = pal; - + if (palette[regno] != pal) { + update_hw = 1; + palette[regno] = pal; + } } else if ((info->var.bits_per_pixel == 16) && regno < 16) { red >>= (16 - info->var.red.length); red <<= info->var.red.offset; @@ -453,9 +490,16 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, par->pseudo_palette[regno] = red | green | blue; - palette[0] = 0x4000; + if (palette[0] != 0x4000) { + update_hw = 1; + palette[0] = 0x4000; + } } + /* Update the palette in the h/w as needed. */ + if (update_hw) + lcd_blit(LOAD_PALETTE, par); + return 0; } @@ -541,15 +585,54 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, static irqreturn_t lcdc_irq_handler(int irq, void *arg) { + struct da8xx_fb_par *par = arg; u32 stat = lcdc_read(LCD_STAT_REG); + u32 reg_ras; if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { lcd_disable_raster(); lcdc_write(stat, LCD_STAT_REG); lcd_enable_raster(); - } else + } else if (stat & LCD_PL_LOAD_DONE) { + /* + * Must disable raster before changing state of any control bit. + * And also must be disabled before clearing the PL loading + * interrupt via the following write to the status register. If + * this is done after then one gets multiple PL done interrupts. + */ + lcd_disable_raster(); + lcdc_write(stat, LCD_STAT_REG); + /* Disable PL completion inerrupt */ + reg_ras = lcdc_read(LCD_RASTER_CTRL_REG); + reg_ras &= ~LCD_PL_ENABLE; + lcdc_write(reg_ras, LCD_RASTER_CTRL_REG); + + /* Setup and start data loading mode */ + lcd_blit(LOAD_DATA, par); + } else { + lcdc_write(stat, LCD_STAT_REG); + + if (stat & LCD_END_OF_FRAME0) { + lcdc_write(par->dma_start, + LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(par->dma_end, + LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + par->vsync_flag = 1; + wake_up_interruptible(&par->vsync_wait); + } + + if (stat & LCD_END_OF_FRAME1) { + lcdc_write(par->dma_start, + LCD_DMA_FRM_BUF_BASE_ADDR_1_REG); + lcdc_write(par->dma_end, + LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG); + par->vsync_flag = 1; + wake_up_interruptible(&par->vsync_wait); + } + } + return IRQ_HANDLED; } @@ -654,9 +737,10 @@ static int __devexit fb_remove(struct platform_device *dev) unregister_framebuffer(info); fb_dealloc_cmap(&info->cmap); - dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, - info->screen_base - PAGE_SIZE, - info->fix.smem_start); + dma_free_coherent(NULL, PALETTE_SIZE, par->v_palette_base, + par->p_palette_base); + dma_free_coherent(NULL, par->vram_size, par->vram_virt, + par->vram_phys); free_irq(par->irq, par); clk_disable(par->lcdc_clk); clk_put(par->lcdc_clk); @@ -668,6 +752,39 @@ static int __devexit fb_remove(struct platform_device *dev) return 0; } +/* + * Function to wait for vertical sync which for this LCD peripheral + * translates into waiting for the current raster frame to complete. + */ +static int fb_wait_for_vsync(struct fb_info *info) +{ + struct da8xx_fb_par *par = info->par; + int ret; + + /* + * Set flag to 0 and wait for isr to set to 1. It would seem there is a + * race condition here where the ISR could have occured just before or + * just after this set. But since we are just coarsely waiting for + * a frame to complete then that's OK. i.e. if the frame completed + * just before this code executed then we have to wait another full + * frame time but there is no way to avoid such a situation. On the + * other hand if the frame completed just after then we don't need + * to wait long at all. Either way we are guaranteed to return to the + * user immediately after a frame completion which is all that is + * required. + */ + par->vsync_flag = 0; + ret = wait_event_interruptible_timeout(par->vsync_wait, + par->vsync_flag != 0, + par->vsync_timeout); + if (ret < 0) + return ret; + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + static int fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -697,6 +814,8 @@ static int fb_ioctl(struct fb_info *info, unsigned int cmd, sync_arg.pulse_width, sync_arg.front_porch); break; + case FBIO_WAITFORVSYNC: + return fb_wait_for_vsync(info); default: return -EINVAL; } @@ -732,10 +851,47 @@ static int cfb_blank(int blank, struct fb_info *info) return ret; } +/* + * Set new x,y offsets in the virtual display for the visible area and switch + * to the new mode. + */ +static int da8xx_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + int ret = 0; + struct fb_var_screeninfo new_var; + struct da8xx_fb_par *par = fbi->par; + struct fb_fix_screeninfo *fix = &fbi->fix; + unsigned int end; + unsigned int start; + + if (var->xoffset != fbi->var.xoffset || + var->yoffset != fbi->var.yoffset) { + memcpy(&new_var, &fbi->var, sizeof(new_var)); + new_var.xoffset = var->xoffset; + new_var.yoffset = var->yoffset; + if (fb_check_var(&new_var, fbi)) + ret = -EINVAL; + else { + memcpy(&fbi->var, &new_var, sizeof(new_var)); + + start = fix->smem_start + + new_var.yoffset * fix->line_length + + new_var.xoffset * var->bits_per_pixel / 8; + end = start + var->yres * fix->line_length - 1; + par->dma_start = start; + par->dma_end = end; + } + } + + return ret; +} + static struct fb_ops da8xx_fb_ops = { .owner = THIS_MODULE, .fb_check_var = fb_check_var, .fb_setcolreg = fb_setcolreg, + .fb_pan_display = da8xx_pan_display, .fb_ioctl = fb_ioctl, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, @@ -829,40 +985,53 @@ static int __init fb_probe(struct platform_device *device) } /* allocate frame buffer */ - da8xx_fb_info->screen_base = dma_alloc_coherent(NULL, - par->databuf_sz + PAGE_SIZE, - (resource_size_t *) - &da8xx_fb_info->fix.smem_start, - GFP_KERNEL | GFP_DMA); - - if (!da8xx_fb_info->screen_base) { + par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp; + par->vram_size = PAGE_ALIGN(par->vram_size/8); + par->vram_size = par->vram_size * LCD_NUM_BUFFERS; + + par->vram_virt = dma_alloc_coherent(NULL, + par->vram_size, + (resource_size_t *) &par->vram_phys, + GFP_KERNEL | GFP_DMA); + if (!par->vram_virt) { dev_err(&device->dev, "GLCD: kmalloc for frame buffer failed\n"); ret = -EINVAL; goto err_release_fb; } - /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */ - par->v_palette_base = da8xx_fb_info->screen_base + - (PAGE_SIZE - par->palette_sz); - par->p_palette_base = da8xx_fb_info->fix.smem_start + - (PAGE_SIZE - par->palette_sz); - - /* the rest of the frame buffer is pixel data */ - da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz; - da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz; - da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; - da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; + da8xx_fb_info->screen_base = (char __iomem *) par->vram_virt; + da8xx_fb_fix.smem_start = par->vram_phys; + da8xx_fb_fix.smem_len = par->vram_size; + da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; + + par->dma_start = par->vram_phys; + par->dma_end = par->dma_start + lcdc_info->height * + da8xx_fb_fix.line_length - 1; + + /* allocate palette buffer */ + par->v_palette_base = dma_alloc_coherent(NULL, + PALETTE_SIZE, + (resource_size_t *) + &par->p_palette_base, + GFP_KERNEL | GFP_DMA); + if (!par->v_palette_base) { + dev_err(&device->dev, + "GLCD: kmalloc for palette buffer failed\n"); + ret = -EINVAL; + goto err_release_fb_mem; + } + memset(par->v_palette_base, 0, PALETTE_SIZE); par->irq = platform_get_irq(device, 0); if (par->irq < 0) { ret = -ENOENT; - goto err_release_fb_mem; + goto err_release_pl_mem; } ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); if (ret) - goto err_release_fb_mem; + goto err_release_pl_mem; /* Initialize par */ da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; @@ -870,8 +1039,8 @@ static int __init fb_probe(struct platform_device *device) da8xx_fb_var.xres = lcdc_info->width; da8xx_fb_var.xres_virtual = lcdc_info->width; - da8xx_fb_var.yres = lcdc_info->height; - da8xx_fb_var.yres_virtual = lcdc_info->height; + da8xx_fb_var.yres = lcdc_info->height; + da8xx_fb_var.yres_virtual = lcdc_info->height * LCD_NUM_BUFFERS; da8xx_fb_var.grayscale = lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; @@ -892,18 +1061,18 @@ static int __init fb_probe(struct platform_device *device) ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); if (ret) goto err_free_irq; - - /* First palette_sz byte of the frame buffer is the palette */ da8xx_fb_info->cmap.len = par->palette_sz; - /* Flush the buffer to the screen. */ - lcd_blit(LOAD_DATA, par); - /* initialize var_screeninfo */ da8xx_fb_var.activate = FB_ACTIVATE_FORCE; fb_set_var(da8xx_fb_info, &da8xx_fb_var); dev_set_drvdata(&device->dev, da8xx_fb_info); + + /* initialize the vsync wait queue */ + init_waitqueue_head(&par->vsync_wait); + par->vsync_timeout = HZ / 5; + /* Register the Frame Buffer */ if (register_framebuffer(da8xx_fb_info) < 0) { dev_err(&device->dev, @@ -919,10 +1088,6 @@ static int __init fb_probe(struct platform_device *device) goto err_cpu_freq; } #endif - - /* enable raster engine */ - lcd_enable_raster(); - return 0; #ifdef CONFIG_CPU_FREQ @@ -936,10 +1101,12 @@ err_dealloc_cmap: err_free_irq: free_irq(par->irq, par); +err_release_pl_mem: + dma_free_coherent(NULL, PALETTE_SIZE, par->v_palette_base, + par->p_palette_base); + err_release_fb_mem: - dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, - da8xx_fb_info->screen_base - PAGE_SIZE, - da8xx_fb_info->fix.smem_start); + dma_free_coherent(NULL, par->vram_size, par->vram_virt, par->vram_phys); err_release_fb: framebuffer_release(da8xx_fb_info); diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 6113c47e095..073c9b408cf 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -66,7 +66,7 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma, return 0; } -int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync) +int fb_deferred_io_fsync(struct file *file, int datasync) { struct fb_info *info = file->private_data; @@ -155,25 +155,41 @@ static void fb_deferred_io_work(struct work_struct *work) { struct fb_info *info = container_of(work, struct fb_info, deferred_work.work); - struct list_head *node, *next; - struct page *cur; struct fb_deferred_io *fbdefio = info->fbdefio; + struct page *page, *tmp_page; + struct list_head *node, *tmp_node; + struct list_head non_dirty; + + INIT_LIST_HEAD(&non_dirty); /* here we mkclean the pages, then do all deferred IO */ mutex_lock(&fbdefio->lock); - list_for_each_entry(cur, &fbdefio->pagelist, lru) { - lock_page(cur); - page_mkclean(cur); - unlock_page(cur); + list_for_each_entry_safe(page, tmp_page, &fbdefio->pagelist, lru) { + lock_page(page); + /* + * The workqueue callback can be triggered after a + * ->page_mkwrite() call but before the PTE has been marked + * dirty. In this case page_mkclean() won't "rearm" the page. + * + * To avoid this, remove those "non-dirty" pages from the + * pagelist before calling the driver's callback, then add + * them back to get processed on the next work iteration. + * At that time, their PTEs will hopefully be dirty for real. + */ + if (!page_mkclean(page)) + list_move_tail(&page->lru, &non_dirty); + unlock_page(page); } /* driver's callback with pagelist */ fbdefio->deferred_io(info, &fbdefio->pagelist); - /* clear the list */ - list_for_each_safe(node, next, &fbdefio->pagelist) { + /* clear the list... */ + list_for_each_safe(node, tmp_node, &fbdefio->pagelist) { list_del(node); } + /* ... and add back the "non-dirty" pages to the list */ + list_splice_tail(&non_dirty, &fbdefio->pagelist); mutex_unlock(&fbdefio->lock); } @@ -202,6 +218,7 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_open); void fb_deferred_io_cleanup(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; + struct list_head *node, *tmp_node; struct page *page; int i; @@ -209,6 +226,13 @@ void fb_deferred_io_cleanup(struct fb_info *info) cancel_delayed_work(&info->deferred_work); flush_scheduled_work(); + /* the list may have still some non-dirty pages at this point */ + mutex_lock(&fbdefio->lock); + list_for_each_safe(node, tmp_node, &fbdefio->pagelist) { + list_del(node); + } + mutex_unlock(&fbdefio->lock); + /* clear out the mapping that we setup */ for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) { page = fb_deferred_io_page(info, i); diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c index 8bbf251f83d..af8f0f2cc78 100644 --- a/drivers/video/hgafb.c +++ b/drivers/video/hgafb.c @@ -106,7 +106,7 @@ static DEFINE_SPINLOCK(hga_reg_lock); /* Framebuffer driver structures */ -static struct fb_var_screeninfo __initdata hga_default_var = { +static struct fb_var_screeninfo hga_default_var __devinitdata = { .xres = 720, .yres = 348, .xres_virtual = 720, @@ -120,7 +120,7 @@ static struct fb_var_screeninfo __initdata hga_default_var = { .width = -1, }; -static struct fb_fix_screeninfo __initdata hga_fix = { +static struct fb_fix_screeninfo hga_fix __devinitdata = { .id = "HGA", .type = FB_TYPE_PACKED_PIXELS, /* (not sure) */ .visual = FB_VISUAL_MONO10, @@ -276,7 +276,7 @@ static void hga_blank(int blank_mode) spin_unlock_irqrestore(&hga_reg_lock, flags); } -static int __init hga_card_detect(void) +static int __devinit hga_card_detect(void) { int count = 0; void __iomem *p, *q; @@ -596,7 +596,7 @@ static int __devinit hgafb_probe(struct platform_device *pdev) return 0; } -static int hgafb_remove(struct platform_device *pdev) +static int __devexit hgafb_remove(struct platform_device *pdev) { struct fb_info *info = platform_get_drvdata(pdev); @@ -621,7 +621,7 @@ static int hgafb_remove(struct platform_device *pdev) static struct platform_driver hgafb_driver = { .probe = hgafb_probe, - .remove = hgafb_remove, + .remove = __devexit_p(hgafb_remove), .driver = { .name = "hgafb", }, diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c index 393f3f3d3df..cfb8d645101 100644 --- a/drivers/video/hitfb.c +++ b/drivers/video/hitfb.c @@ -30,14 +30,14 @@ #define WIDTH 640 -static struct fb_var_screeninfo hitfb_var __initdata = { +static struct fb_var_screeninfo hitfb_var __devinitdata = { .activate = FB_ACTIVATE_NOW, .height = -1, .width = -1, .vmode = FB_VMODE_NONINTERLACED, }; -static struct fb_fix_screeninfo hitfb_fix __initdata = { +static struct fb_fix_screeninfo hitfb_fix __devinitdata = { .id = "Hitachi HD64461", .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE, @@ -417,7 +417,7 @@ err_fb: return ret; } -static int __exit hitfb_remove(struct platform_device *dev) +static int __devexit hitfb_remove(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); @@ -462,7 +462,7 @@ static const struct dev_pm_ops hitfb_dev_pm_ops = { static struct platform_driver hitfb_driver = { .probe = hitfb_probe, - .remove = __exit_p(hitfb_remove), + .remove = __devexit_p(hitfb_remove), .driver = { .name = "hitfb", .owner = THIS_MODULE, diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index 40984551c92..6b51175629c 100644 --- a/drivers/video/intelfb/intelfb.h +++ b/drivers/video/intelfb/intelfb.h @@ -371,10 +371,6 @@ struct intelfb_info { ((dinfo)->chipset == INTEL_965G) || \ ((dinfo)->chipset == INTEL_965GM)) -#ifndef FBIO_WAITFORVSYNC -#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) -#endif - /*** function prototypes ***/ extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var); diff --git a/drivers/video/nuc900fb.c b/drivers/video/nuc900fb.c index 6bf0d460a73..d4cde79ea15 100644 --- a/drivers/video/nuc900fb.c +++ b/drivers/video/nuc900fb.c @@ -667,7 +667,7 @@ release_irq: release_regs: iounmap(fbi->io); release_mem_region: - release_mem_region((unsigned long)fbi->mem, size); + release_mem_region(res->start, size); free_fb: framebuffer_release(fbinfo); return ret; diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 2b094dec4a5..46b430978bc 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -631,7 +631,7 @@ static struct fb_ops s3c2410fb_ops = { * cache. Once this area is remapped, all virtual memory * access to the video memory should occur at the new region. */ -static int __init s3c2410fb_map_video_memory(struct fb_info *info) +static int __devinit s3c2410fb_map_video_memory(struct fb_info *info) { struct s3c2410fb_info *fbi = info->par; dma_addr_t map_dma; @@ -814,7 +814,7 @@ static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info) static char driver_name[] = "s3c2410fb"; -static int __init s3c24xxfb_probe(struct platform_device *pdev, +static int __devinit s3c24xxfb_probe(struct platform_device *pdev, enum s3c_drv_type drv_type) { struct s3c2410fb_info *info; @@ -1018,7 +1018,7 @@ static int __devinit s3c2412fb_probe(struct platform_device *pdev) /* * Cleanup */ -static int s3c2410fb_remove(struct platform_device *pdev) +static int __devexit s3c2410fb_remove(struct platform_device *pdev) { struct fb_info *fbinfo = platform_get_drvdata(pdev); struct s3c2410fb_info *info = fbinfo->par; @@ -1096,7 +1096,7 @@ static int s3c2410fb_resume(struct platform_device *dev) static struct platform_driver s3c2410fb_driver = { .probe = s3c2410fb_probe, - .remove = s3c2410fb_remove, + .remove = __devexit_p(s3c2410fb_remove), .suspend = s3c2410fb_suspend, .resume = s3c2410fb_resume, .driver = { @@ -1107,7 +1107,7 @@ static struct platform_driver s3c2410fb_driver = { static struct platform_driver s3c2412fb_driver = { .probe = s3c2412fb_probe, - .remove = s3c2410fb_remove, + .remove = __devexit_p(s3c2410fb_remove), .suspend = s3c2410fb_suspend, .resume = s3c2410fb_resume, .driver = { diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index d4471b4c037..dce8c97b433 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c @@ -71,7 +71,8 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64", "S3 Trio64UV+", "S3 Trio64V2/DX", "S3 Trio64V2/GX", "S3 Plato/PX", "S3 Aurora64VP", "S3 Virge", "S3 Virge/VX", "S3 Virge/DX", "S3 Virge/GX", - "S3 Virge/GX2", "S3 Virge/GX2P", "S3 Virge/GX2P"}; + "S3 Virge/GX2", "S3 Virge/GX2P", "S3 Virge/GX2P", + "S3 Trio3D/1X", "S3 Trio3D/2X", "S3 Trio3D/2X"}; #define CHIP_UNKNOWN 0x00 #define CHIP_732_TRIO32 0x01 @@ -89,10 +90,14 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64", #define CHIP_356_VIRGE_GX2 0x0D #define CHIP_357_VIRGE_GX2P 0x0E #define CHIP_359_VIRGE_GX2P 0x0F +#define CHIP_360_TRIO3D_1X 0x10 +#define CHIP_362_TRIO3D_2X 0x11 +#define CHIP_368_TRIO3D_2X 0x12 #define CHIP_XXX_TRIO 0x80 #define CHIP_XXX_TRIO64V2_DXGX 0x81 #define CHIP_XXX_VIRGE_DXGX 0x82 +#define CHIP_36X_TRIO3D_1X_2X 0x83 #define CHIP_UNDECIDED_FLAG 0x80 #define CHIP_MASK 0xFF @@ -324,6 +329,7 @@ static void s3fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) static void s3_set_pixclock(struct fb_info *info, u32 pixclock) { + struct s3fb_info *par = info->par; u16 m, n, r; u8 regval; int rv; @@ -339,7 +345,13 @@ static void s3_set_pixclock(struct fb_info *info, u32 pixclock) vga_w(NULL, VGA_MIS_W, regval | VGA_MIS_ENB_PLL_LOAD); /* Set S3 clock registers */ - vga_wseq(NULL, 0x12, ((n - 2) | (r << 5))); + if (par->chip == CHIP_360_TRIO3D_1X || + par->chip == CHIP_362_TRIO3D_2X || + par->chip == CHIP_368_TRIO3D_2X) { + vga_wseq(NULL, 0x12, (n - 2) | ((r & 3) << 6)); /* n and two bits of r */ + vga_wseq(NULL, 0x29, r >> 2); /* remaining highest bit of r */ + } else + vga_wseq(NULL, 0x12, (n - 2) | (r << 5)); vga_wseq(NULL, 0x13, m - 2); udelay(1000); @@ -456,7 +468,7 @@ static int s3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) static int s3fb_set_par(struct fb_info *info) { struct s3fb_info *par = info->par; - u32 value, mode, hmul, offset_value, screen_size, multiplex; + u32 value, mode, hmul, offset_value, screen_size, multiplex, dbytes; u32 bpp = info->var.bits_per_pixel; if (bpp != 0) { @@ -518,7 +530,7 @@ static int s3fb_set_par(struct fb_info *info) svga_wcrt_mask(0x33, 0x00, 0x08); /* no DDR ? */ svga_wcrt_mask(0x43, 0x00, 0x01); /* no DDR ? */ - svga_wcrt_mask(0x5D, 0x00, 0x28); // Clear strange HSlen bits + svga_wcrt_mask(0x5D, 0x00, 0x28); /* Clear strange HSlen bits */ /* svga_wcrt_mask(0x58, 0x03, 0x03); */ @@ -530,10 +542,14 @@ static int s3fb_set_par(struct fb_info *info) pr_debug("fb%d: offset register : %d\n", info->node, offset_value); svga_wcrt_multi(s3_offset_regs, offset_value); - vga_wcrt(NULL, 0x54, 0x18); /* M parameter */ - vga_wcrt(NULL, 0x60, 0xff); /* N parameter */ - vga_wcrt(NULL, 0x61, 0xff); /* L parameter */ - vga_wcrt(NULL, 0x62, 0xff); /* L parameter */ + if (par->chip != CHIP_360_TRIO3D_1X && + par->chip != CHIP_362_TRIO3D_2X && + par->chip != CHIP_368_TRIO3D_2X) { + vga_wcrt(NULL, 0x54, 0x18); /* M parameter */ + vga_wcrt(NULL, 0x60, 0xff); /* N parameter */ + vga_wcrt(NULL, 0x61, 0xff); /* L parameter */ + vga_wcrt(NULL, 0x62, 0xff); /* L parameter */ + } vga_wcrt(NULL, 0x3A, 0x35); svga_wattr(0x33, 0x00); @@ -570,6 +586,16 @@ static int s3fb_set_par(struct fb_info *info) vga_wcrt(NULL, 0x66, 0x90); } + if (par->chip == CHIP_360_TRIO3D_1X || + par->chip == CHIP_362_TRIO3D_2X || + par->chip == CHIP_368_TRIO3D_2X) { + dbytes = info->var.xres * ((bpp+7)/8); + vga_wcrt(NULL, 0x91, (dbytes + 7) / 8); + vga_wcrt(NULL, 0x90, (((dbytes + 7) / 8) >> 8) | 0x80); + + vga_wcrt(NULL, 0x66, 0x81); + } + svga_wcrt_mask(0x31, 0x00, 0x40); multiplex = 0; hmul = 1; @@ -615,11 +641,13 @@ static int s3fb_set_par(struct fb_info *info) break; case 3: pr_debug("fb%d: 8 bit pseudocolor\n", info->node); - if (info->var.pixclock > 20000) { - svga_wcrt_mask(0x50, 0x00, 0x30); + svga_wcrt_mask(0x50, 0x00, 0x30); + if (info->var.pixclock > 20000 || + par->chip == CHIP_360_TRIO3D_1X || + par->chip == CHIP_362_TRIO3D_2X || + par->chip == CHIP_368_TRIO3D_2X) svga_wcrt_mask(0x67, 0x00, 0xF0); - } else { - svga_wcrt_mask(0x50, 0x00, 0x30); + else { svga_wcrt_mask(0x67, 0x10, 0xF0); multiplex = 1; } @@ -634,7 +662,10 @@ static int s3fb_set_par(struct fb_info *info) } else { svga_wcrt_mask(0x50, 0x10, 0x30); svga_wcrt_mask(0x67, 0x30, 0xF0); - hmul = 2; + if (par->chip != CHIP_360_TRIO3D_1X && + par->chip != CHIP_362_TRIO3D_2X && + par->chip != CHIP_368_TRIO3D_2X) + hmul = 2; } break; case 5: @@ -647,7 +678,10 @@ static int s3fb_set_par(struct fb_info *info) } else { svga_wcrt_mask(0x50, 0x10, 0x30); svga_wcrt_mask(0x67, 0x50, 0xF0); - hmul = 2; + if (par->chip != CHIP_360_TRIO3D_1X && + par->chip != CHIP_362_TRIO3D_2X && + par->chip != CHIP_368_TRIO3D_2X) + hmul = 2; } break; case 6: @@ -866,6 +900,17 @@ static int __devinit s3_identification(int chip) return CHIP_385_VIRGE_GX; } + if (chip == CHIP_36X_TRIO3D_1X_2X) { + switch (vga_rcrt(NULL, 0x2f)) { + case 0x00: + return CHIP_360_TRIO3D_1X; + case 0x01: + return CHIP_362_TRIO3D_2X; + case 0x02: + return CHIP_368_TRIO3D_2X; + } + } + return CHIP_UNKNOWN; } @@ -930,17 +975,32 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i vga_wcrt(NULL, 0x38, 0x48); vga_wcrt(NULL, 0x39, 0xA5); - /* Find how many physical memory there is on card */ - /* 0x36 register is accessible even if other registers are locked */ - regval = vga_rcrt(NULL, 0x36); - info->screen_size = s3_memsizes[regval >> 5] << 10; - info->fix.smem_len = info->screen_size; - + /* Identify chip type */ par->chip = id->driver_data & CHIP_MASK; par->rev = vga_rcrt(NULL, 0x2f); if (par->chip & CHIP_UNDECIDED_FLAG) par->chip = s3_identification(par->chip); + /* Find how many physical memory there is on card */ + /* 0x36 register is accessible even if other registers are locked */ + regval = vga_rcrt(NULL, 0x36); + if (par->chip == CHIP_360_TRIO3D_1X || + par->chip == CHIP_362_TRIO3D_2X || + par->chip == CHIP_368_TRIO3D_2X) { + switch ((regval & 0xE0) >> 5) { + case 0: /* 8MB -- only 4MB usable for display */ + case 1: /* 4MB with 32-bit bus */ + case 2: /* 4MB */ + info->screen_size = 4 << 20; + break; + case 6: /* 2MB */ + info->screen_size = 2 << 20; + break; + } + } else + info->screen_size = s3_memsizes[regval >> 5] << 10; + info->fix.smem_len = info->screen_size; + /* Find MCLK frequency */ regval = vga_rseq(NULL, 0x10); par->mclk_freq = ((vga_rseq(NULL, 0x11) + 2) * 14318) / ((regval & 0x1F) + 2); @@ -1131,6 +1191,7 @@ static struct pci_device_id s3_devices[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A10), .driver_data = CHIP_356_VIRGE_GX2}, {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A11), .driver_data = CHIP_357_VIRGE_GX2P}, {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P}, + {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X}, {0, 0, 0, 0, 0, 0, 0} }; diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c index 7a3a5e28eca..53455f29551 100644 --- a/drivers/video/sgivwfb.c +++ b/drivers/video/sgivwfb.c @@ -47,7 +47,7 @@ static int ywrap = 0; static int flatpanel_id = -1; -static struct fb_fix_screeninfo sgivwfb_fix __initdata = { +static struct fb_fix_screeninfo sgivwfb_fix __devinitdata = { .id = "SGI Vis WS FB", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, @@ -57,7 +57,7 @@ static struct fb_fix_screeninfo sgivwfb_fix __initdata = { .line_length = 640, }; -static struct fb_var_screeninfo sgivwfb_var __initdata = { +static struct fb_var_screeninfo sgivwfb_var __devinitdata = { /* 640x480, 8 bpp */ .xres = 640, .yres = 480, @@ -79,7 +79,7 @@ static struct fb_var_screeninfo sgivwfb_var __initdata = { .vmode = FB_VMODE_NONINTERLACED }; -static struct fb_var_screeninfo sgivwfb_var1600sw __initdata = { +static struct fb_var_screeninfo sgivwfb_var1600sw __devinitdata = { /* 1600x1024, 8 bpp */ .xres = 1600, .yres = 1024, @@ -825,7 +825,7 @@ fail_ioremap_regs: return -ENXIO; } -static int sgivwfb_remove(struct platform_device *dev) +static int __devexit sgivwfb_remove(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); @@ -845,7 +845,7 @@ static int sgivwfb_remove(struct platform_device *dev) static struct platform_driver sgivwfb_driver = { .probe = sgivwfb_probe, - .remove = sgivwfb_remove, + .remove = __devexit_p(sgivwfb_remove), .driver = { .name = "sgivwfb", }, diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index a531a0f7cdf..559bf1727a2 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -1845,7 +1845,7 @@ sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - strcpy(fix->id, ivideo->myid); + strlcpy(fix->id, ivideo->myid, sizeof(fix->id)); mutex_lock(&info->mm_lock); fix->smem_start = ivideo->video_base + ivideo->video_offset; diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c index 9b5532b4de3..bc67251f1a2 100644 --- a/drivers/video/vfb.c +++ b/drivers/video/vfb.c @@ -78,7 +78,7 @@ static void rvfree(void *mem, unsigned long size) vfree(mem); } -static struct fb_var_screeninfo vfb_default __initdata = { +static struct fb_var_screeninfo vfb_default __devinitdata = { .xres = 640, .yres = 480, .xres_virtual = 640, @@ -100,7 +100,7 @@ static struct fb_var_screeninfo vfb_default __initdata = { .vmode = FB_VMODE_NONINTERLACED, }; -static struct fb_fix_screeninfo vfb_fix __initdata = { +static struct fb_fix_screeninfo vfb_fix __devinitdata = { .id = "Virtual FB", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 149c47ac7e9..28ccab44a39 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -65,7 +65,7 @@ struct vga16fb_par { /* --------------------------------------------------------------------- */ -static struct fb_var_screeninfo vga16fb_defined __initdata = { +static struct fb_var_screeninfo vga16fb_defined __devinitdata = { .xres = 640, .yres = 480, .xres_virtual = 640, @@ -85,7 +85,7 @@ static struct fb_var_screeninfo vga16fb_defined __initdata = { }; /* name should not depend on EGA/VGA */ -static struct fb_fix_screeninfo vga16fb_fix __initdata = { +static struct fb_fix_screeninfo vga16fb_fix __devinitdata = { .id = "VGA16 VGA", .smem_start = VGA_FB_PHYS, .smem_len = VGA_FB_PHYS_LEN, @@ -1287,7 +1287,7 @@ static struct fb_ops vga16fb_ops = { }; #ifndef MODULE -static int vga16fb_setup(char *options) +static int __init vga16fb_setup(char *options) { char *this_opt; @@ -1393,7 +1393,7 @@ static int __devinit vga16fb_probe(struct platform_device *dev) return ret; } -static int vga16fb_remove(struct platform_device *dev) +static int __devexit vga16fb_remove(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); @@ -1405,7 +1405,7 @@ static int vga16fb_remove(struct platform_device *dev) static struct platform_driver vga16fb_driver = { .probe = vga16fb_probe, - .remove = vga16fb_remove, + .remove = __devexit_p(vga16fb_remove), .driver = { .name = "vga16fb", }, diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 2bc40e682f9..1082541358f 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -578,14 +578,9 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) break; case VIAFB_SET_GAMMA_LUT: - viafb_gamma_table = kmalloc(256 * sizeof(u32), GFP_KERNEL); - if (!viafb_gamma_table) - return -ENOMEM; - if (copy_from_user(viafb_gamma_table, argp, - 256 * sizeof(u32))) { - kfree(viafb_gamma_table); - return -EFAULT; - } + viafb_gamma_table = memdup_user(argp, 256 * sizeof(u32)); + if (IS_ERR(viafb_gamma_table)) + return PTR_ERR(viafb_gamma_table); viafb_set_gamma_table(viafb_bpp, viafb_gamma_table); kfree(viafb_gamma_table); break; diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c index 31b0e17ed09..e66b8b19ce5 100644 --- a/drivers/video/w100fb.c +++ b/drivers/video/w100fb.c @@ -53,7 +53,7 @@ static void w100_update_enable(void); static void w100_update_disable(void); static void calc_hsync(struct w100fb_par *par); static void w100_init_graphic_engine(struct w100fb_par *par); -struct w100_pll_info *w100_get_xtal_table(unsigned int freq); +struct w100_pll_info *w100_get_xtal_table(unsigned int freq) __devinit; /* Pseudo palette size */ #define MAX_PALETTES 16 @@ -782,7 +782,7 @@ out: } -static int w100fb_remove(struct platform_device *pdev) +static int __devexit w100fb_remove(struct platform_device *pdev) { struct fb_info *info = platform_get_drvdata(pdev); struct w100fb_par *par=info->par; @@ -1020,7 +1020,7 @@ static struct pll_entries { { 0 }, }; -struct w100_pll_info *w100_get_xtal_table(unsigned int freq) +struct w100_pll_info __devinit *w100_get_xtal_table(unsigned int freq) { struct pll_entries *pll_entry = w100_pll_tables; @@ -1611,7 +1611,7 @@ static void w100_vsync(void) static struct platform_driver w100fb_driver = { .probe = w100fb_probe, - .remove = w100fb_remove, + .remove = __devexit_p(w100fb_remove), .suspend = w100fb_suspend, .resume = w100fb_resume, .driver = { @@ -1619,7 +1619,7 @@ static struct platform_driver w100fb_driver = { }, }; -int __devinit w100fb_init(void) +int __init w100fb_init(void) { return platform_driver_register(&w100fb_driver); } |