aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/backlight
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r--drivers/video/backlight/88pm860x_bl.c278
-rw-r--r--drivers/video/backlight/Kconfig355
-rw-r--r--drivers/video/backlight/Makefile68
-rw-r--r--drivers/video/backlight/aat2870_bl.c240
-rw-r--r--drivers/video/backlight/adp5520_bl.c389
-rw-r--r--drivers/video/backlight/adp8860_bl.c819
-rw-r--r--drivers/video/backlight/adp8870_bl.c991
-rw-r--r--drivers/video/backlight/ams369fg06.c577
-rw-r--r--drivers/video/backlight/apple_bl.c257
-rw-r--r--drivers/video/backlight/as3711_bl.c480
-rw-r--r--drivers/video/backlight/atmel-pwm-bl.c143
-rw-r--r--drivers/video/backlight/backlight.c396
-rw-r--r--drivers/video/backlight/bd6107.c213
-rw-r--r--drivers/video/backlight/corgi_bl.c169
-rw-r--r--drivers/video/backlight/corgi_lcd.c612
-rw-r--r--drivers/video/backlight/cr_bllcd.c52
-rw-r--r--drivers/video/backlight/da903x_bl.c176
-rw-r--r--drivers/video/backlight/da9052_bl.c185
-rw-r--r--drivers/video/backlight/ep93xx_bl.c142
-rw-r--r--drivers/video/backlight/generic_bl.c137
-rw-r--r--drivers/video/backlight/gpio_backlight.c171
-rw-r--r--drivers/video/backlight/hp680_bl.c61
-rw-r--r--drivers/video/backlight/hx8357.c682
-rw-r--r--drivers/video/backlight/ili922x.c550
-rw-r--r--drivers/video/backlight/ili9320.c75
-rw-r--r--drivers/video/backlight/ili9320.h4
-rw-r--r--drivers/video/backlight/jornada720_bl.c154
-rw-r--r--drivers/video/backlight/jornada720_lcd.c134
-rw-r--r--drivers/video/backlight/kb3886_bl.c197
-rw-r--r--drivers/video/backlight/l4f00242t03.c269
-rw-r--r--drivers/video/backlight/lcd.c176
-rw-r--r--drivers/video/backlight/ld9040.c818
-rw-r--r--drivers/video/backlight/ld9040_gamma.h202
-rw-r--r--drivers/video/backlight/lm3533_bl.c412
-rw-r--r--drivers/video/backlight/lm3630a_bl.c483
-rw-r--r--drivers/video/backlight/lm3639_bl.c434
-rw-r--r--drivers/video/backlight/lms283gf05.c204
-rw-r--r--drivers/video/backlight/lms501kf03.c437
-rw-r--r--drivers/video/backlight/locomolcd.c80
-rw-r--r--drivers/video/backlight/lp855x_bl.c505
-rw-r--r--drivers/video/backlight/lp8788_bl.c332
-rw-r--r--drivers/video/backlight/ltv350qv.c80
-rw-r--r--drivers/video/backlight/lv5207lp.c170
-rw-r--r--drivers/video/backlight/max8925_bl.c210
-rw-r--r--drivers/video/backlight/mbp_nvidia_bl.c116
-rw-r--r--drivers/video/backlight/omap1_bl.c85
-rw-r--r--drivers/video/backlight/ot200_bl.c166
-rw-r--r--drivers/video/backlight/pandora_bl.c161
-rw-r--r--drivers/video/backlight/pcf50633-backlight.c163
-rw-r--r--drivers/video/backlight/platform_lcd.c84
-rw-r--r--drivers/video/backlight/progear_bl.c152
-rw-r--r--drivers/video/backlight/pwm_bl.c367
-rw-r--r--drivers/video/backlight/s6e63m0.c864
-rw-r--r--drivers/video/backlight/s6e63m0_gamma.h266
-rw-r--r--drivers/video/backlight/tdo24m.c451
-rw-r--r--drivers/video/backlight/tosa_bl.c178
-rw-r--r--drivers/video/backlight/tosa_lcd.c278
-rw-r--r--drivers/video/backlight/tps65217_bl.c341
-rw-r--r--drivers/video/backlight/vgg2432a4.c57
-rw-r--r--drivers/video/backlight/wm831x_bl.c230
60 files changed, 16413 insertions, 1065 deletions
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
new file mode 100644
index 00000000000..7db5234462d
--- /dev/null
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -0,0 +1,278 @@
+/*
+ * Backlight driver for Marvell Semiconductor 88PM8606
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/backlight.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/module.h>
+
+#define MAX_BRIGHTNESS (0xFF)
+#define MIN_BRIGHTNESS (0)
+
+#define CURRENT_BITMASK (0x1F << 1)
+
+struct pm860x_backlight_data {
+ struct pm860x_chip *chip;
+ struct i2c_client *i2c;
+ int current_brightness;
+ int port;
+ int pwm;
+ int iset;
+ int reg_duty_cycle;
+ int reg_always_on;
+ int reg_current;
+};
+
+static int backlight_power_set(struct pm860x_chip *chip, int port,
+ int on)
+{
+ int ret = -EINVAL;
+
+ switch (port) {
+ case 0:
+ ret = on ? pm8606_osc_enable(chip, WLED1_DUTY) :
+ pm8606_osc_disable(chip, WLED1_DUTY);
+ break;
+ case 1:
+ ret = on ? pm8606_osc_enable(chip, WLED2_DUTY) :
+ pm8606_osc_disable(chip, WLED2_DUTY);
+ break;
+ case 2:
+ ret = on ? pm8606_osc_enable(chip, WLED3_DUTY) :
+ pm8606_osc_disable(chip, WLED3_DUTY);
+ break;
+ }
+ return ret;
+}
+
+static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
+{
+ struct pm860x_backlight_data *data = bl_get_data(bl);
+ struct pm860x_chip *chip = data->chip;
+ unsigned char value;
+ int ret;
+
+ if (brightness > MAX_BRIGHTNESS)
+ value = MAX_BRIGHTNESS;
+ else
+ value = brightness;
+
+ if (brightness)
+ backlight_power_set(chip, data->port, 1);
+
+ ret = pm860x_reg_write(data->i2c, data->reg_duty_cycle, value);
+ if (ret < 0)
+ goto out;
+
+ if ((data->current_brightness == 0) && brightness) {
+ if (data->iset) {
+ ret = pm860x_set_bits(data->i2c, data->reg_current,
+ CURRENT_BITMASK, data->iset);
+ if (ret < 0)
+ goto out;
+ }
+ if (data->pwm) {
+ ret = pm860x_set_bits(data->i2c, PM8606_PWM,
+ PM8606_PWM_FREQ_MASK, data->pwm);
+ if (ret < 0)
+ goto out;
+ }
+ if (brightness == MAX_BRIGHTNESS) {
+ /* set WLED_ON bit as 100% */
+ ret = pm860x_set_bits(data->i2c, data->reg_always_on,
+ PM8606_WLED_ON, PM8606_WLED_ON);
+ }
+ } else {
+ if (brightness == MAX_BRIGHTNESS) {
+ /* set WLED_ON bit as 100% */
+ ret = pm860x_set_bits(data->i2c, data->reg_always_on,
+ PM8606_WLED_ON, PM8606_WLED_ON);
+ } else {
+ /* clear WLED_ON bit since it's not 100% */
+ ret = pm860x_set_bits(data->i2c, data->reg_always_on,
+ PM8606_WLED_ON, 0);
+ }
+ }
+ if (ret < 0)
+ goto out;
+
+ if (brightness == 0)
+ backlight_power_set(chip, data->port, 0);
+
+ dev_dbg(chip->dev, "set brightness %d\n", value);
+ data->current_brightness = value;
+ return 0;
+out:
+ dev_dbg(chip->dev, "set brightness %d failure with return value: %d\n",
+ value, ret);
+ return ret;
+}
+
+static int pm860x_backlight_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;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ return pm860x_backlight_set(bl, brightness);
+}
+
+static int pm860x_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct pm860x_backlight_data *data = bl_get_data(bl);
+ struct pm860x_chip *chip = data->chip;
+ int ret;
+
+ ret = pm860x_reg_read(data->i2c, data->reg_duty_cycle);
+ if (ret < 0)
+ goto out;
+ data->current_brightness = ret;
+ dev_dbg(chip->dev, "get brightness %d\n", data->current_brightness);
+ return data->current_brightness;
+out:
+ return -EINVAL;
+}
+
+static const struct backlight_ops pm860x_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = pm860x_backlight_update_status,
+ .get_brightness = pm860x_backlight_get_brightness,
+};
+
+#ifdef CONFIG_OF
+static int pm860x_backlight_dt_init(struct platform_device *pdev,
+ struct pm860x_backlight_data *data,
+ char *name)
+{
+ struct device_node *nproot, *np;
+ int iset = 0;
+
+ nproot = of_node_get(pdev->dev.parent->of_node);
+ if (!nproot)
+ return -ENODEV;
+ nproot = of_find_node_by_name(nproot, "backlights");
+ if (!nproot) {
+ dev_err(&pdev->dev, "failed to find backlights node\n");
+ return -ENODEV;
+ }
+ for_each_child_of_node(nproot, np) {
+ if (!of_node_cmp(np->name, name)) {
+ of_property_read_u32(np, "marvell,88pm860x-iset",
+ &iset);
+ data->iset = PM8606_WLED_CURRENT(iset);
+ of_property_read_u32(np, "marvell,88pm860x-pwm",
+ &data->pwm);
+ break;
+ }
+ }
+ of_node_put(nproot);
+ return 0;
+}
+#else
+#define pm860x_backlight_dt_init(x, y, z) (-1)
+#endif
+
+static int pm860x_backlight_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_backlight_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct pm860x_backlight_data *data;
+ struct backlight_device *bl;
+ struct resource *res;
+ struct backlight_properties props;
+ char name[MFD_NAME_SIZE];
+ int ret = 0;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_backlight_data),
+ GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ res = platform_get_resource_byname(pdev, IORESOURCE_REG, "duty cycle");
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for duty cycle\n");
+ return -ENXIO;
+ }
+ data->reg_duty_cycle = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_REG, "always on");
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resorce for always on\n");
+ return -ENXIO;
+ }
+ data->reg_always_on = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_REG, "current");
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for current\n");
+ return -ENXIO;
+ }
+ data->reg_current = res->start;
+
+ memset(name, 0, MFD_NAME_SIZE);
+ sprintf(name, "backlight-%d", pdev->id);
+ data->port = pdev->id;
+ data->chip = chip;
+ data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
+ data->current_brightness = MAX_BRIGHTNESS;
+ if (pm860x_backlight_dt_init(pdev, data, name)) {
+ if (pdata) {
+ data->pwm = pdata->pwm;
+ data->iset = pdata->iset;
+ }
+ }
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = MAX_BRIGHTNESS;
+ bl = devm_backlight_device_register(&pdev->dev, name, &pdev->dev, data,
+ &pm860x_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+ bl->props.brightness = MAX_BRIGHTNESS;
+
+ platform_set_drvdata(pdev, bl);
+
+ /* read current backlight */
+ ret = pm860x_backlight_get_brightness(bl);
+ if (ret < 0)
+ return ret;
+
+ backlight_update_status(bl);
+ return 0;
+}
+
+static struct platform_driver pm860x_backlight_driver = {
+ .driver = {
+ .name = "88pm860x-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = pm860x_backlight_probe,
+};
+
+module_platform_driver(pm860x_backlight_driver);
+
+MODULE_DESCRIPTION("Backlight Driver for Marvell Semiconductor 88PM8606");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:88pm860x-backlight");
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 452b770d8cc..5d449059a55 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,10 +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 SPI_MASTER && PXA_SHARPSL && BACKLIGHT_CLASS_DEVICE
+ 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 SPI_MASTER && GPIOLIB
+ 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 SPI_MASTER && GPIOLIB
+ 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
@@ -36,36 +59,102 @@ config LCD_LTV350QV
The LTV350QV panel is present on all ATSTK1000 boards.
+config LCD_ILI922X
+ tristate "ILI Technology ILI9221/ILI9222 support"
+ depends on SPI
+ help
+ If you have a panel based on the ILI9221/9222 controller
+ chip then say y to include a driver for it.
+
config LCD_ILI9320
- tristate
- depends on LCD_CLASS_DEVICE && BACKLIGHT_LCD_SUPPORT
- default n
+ tristate "ILI Technology ILI9320 controller support"
+ depends on SPI
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 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 I2C && 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 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.
+
+config LCD_LD9040
+ tristate "LD9040 AMOLED LCD Driver"
+ depends on SPI && BACKLIGHT_CLASS_DEVICE
+ default n
+ help
+ If you have an LD9040 Panel, say Y to enable its
+ control driver.
+
+config LCD_AMS369FG06
+ tristate "AMS369FG06 AMOLED LCD Driver"
+ depends on SPI && BACKLIGHT_CLASS_DEVICE
+ default n
+ help
+ If you have an AMS369FG06 AMOLED Panel, say Y to enable its
+ LCD control driver.
+
+config LCD_LMS501KF03
+ tristate "LMS501KF03 LCD Driver"
+ depends on SPI
+ default n
+ help
+ If you have an LMS501KF03 LCD Panel, say Y to enable its
+ LCD control driver.
+
+config LCD_HX8357
+ tristate "Himax HX-8357 LCD Driver"
+ depends on SPI
+ help
+ If you have a HX-8357 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
@@ -74,10 +163,12 @@ 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
- default y if MACH_SAM9261EK || MACH_SAM9263EK
+ depends on FB_ATMEL
+ default y if MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK
help
This provides a backlight control internal to the Atmel LCDC
driver. If the LCD "contrast control" on your board is wired
@@ -89,8 +180,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
@@ -99,18 +189,39 @@ config BACKLIGHT_ATMEL_PWM
To compile this driver as a module, choose M here: the module will be
called atmel-pwm-bl.
-config BACKLIGHT_CORGI
+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 n
+ default y
help
Say y to enable the generic platform backlight driver previously
known as the Corgi backlight driver. If you have a Sharp Zaurus
- SL-C7xx, SL-Cxx00 or SL-6000x say y. Most users can say n.
+ SL-C7xx, SL-Cxx00 or SL-6000x say y.
+
+config BACKLIGHT_LM3533
+ tristate "Backlight Driver for LM3533"
+ depends on BACKLIGHT_CLASS_DEVICE
+ depends on MFD_LM3533
+ help
+ Say Y to enable the backlight driver for National Semiconductor / TI
+ LM3533 Lighting Power chips.
+
+ The backlights can be controlled directly, through PWM input, or by
+ the ambient-light-sensor interface. The chip supports 256 brightness
+ levels.
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
@@ -118,7 +229,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
@@ -127,40 +238,212 @@ 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
backlight driver.
-config BACKLIGHT_PROGEAR
- tristate "Frontpath ProGear Backlight Driver"
- depends on BACKLIGHT_CLASS_DEVICE && PCI && X86
- default n
+config BACKLIGHT_HP700
+ tristate "HP Jornada 700 series Backlight Driver"
+ depends on SA1100_JORNADA720_SSP && !PREEMPT
+ default y
help
- If you have a Frontpath ProGear say Y to enable the
- backlight driver.
+ If you have an HP Jornada 700 series,
+ say Y to include backlight control 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 PWM
help
If you have a LCD backlight adjustable by PWM, say Y to enable
this driver.
-config BACKLIGHT_MBP_NVIDIA
- tristate "MacBook Pro Nvidia Backlight Driver"
- depends on BACKLIGHT_CLASS_DEVICE && X86
- default n
+config BACKLIGHT_DA903X
+ tristate "Backlight Driver for DA9030/DA9034 using WLED"
+ 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_DA9052
+ tristate "Dialog DA9052/DA9053 WLED"
+ depends on PMIC_DA9052
+ help
+ Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs.
+
+config BACKLIGHT_MAX8925
+ tristate "Backlight driver for 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_APPLE
+ tristate "Apple Backlight Driver"
+ depends on X86 && ACPI
help
- If you have an Apple Macbook Pro with Nvidia graphics hardware say Y
- to enable a driver for its backlight
+ If you have an Intel-based Apple say Y to enable a driver for its
+ backlight.
+
+config BACKLIGHT_TOSA
+ tristate "Sharp SL-6000 Backlight Driver"
+ 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 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 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_ADP5520
+ tristate "Backlight Driver for ADP5520/ADP5501 using WLED"
+ 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.
+
+ 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_ADP8870
+ tristate "Backlight Driver for ADP8870 using WLED"
+ depends on BACKLIGHT_CLASS_DEVICE && I2C
+ select NEW_LEDS
+ select LEDS_CLASS
+ help
+ If you have a LCD backlight connected to the ADP8870,
+ say Y here to enable this driver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called adp8870_bl.
+
+config BACKLIGHT_88PM860X
+ tristate "Backlight Driver for 88PM8606 using WLED"
+ 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.
+
+config BACKLIGHT_AAT2870
+ tristate "AnalogicTech AAT2870 Backlight"
+ depends on BACKLIGHT_CLASS_DEVICE && MFD_AAT2870_CORE
+ help
+ If you have a AnalogicTech AAT2870 say Y to enable the
+ backlight driver.
+
+config BACKLIGHT_LM3630A
+ tristate "Backlight Driver for LM3630A"
+ depends on BACKLIGHT_CLASS_DEVICE && I2C && PWM
+ select REGMAP_I2C
+ help
+ This supports TI LM3630A Backlight Driver
+
+config BACKLIGHT_LM3639
+ tristate "Backlight Driver for LM3639"
+ depends on BACKLIGHT_CLASS_DEVICE && I2C
+ select REGMAP_I2C
+ select NEW_LEDS
+ select LEDS_CLASS
+ help
+ This supports TI LM3639 Backlight + 1.5A Flash LED Driver
+
+config BACKLIGHT_LP855X
+ tristate "Backlight driver for TI LP855X"
+ depends on BACKLIGHT_CLASS_DEVICE && I2C && PWM
+ help
+ This supports TI LP8550, LP8551, LP8552, LP8553, LP8555, LP8556 and
+ LP8557 backlight driver.
+
+config BACKLIGHT_LP8788
+ tristate "Backlight driver for TI LP8788 MFD"
+ depends on BACKLIGHT_CLASS_DEVICE && MFD_LP8788 && PWM
+ help
+ This supports TI LP8788 backlight driver.
+
+config BACKLIGHT_OT200
+ tristate "Backlight driver for ot200 visualisation device"
+ depends on BACKLIGHT_CLASS_DEVICE && CS5535_MFGPT && GPIO_CS5535
+ help
+ To compile this driver as a module, choose M here: the module will be
+ called ot200_bl.
+
+config BACKLIGHT_PANDORA
+ tristate "Backlight driver for Pandora console"
+ depends on TWL4030_CORE
+ help
+ If you have a Pandora console, say Y to enable the
+ backlight driver.
+
+config BACKLIGHT_TPS65217
+ tristate "TPS65217 Backlight"
+ depends on BACKLIGHT_CLASS_DEVICE && MFD_TPS65217
+ help
+ If you have a Texas Instruments TPS65217 say Y to enable the
+ backlight driver.
+
+config BACKLIGHT_AS3711
+ tristate "AS3711 Backlight"
+ depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711
+ help
+ If you have an Austrian Microsystems AS3711 say Y to enable the
+ backlight driver.
+
+config BACKLIGHT_GPIO
+ tristate "Generic GPIO based Backlight Driver"
+ depends on GPIOLIB
+ help
+ If you have a LCD backlight adjustable by GPIO, say Y to enable
+ this driver.
+
+config BACKLIGHT_LV5207LP
+ tristate "Sanyo LV5207LP Backlight"
+ depends on I2C
+ help
+ If you have a Sanyo LV5207LP say Y to enable the backlight driver.
+
+config BACKLIGHT_BD6107
+ tristate "Rohm BD6107 Backlight"
+ depends on I2C
+ help
+ If you have a Rohm BD6107 say Y to enable the backlight driver.
+
+endif # BACKLIGHT_CLASS_DEVICE
+endif # BACKLIGHT_LCD_SUPPORT
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index b405aace803..bb820024f34 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -1,19 +1,55 @@
# Backlight & LCD drivers
-obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
-obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
-obj-$(CONFIG_LCD_ILI9320) += ili9320.o
-obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
-obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o
-
-obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
-obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
-obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o
-obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
-obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
-obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
-obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
-obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
-obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
-obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o
+obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o
+obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
+obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o
+obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o
+obj-$(CONFIG_LCD_HX8357) += hx8357.o
+obj-$(CONFIG_LCD_ILI922X) += ili922x.o
+obj-$(CONFIG_LCD_ILI9320) += ili9320.o
+obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o
+obj-$(CONFIG_LCD_LD9040) += ld9040.o
+obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o
+obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o
+obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
+obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
+obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
+obj-$(CONFIG_LCD_TDO24M) += tdo24m.o
+obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
+obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o
+obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
+obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o
+obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
+obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
+obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
+obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
+obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
+obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
+obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o
+obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
+obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
+obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o
+obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o
+obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o
+obj-$(CONFIG_BACKLIGHT_GPIO) += gpio_backlight.o
+obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
+obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o
+obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o
+obj-$(CONFIG_BACKLIGHT_LM3630A) += lm3630a_bl.o
+obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o
+obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
+obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
+obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o
+obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o
+obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
+obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
+obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o
+obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o
+obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
+obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
+obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o
+obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o
+obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o
+obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o
diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c
new file mode 100644
index 00000000000..ec5350f2c28
--- /dev/null
+++ b/drivers/video/backlight/aat2870_bl.c
@@ -0,0 +1,240 @@
+/*
+ * linux/drivers/video/backlight/aat2870_bl.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.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 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/mfd/aat2870.h>
+
+struct aat2870_bl_driver_data {
+ struct platform_device *pdev;
+ struct backlight_device *bd;
+
+ int channels;
+ int max_current;
+ int brightness; /* current brightness */
+};
+
+static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl,
+ int brightness)
+{
+ struct backlight_device *bd = aat2870_bl->bd;
+ int val;
+
+ val = brightness * (aat2870_bl->max_current - 1);
+ val /= bd->props.max_brightness;
+
+ return val;
+}
+
+static inline int aat2870_bl_enable(struct aat2870_bl_driver_data *aat2870_bl)
+{
+ struct aat2870_data *aat2870
+ = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+
+ return aat2870->write(aat2870, AAT2870_BL_CH_EN,
+ (u8)aat2870_bl->channels);
+}
+
+static inline int aat2870_bl_disable(struct aat2870_bl_driver_data *aat2870_bl)
+{
+ struct aat2870_data *aat2870
+ = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+
+ return aat2870->write(aat2870, AAT2870_BL_CH_EN, 0x0);
+}
+
+static int aat2870_bl_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int aat2870_bl_update_status(struct backlight_device *bd)
+{
+ struct aat2870_bl_driver_data *aat2870_bl = bl_get_data(bd);
+ struct aat2870_data *aat2870 =
+ dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+ int brightness = bd->props.brightness;
+ int ret;
+
+ if ((brightness < 0) || (bd->props.max_brightness < brightness)) {
+ dev_err(&bd->dev, "invalid brightness, %d\n", brightness);
+ return -EINVAL;
+ }
+
+ dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n",
+ bd->props.brightness, bd->props.power, bd->props.state);
+
+ if ((bd->props.power != FB_BLANK_UNBLANK) ||
+ (bd->props.state & BL_CORE_FBBLANK) ||
+ (bd->props.state & BL_CORE_SUSPENDED))
+ brightness = 0;
+
+ ret = aat2870->write(aat2870, AAT2870_BLM,
+ (u8)aat2870_brightness(aat2870_bl, brightness));
+ if (ret < 0)
+ return ret;
+
+ if (brightness == 0) {
+ ret = aat2870_bl_disable(aat2870_bl);
+ if (ret < 0)
+ return ret;
+ } else if (aat2870_bl->brightness == 0) {
+ ret = aat2870_bl_enable(aat2870_bl);
+ if (ret < 0)
+ return ret;
+ }
+
+ aat2870_bl->brightness = brightness;
+
+ return 0;
+}
+
+static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi)
+{
+ return 1;
+}
+
+static const struct backlight_ops aat2870_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = aat2870_bl_get_brightness,
+ .update_status = aat2870_bl_update_status,
+ .check_fb = aat2870_bl_check_fb,
+};
+
+static int aat2870_bl_probe(struct platform_device *pdev)
+{
+ struct aat2870_bl_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct aat2870_bl_driver_data *aat2870_bl;
+ struct backlight_device *bd;
+ struct backlight_properties props;
+ int ret = 0;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (pdev->id != AAT2870_ID_BL) {
+ dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ aat2870_bl = devm_kzalloc(&pdev->dev,
+ sizeof(struct aat2870_bl_driver_data),
+ GFP_KERNEL);
+ if (!aat2870_bl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+
+ props.type = BACKLIGHT_RAW;
+ bd = devm_backlight_device_register(&pdev->dev, "aat2870-backlight",
+ &pdev->dev, aat2870_bl, &aat2870_bl_ops,
+ &props);
+ if (IS_ERR(bd)) {
+ dev_err(&pdev->dev,
+ "Failed allocate memory for backlight device\n");
+ ret = PTR_ERR(bd);
+ goto out;
+ }
+
+ aat2870_bl->pdev = pdev;
+ platform_set_drvdata(pdev, aat2870_bl);
+
+ aat2870_bl->bd = bd;
+
+ if (pdata->channels > 0)
+ aat2870_bl->channels = pdata->channels;
+ else
+ aat2870_bl->channels = AAT2870_BL_CH_ALL;
+
+ if (pdata->max_current > 0)
+ aat2870_bl->max_current = pdata->max_current;
+ else
+ aat2870_bl->max_current = AAT2870_CURRENT_27_9;
+
+ if (pdata->max_brightness > 0)
+ bd->props.max_brightness = pdata->max_brightness;
+ else
+ bd->props.max_brightness = 255;
+
+ aat2870_bl->brightness = 0;
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.brightness = bd->props.max_brightness;
+
+ ret = aat2870_bl_update_status(bd);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize\n");
+ return ret;
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static int aat2870_bl_remove(struct platform_device *pdev)
+{
+ struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev);
+ struct backlight_device *bd = aat2870_bl->bd;
+
+ bd->props.power = FB_BLANK_POWERDOWN;
+ bd->props.brightness = 0;
+ backlight_update_status(bd);
+
+ return 0;
+}
+
+static struct platform_driver aat2870_bl_driver = {
+ .driver = {
+ .name = "aat2870-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = aat2870_bl_probe,
+ .remove = aat2870_bl_remove,
+};
+
+static int __init aat2870_bl_init(void)
+{
+ return platform_driver_register(&aat2870_bl_driver);
+}
+subsys_initcall(aat2870_bl_init);
+
+static void __exit aat2870_bl_exit(void)
+{
+ platform_driver_unregister(&aat2870_bl_driver);
+}
+module_exit(aat2870_bl_exit);
+
+MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c
new file mode 100644
index 00000000000..f37097a261a
--- /dev/null
+++ b/drivers/video/backlight/adp5520_bl.c
@@ -0,0 +1,389 @@
+/*
+ * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/mfd/adp5520.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+struct adp5520_bl {
+ struct device *master;
+ struct adp5520_backlight_platform_data *pdata;
+ struct mutex lock;
+ unsigned long cached_daylight_max;
+ int id;
+ int current_brightness;
+};
+
+static int adp5520_bl_set(struct backlight_device *bl, int brightness)
+{
+ struct adp5520_bl *data = bl_get_data(bl);
+ struct device *master = data->master;
+ int ret = 0;
+
+ if (data->pdata->en_ambl_sens) {
+ if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) {
+ /* Disable Ambient Light auto adjust */
+ ret |= adp5520_clr_bits(master, ADP5520_BL_CONTROL,
+ ADP5520_BL_AUTO_ADJ);
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
+ brightness);
+ } else {
+ /*
+ * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
+ * restore daylight l3 sysfs brightness
+ */
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
+ data->cached_daylight_max);
+ ret |= adp5520_set_bits(master, ADP5520_BL_CONTROL,
+ ADP5520_BL_AUTO_ADJ);
+ }
+ } else {
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, brightness);
+ }
+
+ if (data->current_brightness && brightness == 0)
+ ret |= adp5520_set_bits(master,
+ ADP5520_MODE_STATUS, ADP5520_DIM_EN);
+ else if (data->current_brightness == 0 && brightness)
+ ret |= adp5520_clr_bits(master,
+ ADP5520_MODE_STATUS, ADP5520_DIM_EN);
+
+ if (!ret)
+ data->current_brightness = brightness;
+
+ return ret;
+}
+
+static int adp5520_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 adp5520_bl_set(bl, brightness);
+}
+
+static int adp5520_bl_get_brightness(struct backlight_device *bl)
+{
+ struct adp5520_bl *data = bl_get_data(bl);
+ int error;
+ uint8_t reg_val;
+
+ error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val);
+
+ return error ? data->current_brightness : reg_val;
+}
+
+static const struct backlight_ops adp5520_bl_ops = {
+ .update_status = adp5520_bl_update_status,
+ .get_brightness = adp5520_bl_get_brightness,
+};
+
+static int adp5520_bl_setup(struct backlight_device *bl)
+{
+ struct adp5520_bl *data = bl_get_data(bl);
+ struct device *master = data->master;
+ struct adp5520_backlight_platform_data *pdata = data->pdata;
+ int ret = 0;
+
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
+ pdata->l1_daylight_max);
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM,
+ pdata->l1_daylight_dim);
+
+ if (pdata->en_ambl_sens) {
+ data->cached_daylight_max = pdata->l1_daylight_max;
+ ret |= adp5520_write(master, ADP5520_OFFICE_MAX,
+ pdata->l2_office_max);
+ ret |= adp5520_write(master, ADP5520_OFFICE_DIM,
+ pdata->l2_office_dim);
+ ret |= adp5520_write(master, ADP5520_DARK_MAX,
+ pdata->l3_dark_max);
+ ret |= adp5520_write(master, ADP5520_DARK_DIM,
+ pdata->l3_dark_dim);
+ ret |= adp5520_write(master, ADP5520_L2_TRIP,
+ pdata->l2_trip);
+ ret |= adp5520_write(master, ADP5520_L2_HYS,
+ pdata->l2_hyst);
+ ret |= adp5520_write(master, ADP5520_L3_TRIP,
+ pdata->l3_trip);
+ ret |= adp5520_write(master, ADP5520_L3_HYS,
+ pdata->l3_hyst);
+ ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG,
+ ALS_CMPR_CFG_VAL(pdata->abml_filt,
+ ADP5520_L3_EN));
+ }
+
+ ret |= adp5520_write(master, ADP5520_BL_CONTROL,
+ BL_CTRL_VAL(pdata->fade_led_law,
+ pdata->en_ambl_sens));
+
+ ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in,
+ pdata->fade_out));
+
+ ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS,
+ ADP5520_BL_EN | ADP5520_DIM_EN);
+
+ return ret;
+}
+
+static ssize_t adp5520_show(struct device *dev, char *buf, int reg)
+{
+ struct adp5520_bl *data = dev_get_drvdata(dev);
+ int ret;
+ uint8_t reg_val;
+
+ mutex_lock(&data->lock);
+ ret = adp5520_read(data->master, reg, &reg_val);
+ mutex_unlock(&data->lock);
+
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", reg_val);
+}
+
+static ssize_t adp5520_store(struct device *dev, const char *buf,
+ size_t count, int reg)
+{
+ struct adp5520_bl *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->lock);
+ adp5520_write(data->master, reg, val);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t adp5520_bl_dark_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp5520_show(dev, buf, ADP5520_DARK_MAX);
+}
+
+static ssize_t adp5520_bl_dark_max_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp5520_store(dev, buf, count, ADP5520_DARK_MAX);
+}
+static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show,
+ adp5520_bl_dark_max_store);
+
+static ssize_t adp5520_bl_office_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp5520_show(dev, buf, ADP5520_OFFICE_MAX);
+}
+
+static ssize_t adp5520_bl_office_max_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX);
+}
+static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
+ adp5520_bl_office_max_store);
+
+static ssize_t adp5520_bl_daylight_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX);
+}
+
+static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adp5520_bl *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtoul(buf, 10, &data->cached_daylight_max);
+ if (ret < 0)
+ return ret;
+
+ return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX);
+}
+static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
+ adp5520_bl_daylight_max_store);
+
+static ssize_t adp5520_bl_dark_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp5520_show(dev, buf, ADP5520_DARK_DIM);
+}
+
+static ssize_t adp5520_bl_dark_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp5520_store(dev, buf, count, ADP5520_DARK_DIM);
+}
+static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
+ adp5520_bl_dark_dim_store);
+
+static ssize_t adp5520_bl_office_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp5520_show(dev, buf, ADP5520_OFFICE_DIM);
+}
+
+static ssize_t adp5520_bl_office_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp5520_store(dev, buf, count, ADP5520_OFFICE_DIM);
+}
+static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show,
+ adp5520_bl_office_dim_store);
+
+static ssize_t adp5520_bl_daylight_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp5520_show(dev, buf, ADP5520_DAYLIGHT_DIM);
+}
+
+static ssize_t adp5520_bl_daylight_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_DIM);
+}
+static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show,
+ adp5520_bl_daylight_dim_store);
+
+static struct attribute *adp5520_bl_attributes[] = {
+ &dev_attr_dark_max.attr,
+ &dev_attr_dark_dim.attr,
+ &dev_attr_office_max.attr,
+ &dev_attr_office_dim.attr,
+ &dev_attr_daylight_max.attr,
+ &dev_attr_daylight_dim.attr,
+ NULL
+};
+
+static const struct attribute_group adp5520_bl_attr_group = {
+ .attrs = adp5520_bl_attributes,
+};
+
+static int adp5520_bl_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ struct adp5520_bl *data;
+ int ret = 0;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->master = pdev->dev.parent;
+ data->pdata = dev_get_platdata(&pdev->dev);
+
+ if (data->pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ data->id = pdev->id;
+ data->current_brightness = 0;
+
+ mutex_init(&data->lock);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = ADP5020_MAX_BRIGHTNESS;
+ bl = devm_backlight_device_register(&pdev->dev, pdev->name,
+ data->master, data, &adp5520_bl_ops,
+ &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ bl->props.brightness = ADP5020_MAX_BRIGHTNESS;
+ if (data->pdata->en_ambl_sens)
+ ret = sysfs_create_group(&bl->dev.kobj,
+ &adp5520_bl_attr_group);
+
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register sysfs\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, bl);
+ ret |= adp5520_bl_setup(bl);
+ backlight_update_status(bl);
+
+ return ret;
+}
+
+static int adp5520_bl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct adp5520_bl *data = bl_get_data(bl);
+
+ adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN);
+
+ if (data->pdata->en_ambl_sens)
+ sysfs_remove_group(&bl->dev.kobj,
+ &adp5520_bl_attr_group);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adp5520_bl_suspend(struct device *dev)
+{
+ struct backlight_device *bl = dev_get_drvdata(dev);
+
+ return adp5520_bl_set(bl, 0);
+}
+
+static int adp5520_bl_resume(struct device *dev)
+{
+ struct backlight_device *bl = dev_get_drvdata(dev);
+
+ backlight_update_status(bl);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adp5520_bl_pm_ops, adp5520_bl_suspend,
+ adp5520_bl_resume);
+
+static struct platform_driver adp5520_bl_driver = {
+ .driver = {
+ .name = "adp5520-backlight",
+ .owner = THIS_MODULE,
+ .pm = &adp5520_bl_pm_ops,
+ },
+ .probe = adp5520_bl_probe,
+ .remove = adp5520_bl_remove,
+};
+
+module_platform_driver(adp5520_bl_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5520(01) Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-backlight");
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
new file mode 100644
index 00000000000..be8d83deca7
--- /dev/null
+++ b/drivers/video/backlight/adp8860_bl.c
@@ -0,0 +1,819 @@
+/*
+ * 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/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, &reg_val);
+
+ if (!ret && ((reg_val & bit_mask) != bit_mask)) {
+ 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, &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 adp8860_led_probe(struct i2c_client *client)
+{
+ struct adp8860_backlight_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct adp8860_bl *data = i2c_get_clientdata(client);
+ struct adp8860_led *led, *led_dat;
+ struct led_info *cur_led;
+ int ret, i;
+
+ led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds,
+ GFP_KERNEL);
+ if (led == NULL)
+ 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");
+ return ret;
+ }
+
+ 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);
+ ret = -EINVAL;
+ 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);
+ ret = -EBUSY;
+ 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);
+ }
+
+ return ret;
+}
+
+static int adp8860_led_remove(struct i2c_client *client)
+{
+ struct adp8860_backlight_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ 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);
+ }
+
+ return 0;
+}
+#else
+static int adp8860_led_probe(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int 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, &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 = kstrtoul(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);
+ int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
+ if (ret)
+ return ret;
+
+ 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, &reg_val);
+ ret_val = reg_val;
+ error |= adp8860_read(data->client, ADP8860_PH1LEVH, &reg_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, &reg_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 = kstrtoul(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 <= 3)) {
+ /* 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, &reg_val);
+ reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
+ reg_val |= (val - 1) << 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 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 =
+ dev_get_platdata(&client->dev);
+ 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 = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ ret = adp8860_read(client, ADP8860_MFDVID, &reg_val);
+ if (ret < 0)
+ return ret;
+
+ 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");
+ return -ENODEV;
+ }
+
+ /* 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.type = BACKLIGHT_RAW;
+ props.max_brightness = ADP8860_MAX_BRIGHTNESS;
+
+ mutex_init(&data->lock);
+
+ bl = devm_backlight_device_register(&client->dev,
+ 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");
+ return PTR_ERR(bl);
+ }
+
+ 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");
+ return ret;
+ }
+
+ 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);
+
+ return ret;
+}
+
+static int 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);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adp8860_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
+
+ return 0;
+}
+
+static int adp8860_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ adp8860_set_bits(client, ADP8860_MDCR, NSTBY | BLEN);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adp8860_i2c_pm_ops, adp8860_i2c_suspend,
+ adp8860_i2c_resume);
+
+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,
+ .pm = &adp8860_i2c_pm_ops,
+ },
+ .probe = adp8860_probe,
+ .remove = adp8860_remove,
+ .id_table = adp8860_id,
+};
+
+module_i2c_driver(adp8860_driver);
+
+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/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c
new file mode 100644
index 00000000000..251af4d38d8
--- /dev/null
+++ b/drivers/video/backlight/adp8870_bl.c
@@ -0,0 +1,991 @@
+/*
+ * Backlight driver for Analog Devices ADP8870 Backlight Devices
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.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/workqueue.h>
+#include <linux/slab.h>
+
+#include <linux/i2c/adp8870.h>
+#define ADP8870_EXT_FEATURES
+#define ADP8870_USE_LEDS
+
+
+#define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */
+#define ADP8870_MDCR 0x01 /* Device mode and status */
+#define ADP8870_INT_STAT 0x02 /* Interrupts status */
+#define ADP8870_INT_EN 0x03 /* Interrupts enable */
+#define ADP8870_CFGR 0x04 /* Configuration register */
+#define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */
+#define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */
+#define ADP8870_BLOFF 0x07 /* Backlight off timeout */
+#define ADP8870_BLDIM 0x08 /* Backlight dim timeout */
+#define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */
+#define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */
+#define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */
+#define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */
+#define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */
+#define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */
+#define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */
+#define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */
+#define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */
+#define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */
+#define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */
+#define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */
+#define ADP8870_ISCC 0x1B /* Independent sink current control register */
+#define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */
+#define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */
+#define ADP8870_ISCF 0x1E /* Independent sink current fade register */
+#define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */
+#define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */
+#define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */
+#define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */
+#define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */
+#define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */
+#define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */
+#define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */
+#define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */
+#define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */
+#define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */
+#define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */
+#define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */
+#define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */
+#define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */
+#define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */
+#define ADP8870_L2TRP 0x32 /* L2 comparator reference */
+#define ADP8870_L2HYS 0x33 /* L2 hysteresis */
+#define ADP8870_L3TRP 0x34 /* L3 comparator reference */
+#define ADP8870_L3HYS 0x35 /* L3 hysteresis */
+#define ADP8870_L4TRP 0x36 /* L4 comparator reference */
+#define ADP8870_L4HYS 0x37 /* L4 hysteresis */
+#define ADP8870_L5TRP 0x38 /* L5 comparator reference */
+#define ADP8870_L5HYS 0x39 /* L5 hysteresis */
+#define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */
+#define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */
+#define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */
+#define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */
+
+#define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */
+#define ADP8870_DEVID(x) ((x) & 0xF)
+#define ADP8870_MANID(x) ((x) >> 4)
+
+/* MDCR Device mode and status */
+#define D7ALSEN (1 << 7)
+#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)
+
+/* ADP8870_ALS1_EN Main ALS comparator level enable */
+#define L5_EN (1 << 3)
+#define L4_EN (1 << 2)
+#define L3_EN (1 << 1)
+#define L2_EN (1 << 0)
+
+#define CFGR_BLV_SHIFT 3
+#define CFGR_BLV_MASK 0x7
+#define ADP8870_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_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1)
+
+struct adp8870_bl {
+ struct i2c_client *client;
+ struct backlight_device *bl;
+ struct adp8870_led *led;
+ struct adp8870_backlight_platform_data *pdata;
+ struct mutex lock;
+ unsigned long cached_daylight_max;
+ int id;
+ int revid;
+ int current_brightness;
+};
+
+struct adp8870_led {
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct i2c_client *client;
+ enum led_brightness new_brightness;
+ int id;
+ int flags;
+};
+
+static int adp8870_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 = ret;
+ return 0;
+}
+
+
+static int adp8870_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret)
+ dev_err(&client->dev, "failed to write\n");
+
+ return ret;
+}
+
+static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
+{
+ struct adp8870_bl *data = i2c_get_clientdata(client);
+ uint8_t reg_val;
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ ret = adp8870_read(client, reg, &reg_val);
+
+ if (!ret && ((reg_val & bit_mask) != bit_mask)) {
+ reg_val |= bit_mask;
+ ret = adp8870_write(client, reg, reg_val);
+ }
+
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
+{
+ struct adp8870_bl *data = i2c_get_clientdata(client);
+ uint8_t reg_val;
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ ret = adp8870_read(client, reg, &reg_val);
+
+ if (!ret && (reg_val & bit_mask)) {
+ reg_val &= ~bit_mask;
+ ret = adp8870_write(client, reg, reg_val);
+ }
+
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+/*
+ * Independent sink / LED
+ */
+#if defined(ADP8870_USE_LEDS)
+static void adp8870_led_work(struct work_struct *work)
+{
+ struct adp8870_led *led = container_of(work, struct adp8870_led, work);
+ adp8870_write(led->client, ADP8870_ISC1 + led->id - 1,
+ led->new_brightness >> 1);
+}
+
+static void adp8870_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct adp8870_led *led;
+
+ led = container_of(led_cdev, struct adp8870_led, cdev);
+ led->new_brightness = value;
+ /*
+ * Use workqueue for IO since I2C operations can sleep.
+ */
+ schedule_work(&led->work);
+}
+
+static int adp8870_led_setup(struct adp8870_led *led)
+{
+ struct i2c_client *client = led->client;
+ int ret = 0;
+
+ ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0);
+ if (ret)
+ return ret;
+
+ ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1));
+ if (ret)
+ return ret;
+
+ if (led->id > 4)
+ ret = adp8870_set_bits(client, ADP8870_ISCT1,
+ (led->flags & 0x3) << ((led->id - 5) * 2));
+ else
+ ret = adp8870_set_bits(client, ADP8870_ISCT2,
+ (led->flags & 0x3) << ((led->id - 1) * 2));
+
+ return ret;
+}
+
+static int adp8870_led_probe(struct i2c_client *client)
+{
+ struct adp8870_backlight_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct adp8870_bl *data = i2c_get_clientdata(client);
+ struct adp8870_led *led, *led_dat;
+ struct led_info *cur_led;
+ int ret, i;
+
+ led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led),
+ GFP_KERNEL);
+ if (led == NULL)
+ return -ENOMEM;
+
+ ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_ISCT1,
+ (pdata->led_on_time & 0x3) << 6);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_ISCF,
+ FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pdata->num_leds; ++i) {
+ cur_led = &pdata->leds[i];
+ led_dat = &led[i];
+
+ led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK;
+
+ if (led_dat->id > 7 || led_dat->id < 1) {
+ dev_err(&client->dev, "Invalid LED ID %d\n",
+ led_dat->id);
+ ret = -EINVAL;
+ 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);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ led_dat->cdev.name = cur_led->name;
+ led_dat->cdev.default_trigger = cur_led->default_trigger;
+ led_dat->cdev.brightness_set = adp8870_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, adp8870_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 = adp8870_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);
+ }
+
+ return ret;
+}
+
+static int adp8870_led_remove(struct i2c_client *client)
+{
+ struct adp8870_backlight_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct adp8870_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);
+ }
+
+ return 0;
+}
+#else
+static int adp8870_led_probe(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int adp8870_led_remove(struct i2c_client *client)
+{
+ return 0;
+}
+#endif
+
+static int adp8870_bl_set(struct backlight_device *bl, int brightness)
+{
+ struct adp8870_bl *data = bl_get_data(bl);
+ struct i2c_client *client = data->client;
+ int ret = 0;
+
+ if (data->pdata->en_ambl_sens) {
+ if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) {
+ /* Disable Ambient Light auto adjust */
+ ret = adp8870_clr_bits(client, ADP8870_MDCR,
+ CMP_AUTOEN);
+ if (ret)
+ return ret;
+ ret = adp8870_write(client, ADP8870_BLMX1, brightness);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
+ * restore daylight l1 sysfs brightness
+ */
+ ret = adp8870_write(client, ADP8870_BLMX1,
+ data->cached_daylight_max);
+ if (ret)
+ return ret;
+
+ ret = adp8870_set_bits(client, ADP8870_MDCR,
+ CMP_AUTOEN);
+ if (ret)
+ return ret;
+ }
+ } else {
+ ret = adp8870_write(client, ADP8870_BLMX1, brightness);
+ if (ret)
+ return ret;
+ }
+
+ if (data->current_brightness && brightness == 0)
+ ret = adp8870_set_bits(client,
+ ADP8870_MDCR, DIM_EN);
+ else if (data->current_brightness == 0 && brightness)
+ ret = adp8870_clr_bits(client,
+ ADP8870_MDCR, DIM_EN);
+
+ if (!ret)
+ data->current_brightness = brightness;
+
+ return ret;
+}
+
+static int adp8870_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 adp8870_bl_set(bl, brightness);
+}
+
+static int adp8870_bl_get_brightness(struct backlight_device *bl)
+{
+ struct adp8870_bl *data = bl_get_data(bl);
+
+ return data->current_brightness;
+}
+
+static const struct backlight_ops adp8870_bl_ops = {
+ .update_status = adp8870_bl_update_status,
+ .get_brightness = adp8870_bl_get_brightness,
+};
+
+static int adp8870_bl_setup(struct backlight_device *bl)
+{
+ struct adp8870_bl *data = bl_get_data(bl);
+ struct i2c_client *client = data->client;
+ struct adp8870_backlight_platform_data *pdata = data->pdata;
+ int ret = 0;
+
+ ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim);
+ if (ret)
+ return ret;
+
+ if (pdata->en_ambl_sens) {
+ data->cached_daylight_max = pdata->l1_daylight_max;
+ ret = adp8870_write(client, ADP8870_BLMX2,
+ pdata->l2_bright_max);
+ if (ret)
+ return ret;
+ ret = adp8870_write(client, ADP8870_BLDM2,
+ pdata->l2_bright_dim);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLMX3,
+ pdata->l3_office_max);
+ if (ret)
+ return ret;
+ ret = adp8870_write(client, ADP8870_BLDM3,
+ pdata->l3_office_dim);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLMX4,
+ pdata->l4_indoor_max);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLDM4,
+ pdata->l4_indor_dim);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLMX5,
+ pdata->l5_dark_max);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLDM5,
+ pdata->l5_dark_dim);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN |
+ L3_EN | L2_EN);
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_CMP_CTL,
+ ALS_CMPR_CFG_VAL(pdata->abml_filt));
+ if (ret)
+ return ret;
+ }
+
+ ret = adp8870_write(client, ADP8870_CFGR,
+ BL_CFGR_VAL(pdata->bl_fade_law, 0));
+ if (ret)
+ return ret;
+
+ ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in,
+ pdata->bl_fade_out));
+ if (ret)
+ return ret;
+ /*
+ * ADP8870 Rev0 requires GDWN_DIS bit set
+ */
+
+ ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY |
+ (data->revid == 0 ? GDWN_DIS : 0));
+
+ return ret;
+}
+
+static ssize_t adp8870_show(struct device *dev, char *buf, int reg)
+{
+ struct adp8870_bl *data = dev_get_drvdata(dev);
+ int error;
+ uint8_t reg_val;
+
+ mutex_lock(&data->lock);
+ error = adp8870_read(data->client, reg, &reg_val);
+ mutex_unlock(&data->lock);
+
+ if (error < 0)
+ return error;
+
+ return sprintf(buf, "%u\n", reg_val);
+}
+
+static ssize_t adp8870_store(struct device *dev, const char *buf,
+ size_t count, int reg)
+{
+ struct adp8870_bl *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->lock);
+ adp8870_write(data->client, reg, val);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLMX5);
+}
+
+static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLMX5);
+}
+static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show,
+ adp8870_bl_l5_dark_max_store);
+
+
+static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLMX4);
+}
+
+static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLMX4);
+}
+static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show,
+ adp8870_bl_l4_indoor_max_store);
+
+
+static ssize_t adp8870_bl_l3_office_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLMX3);
+}
+
+static ssize_t adp8870_bl_l3_office_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLMX3);
+}
+
+static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show,
+ adp8870_bl_l3_office_max_store);
+
+static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLMX2);
+}
+
+static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLMX2);
+}
+static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show,
+ adp8870_bl_l2_bright_max_store);
+
+static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLMX1);
+}
+
+static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct adp8870_bl *data = dev_get_drvdata(dev);
+ int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
+ if (ret)
+ return ret;
+
+ return adp8870_store(dev, buf, count, ADP8870_BLMX1);
+}
+static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show,
+ adp8870_bl_l1_daylight_max_store);
+
+static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLDM5);
+}
+
+static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLDM5);
+}
+static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show,
+ adp8870_bl_l5_dark_dim_store);
+
+static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLDM4);
+}
+
+static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLDM4);
+}
+static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show,
+ adp8870_bl_l4_indoor_dim_store);
+
+
+static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLDM3);
+}
+
+static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLDM3);
+}
+static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show,
+ adp8870_bl_l3_office_dim_store);
+
+static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLDM2);
+}
+
+static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLDM2);
+}
+static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show,
+ adp8870_bl_l2_bright_dim_store);
+
+static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return adp8870_show(dev, buf, ADP8870_BLDM1);
+}
+
+static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return adp8870_store(dev, buf, count, ADP8870_BLDM1);
+}
+static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show,
+ adp8870_bl_l1_daylight_dim_store);
+
+#ifdef ADP8870_EXT_FEATURES
+static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adp8870_bl *data = dev_get_drvdata(dev);
+ int error;
+ uint8_t reg_val;
+ uint16_t ret_val;
+
+ mutex_lock(&data->lock);
+ error = adp8870_read(data->client, ADP8870_PH1LEVL, &reg_val);
+ if (error < 0) {
+ mutex_unlock(&data->lock);
+ return error;
+ }
+ ret_val = reg_val;
+ error = adp8870_read(data->client, ADP8870_PH1LEVH, &reg_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,
+ adp8870_bl_ambient_light_level_show, NULL);
+
+static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adp8870_bl *data = dev_get_drvdata(dev);
+ int error;
+ uint8_t reg_val;
+
+ mutex_lock(&data->lock);
+ error = adp8870_read(data->client, ADP8870_CFGR, &reg_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 adp8870_bl_ambient_light_zone_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adp8870_bl *data = dev_get_drvdata(dev);
+ unsigned long val;
+ uint8_t reg_val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val == 0) {
+ /* Enable automatic ambient light sensing */
+ adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
+ } else if ((val > 0) && (val < 6)) {
+ /* Disable automatic ambient light sensing */
+ adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
+
+ /* Set user supplied ambient light zone */
+ mutex_lock(&data->lock);
+ adp8870_read(data->client, ADP8870_CFGR, &reg_val);
+ reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
+ reg_val |= (val - 1) << CFGR_BLV_SHIFT;
+ adp8870_write(data->client, ADP8870_CFGR, reg_val);
+ mutex_unlock(&data->lock);
+ }
+
+ return count;
+}
+static DEVICE_ATTR(ambient_light_zone, 0664,
+ adp8870_bl_ambient_light_zone_show,
+ adp8870_bl_ambient_light_zone_store);
+#endif
+
+static struct attribute *adp8870_bl_attributes[] = {
+ &dev_attr_l5_dark_max.attr,
+ &dev_attr_l5_dark_dim.attr,
+ &dev_attr_l4_indoor_max.attr,
+ &dev_attr_l4_indoor_dim.attr,
+ &dev_attr_l3_office_max.attr,
+ &dev_attr_l3_office_dim.attr,
+ &dev_attr_l2_bright_max.attr,
+ &dev_attr_l2_bright_dim.attr,
+ &dev_attr_l1_daylight_max.attr,
+ &dev_attr_l1_daylight_dim.attr,
+#ifdef ADP8870_EXT_FEATURES
+ &dev_attr_ambient_light_level.attr,
+ &dev_attr_ambient_light_zone.attr,
+#endif
+ NULL
+};
+
+static const struct attribute_group adp8870_bl_attr_group = {
+ .attrs = adp8870_bl_attributes,
+};
+
+static int adp8870_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ struct adp8870_bl *data;
+ struct adp8870_backlight_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ 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;
+ }
+
+ ret = adp8870_read(client, ADP8870_MFDVID, &reg_val);
+ if (ret < 0)
+ return -EIO;
+
+ if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) {
+ dev_err(&client->dev, "failed to probe\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->revid = ADP8870_DEVID(reg_val);
+ data->client = client;
+ data->pdata = pdata;
+ data->id = id->driver_data;
+ data->current_brightness = 0;
+ i2c_set_clientdata(client, data);
+
+ mutex_init(&data->lock);
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS;
+ bl = devm_backlight_device_register(&client->dev,
+ dev_driver_string(&client->dev),
+ &client->dev, data, &adp8870_bl_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&client->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ data->bl = bl;
+
+ if (pdata->en_ambl_sens) {
+ ret = sysfs_create_group(&bl->dev.kobj,
+ &adp8870_bl_attr_group);
+ if (ret) {
+ dev_err(&client->dev, "failed to register sysfs\n");
+ return ret;
+ }
+ }
+
+ ret = adp8870_bl_setup(bl);
+ if (ret) {
+ ret = -EIO;
+ goto out;
+ }
+
+ backlight_update_status(bl);
+
+ dev_info(&client->dev, "Rev.%d Backlight\n", data->revid);
+
+ if (pdata->num_leds)
+ adp8870_led_probe(client);
+
+ return 0;
+
+out:
+ if (data->pdata->en_ambl_sens)
+ sysfs_remove_group(&data->bl->dev.kobj,
+ &adp8870_bl_attr_group);
+
+ return ret;
+}
+
+static int adp8870_remove(struct i2c_client *client)
+{
+ struct adp8870_bl *data = i2c_get_clientdata(client);
+
+ adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
+
+ if (data->led)
+ adp8870_led_remove(client);
+
+ if (data->pdata->en_ambl_sens)
+ sysfs_remove_group(&data->bl->dev.kobj,
+ &adp8870_bl_attr_group);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adp8870_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
+
+ return 0;
+}
+
+static int adp8870_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ adp8870_set_bits(client, ADP8870_MDCR, NSTBY | BLEN);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adp8870_i2c_pm_ops, adp8870_i2c_suspend,
+ adp8870_i2c_resume);
+
+static const struct i2c_device_id adp8870_id[] = {
+ { "adp8870", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp8870_id);
+
+static struct i2c_driver adp8870_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &adp8870_i2c_pm_ops,
+ },
+ .probe = adp8870_probe,
+ .remove = adp8870_remove,
+ .id_table = adp8870_id,
+};
+
+module_i2c_driver(adp8870_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP8870 Backlight driver");
+MODULE_ALIAS("i2c:adp8870-backlight");
diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c
new file mode 100644
index 00000000000..d8952c4aa68
--- /dev/null
+++ b/drivers/video/backlight/ams369fg06.c
@@ -0,0 +1,577 @@
+/*
+ * ams369fg06 AMOLED LCD panel driver.
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * Derived from drivers/video/s6e63m0.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.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+
+#define SLEEPMSEC 0x1000
+#define ENDDEF 0x2000
+#define DEFMASK 0xFF00
+#define COMMAND_ONLY 0xFE
+#define DATA_ONLY 0xFF
+
+#define MAX_GAMMA_LEVEL 5
+#define GAMMA_TABLE_COUNT 21
+
+#define MIN_BRIGHTNESS 0
+#define MAX_BRIGHTNESS 255
+#define DEFAULT_BRIGHTNESS 150
+
+struct ams369fg06 {
+ struct device *dev;
+ struct spi_device *spi;
+ unsigned int power;
+ struct lcd_device *ld;
+ struct backlight_device *bd;
+ struct lcd_platform_data *lcd_pd;
+};
+
+static const unsigned short seq_display_on[] = {
+ 0x14, 0x03,
+ ENDDEF, 0x0000
+};
+
+static const unsigned short seq_display_off[] = {
+ 0x14, 0x00,
+ ENDDEF, 0x0000
+};
+
+static const unsigned short seq_stand_by_on[] = {
+ 0x1D, 0xA1,
+ SLEEPMSEC, 200,
+ ENDDEF, 0x0000
+};
+
+static const unsigned short seq_stand_by_off[] = {
+ 0x1D, 0xA0,
+ SLEEPMSEC, 250,
+ ENDDEF, 0x0000
+};
+
+static const unsigned short seq_setting[] = {
+ 0x31, 0x08,
+ 0x32, 0x14,
+ 0x30, 0x02,
+ 0x27, 0x01,
+ 0x12, 0x08,
+ 0x13, 0x08,
+ 0x15, 0x00,
+ 0x16, 0x00,
+
+ 0xef, 0xd0,
+ DATA_ONLY, 0xe8,
+
+ 0x39, 0x44,
+ 0x40, 0x00,
+ 0x41, 0x3f,
+ 0x42, 0x2a,
+ 0x43, 0x27,
+ 0x44, 0x27,
+ 0x45, 0x1f,
+ 0x46, 0x44,
+ 0x50, 0x00,
+ 0x51, 0x00,
+ 0x52, 0x17,
+ 0x53, 0x24,
+ 0x54, 0x26,
+ 0x55, 0x1f,
+ 0x56, 0x43,
+ 0x60, 0x00,
+ 0x61, 0x3f,
+ 0x62, 0x2a,
+ 0x63, 0x25,
+ 0x64, 0x24,
+ 0x65, 0x1b,
+ 0x66, 0x5c,
+
+ 0x17, 0x22,
+ 0x18, 0x33,
+ 0x19, 0x03,
+ 0x1a, 0x01,
+ 0x22, 0xa4,
+ 0x23, 0x00,
+ 0x26, 0xa0,
+
+ 0x1d, 0xa0,
+ SLEEPMSEC, 300,
+
+ 0x14, 0x03,
+
+ ENDDEF, 0x0000
+};
+
+/* gamma value: 2.2 */
+static const unsigned int ams369fg06_22_250[] = {
+ 0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
+ 0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
+ 0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
+};
+
+static const unsigned int ams369fg06_22_200[] = {
+ 0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
+ 0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
+ 0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
+};
+
+static const unsigned int ams369fg06_22_150[] = {
+ 0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
+ 0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
+ 0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
+};
+
+static const unsigned int ams369fg06_22_100[] = {
+ 0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
+ 0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
+ 0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
+};
+
+static const unsigned int ams369fg06_22_50[] = {
+ 0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
+ 0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
+ 0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
+};
+
+struct ams369fg06_gamma {
+ unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
+};
+
+static struct ams369fg06_gamma gamma_table = {
+ .gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
+ .gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
+ .gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
+ .gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
+ .gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
+};
+
+static int ams369fg06_spi_write_byte(struct ams369fg06 *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 ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
+ unsigned char command)
+{
+ int ret = 0;
+
+ if (address != DATA_ONLY)
+ ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
+ if (command != COMMAND_ONLY)
+ ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
+
+ return ret;
+}
+
+static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
+ const unsigned short *wbuf)
+{
+ int ret = 0, i = 0;
+
+ while ((wbuf[i] & DEFMASK) != ENDDEF) {
+ if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
+ ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
+ if (ret)
+ break;
+ } else {
+ msleep(wbuf[i+1]);
+ }
+ i += 2;
+ }
+
+ return ret;
+}
+
+static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
+ const unsigned int *gamma)
+{
+ unsigned int i = 0;
+ int ret = 0;
+
+ for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
+ ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
+ ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
+ ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
+ if (ret) {
+ dev_err(lcd->dev, "failed to set gamma table.\n");
+ goto gamma_err;
+ }
+ }
+
+gamma_err:
+ return ret;
+}
+
+static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
+{
+ int ret = 0;
+ int gamma = 0;
+
+ if ((brightness >= 0) && (brightness <= 50))
+ gamma = 0;
+ else if ((brightness > 50) && (brightness <= 100))
+ gamma = 1;
+ else if ((brightness > 100) && (brightness <= 150))
+ gamma = 2;
+ else if ((brightness > 150) && (brightness <= 200))
+ gamma = 3;
+ else if ((brightness > 200) && (brightness <= 255))
+ gamma = 4;
+
+ ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
+
+ return ret;
+}
+
+static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
+{
+ int ret, i;
+ static const unsigned short *init_seq[] = {
+ seq_setting,
+ seq_stand_by_off,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
+{
+ int ret, i;
+ static const unsigned short *init_seq[] = {
+ seq_stand_by_off,
+ seq_display_on,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
+{
+ int ret, i;
+
+ static const unsigned short *init_seq[] = {
+ seq_display_off,
+ seq_stand_by_on,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ams369fg06_power_is_on(int power)
+{
+ return power <= FB_BLANK_NORMAL;
+}
+
+static int ams369fg06_power_on(struct ams369fg06 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd;
+ struct backlight_device *bd;
+
+ pd = lcd->lcd_pd;
+ bd = lcd->bd;
+
+ if (pd->power_on) {
+ pd->power_on(lcd->ld, 1);
+ msleep(pd->power_on_delay);
+ }
+
+ if (!pd->reset) {
+ dev_err(lcd->dev, "reset is NULL.\n");
+ return -EINVAL;
+ } else {
+ pd->reset(lcd->ld);
+ msleep(pd->reset_delay);
+ }
+
+ ret = ams369fg06_ldi_init(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to initialize ldi.\n");
+ return ret;
+ }
+
+ ret = ams369fg06_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 = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
+ if (ret) {
+ dev_err(lcd->dev, "lcd gamma setting failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ams369fg06_power_off(struct ams369fg06 *lcd)
+{
+ int ret;
+ struct lcd_platform_data *pd;
+
+ pd = lcd->lcd_pd;
+
+ ret = ams369fg06_ldi_disable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "lcd setting failed.\n");
+ return -EIO;
+ }
+
+ msleep(pd->power_off_delay);
+
+ if (pd->power_on)
+ pd->power_on(lcd->ld, 0);
+
+ return 0;
+}
+
+static int ams369fg06_power(struct ams369fg06 *lcd, int power)
+{
+ int ret = 0;
+
+ if (ams369fg06_power_is_on(power) &&
+ !ams369fg06_power_is_on(lcd->power))
+ ret = ams369fg06_power_on(lcd);
+ else if (!ams369fg06_power_is_on(power) &&
+ ams369fg06_power_is_on(lcd->power))
+ ret = ams369fg06_power_off(lcd);
+
+ if (!ret)
+ lcd->power = power;
+
+ return ret;
+}
+
+static int ams369fg06_get_power(struct lcd_device *ld)
+{
+ struct ams369fg06 *lcd = lcd_get_data(ld);
+
+ return lcd->power;
+}
+
+static int ams369fg06_set_power(struct lcd_device *ld, int power)
+{
+ struct ams369fg06 *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 ams369fg06_power(lcd, power);
+}
+
+static int ams369fg06_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int ams369fg06_set_brightness(struct backlight_device *bd)
+{
+ int ret = 0;
+ int brightness = bd->props.brightness;
+ struct ams369fg06 *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 = ams369fg06_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 ams369fg06_lcd_ops = {
+ .get_power = ams369fg06_get_power,
+ .set_power = ams369fg06_set_power,
+};
+
+static const struct backlight_ops ams369fg06_backlight_ops = {
+ .get_brightness = ams369fg06_get_brightness,
+ .update_status = ams369fg06_set_brightness,
+};
+
+static int ams369fg06_probe(struct spi_device *spi)
+{
+ int ret = 0;
+ struct ams369fg06 *lcd = NULL;
+ struct lcd_device *ld = NULL;
+ struct backlight_device *bd = NULL;
+ struct backlight_properties props;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
+ spi->bits_per_word = 16;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "spi setup failed.\n");
+ return ret;
+ }
+
+ lcd->spi = spi;
+ lcd->dev = &spi->dev;
+
+ lcd->lcd_pd = dev_get_platdata(&spi->dev);
+ if (!lcd->lcd_pd) {
+ dev_err(&spi->dev, "platform data is NULL\n");
+ return -EINVAL;
+ }
+
+ ld = devm_lcd_device_register(&spi->dev, "ams369fg06", &spi->dev, lcd,
+ &ams369fg06_lcd_ops);
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
+
+ lcd->ld = ld;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = MAX_BRIGHTNESS;
+
+ bd = devm_backlight_device_register(&spi->dev, "ams369fg06-bl",
+ &spi->dev, lcd,
+ &ams369fg06_backlight_ops, &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+ lcd->bd = bd;
+
+ 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;
+
+ ams369fg06_power(lcd, FB_BLANK_UNBLANK);
+ } else {
+ lcd->power = FB_BLANK_UNBLANK;
+ }
+
+ spi_set_drvdata(spi, lcd);
+
+ dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
+
+ return 0;
+}
+
+static int ams369fg06_remove(struct spi_device *spi)
+{
+ struct ams369fg06 *lcd = spi_get_drvdata(spi);
+
+ ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ams369fg06_suspend(struct device *dev)
+{
+ struct ams369fg06 *lcd = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "lcd->power = %d\n", lcd->power);
+
+ /*
+ * when lcd panel is suspend, lcd panel becomes off
+ * regardless of status.
+ */
+ return ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static int ams369fg06_resume(struct device *dev)
+{
+ struct ams369fg06 *lcd = dev_get_drvdata(dev);
+
+ lcd->power = FB_BLANK_POWERDOWN;
+
+ return ams369fg06_power(lcd, FB_BLANK_UNBLANK);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ams369fg06_pm_ops, ams369fg06_suspend,
+ ams369fg06_resume);
+
+static void ams369fg06_shutdown(struct spi_device *spi)
+{
+ struct ams369fg06 *lcd = spi_get_drvdata(spi);
+
+ ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver ams369fg06_driver = {
+ .driver = {
+ .name = "ams369fg06",
+ .owner = THIS_MODULE,
+ .pm = &ams369fg06_pm_ops,
+ },
+ .probe = ams369fg06_probe,
+ .remove = ams369fg06_remove,
+ .shutdown = ams369fg06_shutdown,
+};
+
+module_spi_driver(ams369fg06_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("ams369fg06 LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c
new file mode 100644
index 00000000000..d8432967668
--- /dev/null
+++ b/drivers/video/backlight/apple_bl.c
@@ -0,0 +1,257 @@
+/*
+ * Backlight Driver for Intel-based Apples
+ *
+ * Copyright (c) Red Hat <mjg@redhat.com>
+ * Based on code from Pommed:
+ * Copyright (C) 2006 Nicolas Boichat <nicolas @boichat.ch>
+ * Copyright (C) 2006 Felipe Alfaro Solana <felipe_alfaro @linuxmail.org>
+ * Copyright (C) 2007 Julien BLACHE <jb@jblache.org>
+ *
+ * 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 triggers SMIs which cause the firmware to change the
+ * backlight brightness. This is icky in many ways, but it's impractical to
+ * get at the firmware code in order to figure out what it's actually doing.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/atomic.h>
+#include <linux/apple_bl.h>
+
+static struct backlight_device *apple_backlight_device;
+
+struct hw_data {
+ /* I/O resource to allocate. */
+ unsigned long iostart;
+ unsigned long iolen;
+ /* Backlight operations structure. */
+ const struct backlight_ops backlight_ops;
+ void (*set_brightness)(int);
+};
+
+static const struct hw_data *hw_data;
+
+/* Module parameters. */
+static int debug;
+module_param_named(debug, debug, int, 0644);
+MODULE_PARM_DESC(debug, "Set to one to enable debugging messages.");
+
+/*
+ * Implementation for machines with Intel chipset.
+ */
+static void intel_chipset_set_brightness(int intensity)
+{
+ outb(0x04 | (intensity << 4), 0xb3);
+ outb(0xbf, 0xb2);
+}
+
+static int intel_chipset_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+
+ if (debug)
+ pr_debug("setting brightness to %d\n", intensity);
+
+ intel_chipset_set_brightness(intensity);
+ return 0;
+}
+
+static int intel_chipset_get_intensity(struct backlight_device *bd)
+{
+ int intensity;
+
+ outb(0x03, 0xb3);
+ outb(0xbf, 0xb2);
+ intensity = inb(0xb3) >> 4;
+
+ if (debug)
+ pr_debug("read brightness of %d\n", intensity);
+
+ return intensity;
+}
+
+static const struct hw_data intel_chipset_data = {
+ .iostart = 0xb2,
+ .iolen = 2,
+ .backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = intel_chipset_get_intensity,
+ .update_status = intel_chipset_send_intensity,
+ },
+ .set_brightness = intel_chipset_set_brightness,
+};
+
+/*
+ * Implementation for machines with Nvidia chipset.
+ */
+static void nvidia_chipset_set_brightness(int intensity)
+{
+ outb(0x04 | (intensity << 4), 0x52f);
+ outb(0xbf, 0x52e);
+}
+
+static int nvidia_chipset_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+
+ if (debug)
+ pr_debug("setting brightness to %d\n", intensity);
+
+ nvidia_chipset_set_brightness(intensity);
+ return 0;
+}
+
+static int nvidia_chipset_get_intensity(struct backlight_device *bd)
+{
+ int intensity;
+
+ outb(0x03, 0x52f);
+ outb(0xbf, 0x52e);
+ intensity = inb(0x52f) >> 4;
+
+ if (debug)
+ pr_debug("read brightness of %d\n", intensity);
+
+ return intensity;
+}
+
+static const struct hw_data nvidia_chipset_data = {
+ .iostart = 0x52e,
+ .iolen = 2,
+ .backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = nvidia_chipset_get_intensity,
+ .update_status = nvidia_chipset_send_intensity
+ },
+ .set_brightness = nvidia_chipset_set_brightness,
+};
+
+static int apple_bl_add(struct acpi_device *dev)
+{
+ struct backlight_properties props;
+ struct pci_dev *host;
+ int intensity;
+
+ host = pci_get_bus_and_slot(0, 0);
+
+ if (!host) {
+ pr_err("unable to find PCI host\n");
+ return -ENODEV;
+ }
+
+ if (host->vendor == PCI_VENDOR_ID_INTEL)
+ hw_data = &intel_chipset_data;
+ else if (host->vendor == PCI_VENDOR_ID_NVIDIA)
+ hw_data = &nvidia_chipset_data;
+
+ pci_dev_put(host);
+
+ if (!hw_data) {
+ pr_err("unknown hardware\n");
+ return -ENODEV;
+ }
+
+ /* Check that the hardware responds - this may not work under EFI */
+
+ intensity = hw_data->backlight_ops.get_brightness(NULL);
+
+ if (!intensity) {
+ hw_data->set_brightness(1);
+ if (!hw_data->backlight_ops.get_brightness(NULL))
+ return -ENODEV;
+
+ hw_data->set_brightness(0);
+ }
+
+ if (!request_region(hw_data->iostart, hw_data->iolen,
+ "Apple backlight"))
+ return -ENXIO;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = 15;
+ apple_backlight_device = backlight_device_register("apple_backlight",
+ NULL, NULL, &hw_data->backlight_ops, &props);
+
+ if (IS_ERR(apple_backlight_device)) {
+ release_region(hw_data->iostart, hw_data->iolen);
+ return PTR_ERR(apple_backlight_device);
+ }
+
+ apple_backlight_device->props.brightness =
+ hw_data->backlight_ops.get_brightness(apple_backlight_device);
+ backlight_update_status(apple_backlight_device);
+
+ return 0;
+}
+
+static int apple_bl_remove(struct acpi_device *dev)
+{
+ backlight_device_unregister(apple_backlight_device);
+
+ release_region(hw_data->iostart, hw_data->iolen);
+ hw_data = NULL;
+ return 0;
+}
+
+static const struct acpi_device_id apple_bl_ids[] = {
+ {"APP0002", 0},
+ {"", 0},
+};
+
+static struct acpi_driver apple_bl_driver = {
+ .name = "Apple backlight",
+ .ids = apple_bl_ids,
+ .ops = {
+ .add = apple_bl_add,
+ .remove = apple_bl_remove,
+ },
+};
+
+static atomic_t apple_bl_registered = ATOMIC_INIT(0);
+
+int apple_bl_register(void)
+{
+ if (atomic_xchg(&apple_bl_registered, 1) == 0)
+ return acpi_bus_register_driver(&apple_bl_driver);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apple_bl_register);
+
+void apple_bl_unregister(void)
+{
+ if (atomic_xchg(&apple_bl_registered, 0) == 1)
+ acpi_bus_unregister_driver(&apple_bl_driver);
+}
+EXPORT_SYMBOL_GPL(apple_bl_unregister);
+
+static int __init apple_bl_init(void)
+{
+ return apple_bl_register();
+}
+
+static void __exit apple_bl_exit(void)
+{
+ apple_bl_unregister();
+}
+
+module_init(apple_bl_init);
+module_exit(apple_bl_exit);
+
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_DESCRIPTION("Apple Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(acpi, apple_bl_ids);
+MODULE_ALIAS("mbp_nvidia_bl");
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
new file mode 100644
index 00000000000..bb1fc45b754
--- /dev/null
+++ b/drivers/video/backlight/as3711_bl.c
@@ -0,0 +1,480 @@
+/*
+ * AS3711 PMIC backlight driver, using DCDC Step Up Converters
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License as
+ * published by the Free Software Foundation
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/mfd/as3711.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+enum as3711_bl_type {
+ AS3711_BL_SU1,
+ AS3711_BL_SU2,
+};
+
+struct as3711_bl_data {
+ bool powered;
+ const char *fb_name;
+ struct device *fb_dev;
+ enum as3711_bl_type type;
+ int brightness;
+ struct backlight_device *bl;
+};
+
+struct as3711_bl_supply {
+ struct as3711_bl_data su1;
+ struct as3711_bl_data su2;
+ const struct as3711_bl_pdata *pdata;
+ struct as3711 *as3711;
+};
+
+static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su)
+{
+ switch (su->type) {
+ case AS3711_BL_SU1:
+ return container_of(su, struct as3711_bl_supply, su1);
+ case AS3711_BL_SU2:
+ return container_of(su, struct as3711_bl_supply, su2);
+ }
+ return NULL;
+}
+
+static int as3711_set_brightness_auto_i(struct as3711_bl_data *data,
+ unsigned int brightness)
+{
+ struct as3711_bl_supply *supply = to_supply(data);
+ struct as3711 *as3711 = supply->as3711;
+ const struct as3711_bl_pdata *pdata = supply->pdata;
+ int ret = 0;
+
+ /* Only all equal current values are supported */
+ if (pdata->su2_auto_curr1)
+ ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
+ brightness);
+ if (!ret && pdata->su2_auto_curr2)
+ ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
+ brightness);
+ if (!ret && pdata->su2_auto_curr3)
+ ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
+ brightness);
+
+ return ret;
+}
+
+static int as3711_set_brightness_v(struct as3711 *as3711,
+ unsigned int brightness,
+ unsigned int reg)
+{
+ if (brightness > 31)
+ return -EINVAL;
+
+ return regmap_update_bits(as3711->regmap, reg, 0xf0,
+ brightness << 4);
+}
+
+static int as3711_bl_su2_reset(struct as3711_bl_supply *supply)
+{
+ struct as3711 *as3711 = supply->as3711;
+ int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5,
+ 3, supply->pdata->su2_fbprot);
+ if (!ret)
+ ret = regmap_update_bits(as3711->regmap,
+ AS3711_STEPUP_CONTROL_2, 1, 0);
+ if (!ret)
+ ret = regmap_update_bits(as3711->regmap,
+ AS3711_STEPUP_CONTROL_2, 1, 1);
+ return ret;
+}
+
+/*
+ * Someone with less fragile or less expensive hardware could try to simplify
+ * the brightness adjustment procedure.
+ */
+static int as3711_bl_update_status(struct backlight_device *bl)
+{
+ struct as3711_bl_data *data = bl_get_data(bl);
+ struct as3711_bl_supply *supply = to_supply(data);
+ struct as3711 *as3711 = supply->as3711;
+ int brightness = bl->props.brightness;
+ int ret = 0;
+
+ dev_dbg(&bl->dev, "%s(): brightness %u, pwr %x, blank %x, state %x\n",
+ __func__, bl->props.brightness, bl->props.power,
+ bl->props.fb_blank, bl->props.state);
+
+ if (bl->props.power != FB_BLANK_UNBLANK ||
+ bl->props.fb_blank != FB_BLANK_UNBLANK ||
+ bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+ brightness = 0;
+
+ if (data->type == AS3711_BL_SU1) {
+ ret = as3711_set_brightness_v(as3711, brightness,
+ AS3711_STEPUP_CONTROL_1);
+ } else {
+ const struct as3711_bl_pdata *pdata = supply->pdata;
+
+ switch (pdata->su2_feedback) {
+ case AS3711_SU2_VOLTAGE:
+ ret = as3711_set_brightness_v(as3711, brightness,
+ AS3711_STEPUP_CONTROL_2);
+ break;
+ case AS3711_SU2_CURR_AUTO:
+ ret = as3711_set_brightness_auto_i(data, brightness / 4);
+ if (ret < 0)
+ return ret;
+ if (brightness) {
+ ret = as3711_bl_su2_reset(supply);
+ if (ret < 0)
+ return ret;
+ udelay(500);
+ ret = as3711_set_brightness_auto_i(data, brightness);
+ } else {
+ ret = regmap_update_bits(as3711->regmap,
+ AS3711_STEPUP_CONTROL_2, 1, 0);
+ }
+ break;
+ /* Manual one current feedback pin below */
+ case AS3711_SU2_CURR1:
+ ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
+ brightness);
+ break;
+ case AS3711_SU2_CURR2:
+ ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
+ brightness);
+ break;
+ case AS3711_SU2_CURR3:
+ ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
+ brightness);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ }
+ if (!ret)
+ data->brightness = brightness;
+
+ return ret;
+}
+
+static int as3711_bl_get_brightness(struct backlight_device *bl)
+{
+ struct as3711_bl_data *data = bl_get_data(bl);
+
+ return data->brightness;
+}
+
+static const struct backlight_ops as3711_bl_ops = {
+ .update_status = as3711_bl_update_status,
+ .get_brightness = as3711_bl_get_brightness,
+};
+
+static int as3711_bl_init_su2(struct as3711_bl_supply *supply)
+{
+ struct as3711 *as3711 = supply->as3711;
+ const struct as3711_bl_pdata *pdata = supply->pdata;
+ u8 ctl = 0;
+ int ret;
+
+ dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback);
+
+ /* Turn SU2 off */
+ ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0);
+ if (ret < 0)
+ return ret;
+
+ switch (pdata->su2_feedback) {
+ case AS3711_SU2_VOLTAGE:
+ ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0);
+ break;
+ case AS3711_SU2_CURR1:
+ ctl = 1;
+ ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1);
+ break;
+ case AS3711_SU2_CURR2:
+ ctl = 4;
+ ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2);
+ break;
+ case AS3711_SU2_CURR3:
+ ctl = 0x10;
+ ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3);
+ break;
+ case AS3711_SU2_CURR_AUTO:
+ if (pdata->su2_auto_curr1)
+ ctl = 2;
+ if (pdata->su2_auto_curr2)
+ ctl |= 8;
+ if (pdata->su2_auto_curr3)
+ ctl |= 0x20;
+ ret = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!ret)
+ ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl);
+
+ return ret;
+}
+
+static int as3711_bl_register(struct platform_device *pdev,
+ unsigned int max_brightness, struct as3711_bl_data *su)
+{
+ struct backlight_properties props = {.type = BACKLIGHT_RAW,};
+ struct backlight_device *bl;
+
+ /* max tuning I = 31uA for voltage- and 38250uA for current-feedback */
+ props.max_brightness = max_brightness;
+
+ bl = devm_backlight_device_register(&pdev->dev,
+ su->type == AS3711_BL_SU1 ?
+ "as3711-su1" : "as3711-su2",
+ &pdev->dev, su,
+ &as3711_bl_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ bl->props.brightness = props.max_brightness;
+
+ backlight_update_status(bl);
+
+ su->bl = bl;
+
+ return 0;
+}
+
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+ struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+ struct device_node *bl =
+ of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+ int ret;
+
+ if (!bl) {
+ dev_dbg(dev, "backlight node not found\n");
+ return -ENODEV;
+ }
+
+ fb = of_parse_phandle(bl, "su1-dev", 0);
+ if (fb) {
+ pdata->su1_fb = fb->full_name;
+
+ ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+ if (pdata->su1_max_uA <= 0)
+ ret = -EINVAL;
+ if (ret < 0)
+ return ret;
+ }
+
+ fb = of_parse_phandle(bl, "su2-dev", 0);
+ if (fb) {
+ int count = 0;
+
+ pdata->su2_fb = fb->full_name;
+
+ ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+ if (pdata->su2_max_uA <= 0)
+ ret = -EINVAL;
+ if (ret < 0)
+ return ret;
+
+ if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+ pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+ count++;
+ }
+ if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+ pdata->su2_feedback = AS3711_SU2_CURR1;
+ count++;
+ }
+ if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+ pdata->su2_feedback = AS3711_SU2_CURR2;
+ count++;
+ }
+ if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+ pdata->su2_feedback = AS3711_SU2_CURR3;
+ count++;
+ }
+ if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+ pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+ count++;
+ }
+ if (count != 1)
+ return -EINVAL;
+
+ count = 0;
+ if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+ pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+ count++;
+ }
+ if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+ pdata->su2_fbprot = AS3711_SU2_GPIO2;
+ count++;
+ }
+ if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+ pdata->su2_fbprot = AS3711_SU2_GPIO3;
+ count++;
+ }
+ if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+ pdata->su2_fbprot = AS3711_SU2_GPIO4;
+ count++;
+ }
+ if (count != 1)
+ return -EINVAL;
+
+ count = 0;
+ if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+ pdata->su2_auto_curr1 = true;
+ count++;
+ }
+ if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+ pdata->su2_auto_curr2 = true;
+ count++;
+ }
+ if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+ pdata->su2_auto_curr3 = true;
+ count++;
+ }
+
+ /*
+ * At least one su2-auto-curr* must be specified iff
+ * AS3711_SU2_CURR_AUTO is used
+ */
+ if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int as3711_backlight_probe(struct platform_device *pdev)
+{
+ struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
+ struct as3711_bl_supply *supply;
+ struct as3711_bl_data *su;
+ unsigned int max_brightness;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data, exiting...\n");
+ return -ENODEV;
+ }
+
+ if (pdev->dev.parent->of_node) {
+ ret = as3711_backlight_parse_dt(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!pdata->su1_fb && !pdata->su2_fb) {
+ dev_err(&pdev->dev, "No framebuffer specified\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Due to possible hardware damage I chose to block all modes,
+ * unsupported on my hardware. Anyone, wishing to use any of those modes
+ * will have to first review the code, then activate and test it.
+ */
+ if (pdata->su1_fb ||
+ pdata->su2_fbprot != AS3711_SU2_GPIO4 ||
+ pdata->su2_feedback != AS3711_SU2_CURR_AUTO) {
+ dev_warn(&pdev->dev,
+ "Attention! An untested mode has been chosen!\n"
+ "Please, review the code, enable, test, and report success:-)\n");
+ return -EINVAL;
+ }
+
+ supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL);
+ if (!supply)
+ return -ENOMEM;
+
+ supply->as3711 = as3711;
+ supply->pdata = pdata;
+
+ if (pdata->su1_fb) {
+ su = &supply->su1;
+ su->fb_name = pdata->su1_fb;
+ su->type = AS3711_BL_SU1;
+
+ max_brightness = min(pdata->su1_max_uA, 31);
+ ret = as3711_bl_register(pdev, max_brightness, su);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (pdata->su2_fb) {
+ su = &supply->su2;
+ su->fb_name = pdata->su2_fb;
+ su->type = AS3711_BL_SU2;
+
+ switch (pdata->su2_fbprot) {
+ case AS3711_SU2_GPIO2:
+ case AS3711_SU2_GPIO3:
+ case AS3711_SU2_GPIO4:
+ case AS3711_SU2_LX_SD4:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (pdata->su2_feedback) {
+ case AS3711_SU2_VOLTAGE:
+ max_brightness = min(pdata->su2_max_uA, 31);
+ break;
+ case AS3711_SU2_CURR1:
+ case AS3711_SU2_CURR2:
+ case AS3711_SU2_CURR3:
+ case AS3711_SU2_CURR_AUTO:
+ max_brightness = min(pdata->su2_max_uA / 150, 255);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = as3711_bl_init_su2(supply);
+ if (ret < 0)
+ return ret;
+
+ ret = as3711_bl_register(pdev, max_brightness, su);
+ if (ret < 0)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, supply);
+
+ return 0;
+}
+
+static struct platform_driver as3711_backlight_driver = {
+ .driver = {
+ .name = "as3711-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = as3711_backlight_probe,
+};
+
+module_platform_driver(as3711_backlight_driver);
+
+MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:as3711-backlight");
diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c
index 505c0823a10..261b1a4ec3d 100644
--- a/drivers/video/backlight/atmel-pwm-bl.c
+++ b/drivers/video/backlight/atmel-pwm-bl.c
@@ -12,11 +12,11 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
-#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/backlight.h>
#include <linux/atmel_pwm.h>
#include <linux/atmel-pwm-bl.h>
+#include <linux/slab.h>
struct atmel_pwm_bl {
const struct atmel_pwm_bl_platform_data *pdata;
@@ -26,6 +26,14 @@ struct atmel_pwm_bl {
int gpio_on;
};
+static void atmel_pwm_bl_set_gpio_on(struct atmel_pwm_bl *pwmbl, int on)
+{
+ if (!gpio_is_valid(pwmbl->gpio_on))
+ return;
+
+ gpio_set_value(pwmbl->gpio_on, on ^ pwmbl->pdata->on_active_low);
+}
+
static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
{
struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
@@ -48,19 +56,13 @@ static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
pwm_duty = pwmbl->pdata->pwm_duty_min;
if (!intensity) {
- if (pwmbl->gpio_on != -1) {
- gpio_set_value(pwmbl->gpio_on,
- 0 ^ pwmbl->pdata->on_active_low);
- }
+ atmel_pwm_bl_set_gpio_on(pwmbl, 0);
pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
pwm_channel_disable(&pwmbl->pwmc);
} else {
pwm_channel_enable(&pwmbl->pwmc);
pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
- if (pwmbl->gpio_on != -1) {
- gpio_set_value(pwmbl->gpio_on,
- 1 ^ pwmbl->pdata->on_active_low);
- }
+ atmel_pwm_bl_set_gpio_on(pwmbl, 1);
}
return 0;
@@ -69,17 +71,16 @@ static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
static int atmel_pwm_bl_get_intensity(struct backlight_device *bd)
{
struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
- u8 intensity;
+ u32 cdty;
+ u32 intensity;
- if (pwmbl->pdata->pwm_active_low) {
- intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) -
- pwmbl->pdata->pwm_duty_min;
- } else {
- intensity = pwmbl->pdata->pwm_duty_max -
- pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY);
- }
+ cdty = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY);
+ if (pwmbl->pdata->pwm_active_low)
+ intensity = cdty - pwmbl->pdata->pwm_duty_min;
+ else
+ intensity = pwmbl->pdata->pwm_duty_max - cdty;
- return intensity;
+ return intensity & 0xffff;
}
static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl)
@@ -105,71 +106,71 @@ static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl)
pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD,
pwmbl->pdata->pwm_compare_max);
- dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver "
- "(%lu Hz)\n", pwmbl->pwmc.mck /
- pwmbl->pdata->pwm_compare_max /
- (1 << prescale));
+ dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver (%lu Hz)\n",
+ pwmbl->pwmc.mck / pwmbl->pdata->pwm_compare_max /
+ (1 << prescale));
return pwm_channel_enable(&pwmbl->pwmc);
}
-static struct backlight_ops atmel_pwm_bl_ops = {
+static const struct backlight_ops atmel_pwm_bl_ops = {
.get_brightness = atmel_pwm_bl_get_intensity,
.update_status = atmel_pwm_bl_set_intensity,
};
static int atmel_pwm_bl_probe(struct platform_device *pdev)
{
+ struct backlight_properties props;
const struct atmel_pwm_bl_platform_data *pdata;
struct backlight_device *bldev;
struct atmel_pwm_bl *pwmbl;
+ unsigned long flags;
int retval;
- pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL);
- if (!pwmbl)
- return -ENOMEM;
-
- pwmbl->pdev = pdev;
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- retval = -ENODEV;
- goto err_free_mem;
- }
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata)
+ return -ENODEV;
if (pdata->pwm_compare_max < pdata->pwm_duty_max ||
pdata->pwm_duty_min > pdata->pwm_duty_max ||
- pdata->pwm_frequency == 0) {
- retval = -EINVAL;
- goto err_free_mem;
- }
+ pdata->pwm_frequency == 0)
+ return -EINVAL;
+
+ pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl),
+ GFP_KERNEL);
+ if (!pwmbl)
+ return -ENOMEM;
+ pwmbl->pdev = pdev;
pwmbl->pdata = pdata;
pwmbl->gpio_on = pdata->gpio_on;
retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc);
if (retval)
- goto err_free_mem;
+ return retval;
- if (pwmbl->gpio_on != -1) {
- retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl");
- if (retval) {
- pwmbl->gpio_on = -1;
- goto err_free_pwm;
- }
+ if (gpio_is_valid(pwmbl->gpio_on)) {
+ /* Turn display off by default. */
+ if (pdata->on_active_low)
+ flags = GPIOF_OUT_INIT_HIGH;
+ else
+ flags = GPIOF_OUT_INIT_LOW;
- /* Turn display off by defatult. */
- retval = gpio_direction_output(pwmbl->gpio_on,
- 0 ^ pdata->on_active_low);
+ retval = devm_gpio_request_one(&pdev->dev, pwmbl->gpio_on,
+ flags, "gpio_atmel_pwm_bl");
if (retval)
- goto err_free_gpio;
+ goto err_free_pwm;
}
- bldev = backlight_device_register("atmel-pwm-bl",
- &pdev->dev, pwmbl, &atmel_pwm_bl_ops);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min;
+ bldev = devm_backlight_device_register(&pdev->dev, "atmel-pwm-bl",
+ &pdev->dev, pwmbl, &atmel_pwm_bl_ops,
+ &props);
if (IS_ERR(bldev)) {
retval = PTR_ERR(bldev);
- goto err_free_gpio;
+ goto err_free_pwm;
}
pwmbl->bldev = bldev;
@@ -178,43 +179,29 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
/* Power up the backlight by default at middle intesity. */
bldev->props.power = FB_BLANK_UNBLANK;
- bldev->props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min;
bldev->props.brightness = bldev->props.max_brightness / 2;
retval = atmel_pwm_bl_init_pwm(pwmbl);
if (retval)
- goto err_free_bl_dev;
+ goto err_free_pwm;
atmel_pwm_bl_set_intensity(bldev);
return 0;
-err_free_bl_dev:
- platform_set_drvdata(pdev, NULL);
- backlight_device_unregister(bldev);
-err_free_gpio:
- if (pwmbl->gpio_on != -1)
- gpio_free(pwmbl->gpio_on);
err_free_pwm:
pwm_channel_free(&pwmbl->pwmc);
-err_free_mem:
- kfree(pwmbl);
+
return retval;
}
-static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
+static int atmel_pwm_bl_remove(struct platform_device *pdev)
{
struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
- if (pwmbl->gpio_on != -1) {
- gpio_set_value(pwmbl->gpio_on, 0);
- gpio_free(pwmbl->gpio_on);
- }
+ atmel_pwm_bl_set_gpio_on(pwmbl, 0);
pwm_channel_disable(&pwmbl->pwmc);
pwm_channel_free(&pwmbl->pwmc);
- backlight_device_unregister(pwmbl->bldev);
- platform_set_drvdata(pdev, NULL);
- kfree(pwmbl);
return 0;
}
@@ -224,21 +211,13 @@ static struct platform_driver atmel_pwm_bl_driver = {
.name = "atmel-pwm-bl",
},
/* REVISIT add suspend() and resume() */
- .remove = __exit_p(atmel_pwm_bl_remove),
+ .probe = atmel_pwm_bl_probe,
+ .remove = atmel_pwm_bl_remove,
};
-static int __init atmel_pwm_bl_init(void)
-{
- return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe);
-}
-module_init(atmel_pwm_bl_init);
-
-static void __exit atmel_pwm_bl_exit(void)
-{
- platform_driver_unregister(&atmel_pwm_bl_driver);
-}
-module_exit(atmel_pwm_bl_exit);
+module_platform_driver(atmel_pwm_bl_driver);
MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
MODULE_DESCRIPTION("Atmel PWM backlight driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel-pwm-bl");
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index fab0bc874b5..428089009cd 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -5,6 +5,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -13,22 +15,35 @@
#include <linux/ctype.h>
#include <linux/err.h>
#include <linux/fb.h>
+#include <linux/slab.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
+static struct list_head backlight_dev_list;
+static struct mutex backlight_dev_list_mutex;
+static struct blocking_notifier_head backlight_notifier;
+
+static const char *const backlight_types[] = {
+ [BACKLIGHT_RAW] = "raw",
+ [BACKLIGHT_PLATFORM] = "platform",
+ [BACKLIGHT_FIRMWARE] = "firmware",
+};
+
#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
/* This callback gets called when something important happens inside a
* framebuffer driver. We're looking if that important event is blanking,
- * and if it is, we're switching backlight power as well ...
+ * and if it is and necessary, we're switching backlight power as well ...
*/
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct backlight_device *bd;
struct fb_event *evdata = data;
+ int node = evdata->info->node;
+ int fb_blank = 0;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
@@ -38,9 +53,25 @@ static int fb_notifier_callback(struct notifier_block *self,
mutex_lock(&bd->ops_lock);
if (bd->ops)
if (!bd->ops->check_fb ||
- bd->ops->check_fb(evdata->info)) {
- bd->props.fb_blank = *(int *)evdata->data;
- backlight_update_status(bd);
+ bd->ops->check_fb(bd, evdata->info)) {
+ fb_blank = *(int *)evdata->data;
+ if (fb_blank == FB_BLANK_UNBLANK &&
+ !bd->fb_bl_on[node]) {
+ bd->fb_bl_on[node] = true;
+ if (!bd->use_count++) {
+ bd->props.state &= ~BL_CORE_FBBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+ }
+ } else if (fb_blank != FB_BLANK_UNBLANK &&
+ bd->fb_bl_on[node]) {
+ bd->fb_bl_on[node] = false;
+ if (!(--bd->use_count)) {
+ bd->props.state |= BL_CORE_FBBLANK;
+ bd->props.fb_blank = fb_blank;
+ backlight_update_status(bd);
+ }
+ }
}
mutex_unlock(&bd->ops_lock);
return 0;
@@ -69,31 +100,50 @@ static inline void backlight_unregister_fb(struct backlight_device *bd)
}
#endif /* CONFIG_FB */
-static ssize_t backlight_show_power(struct device *dev,
- struct device_attribute *attr,char *buf)
+static void backlight_generate_event(struct backlight_device *bd,
+ enum backlight_update_reason reason)
+{
+ char *envp[2];
+
+ switch (reason) {
+ case BACKLIGHT_UPDATE_SYSFS:
+ envp[0] = "SOURCE=sysfs";
+ break;
+ case BACKLIGHT_UPDATE_HOTKEY:
+ envp[0] = "SOURCE=hotkey";
+ break;
+ default:
+ envp[0] = "SOURCE=unknown";
+ break;
+ }
+ envp[1] = NULL;
+ kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
+ sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
+}
+
+static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct backlight_device *bd = to_backlight_device(dev);
return sprintf(buf, "%d\n", bd->props.power);
}
-static ssize_t backlight_store_power(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- int rc = -ENXIO;
- char *endp;
+ int rc;
struct backlight_device *bd = to_backlight_device(dev);
- int power = simple_strtoul(buf, &endp, 0);
- size_t size = endp - buf;
+ unsigned long power;
- if (*endp && isspace(*endp))
- size++;
- if (size != count)
- return -EINVAL;
+ rc = kstrtoul(buf, 0, &power);
+ if (rc)
+ return rc;
+ rc = -ENXIO;
mutex_lock(&bd->ops_lock);
if (bd->ops) {
- pr_debug("backlight: set power to %d\n", power);
+ pr_debug("set power to %lu\n", power);
if (bd->props.power != power) {
bd->props.power = power;
backlight_update_status(bd);
@@ -104,8 +154,9 @@ static ssize_t backlight_store_power(struct device *dev,
return rc;
}
+static DEVICE_ATTR_RW(bl_power);
-static ssize_t backlight_show_brightness(struct device *dev,
+static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct backlight_device *bd = to_backlight_device(dev);
@@ -113,48 +164,57 @@ static ssize_t backlight_show_brightness(struct device *dev,
return sprintf(buf, "%d\n", bd->props.brightness);
}
-static ssize_t backlight_store_brightness(struct device *dev,
+static ssize_t brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- int rc = -ENXIO;
- char *endp;
+ int rc;
struct backlight_device *bd = to_backlight_device(dev);
- int brightness = simple_strtoul(buf, &endp, 0);
- size_t size = endp - buf;
+ unsigned long brightness;
+
+ rc = kstrtoul(buf, 0, &brightness);
+ if (rc)
+ return rc;
- if (*endp && isspace(*endp))
- size++;
- if (size != count)
- return -EINVAL;
+ rc = -ENXIO;
mutex_lock(&bd->ops_lock);
if (bd->ops) {
if (brightness > bd->props.max_brightness)
rc = -EINVAL;
else {
- pr_debug("backlight: set brightness to %d\n",
- brightness);
- if (bd->props.brightness != brightness) {
- bd->props.brightness = brightness;
- backlight_update_status(bd);
- }
+ pr_debug("set brightness to %lu\n", brightness);
+ bd->props.brightness = brightness;
+ backlight_update_status(bd);
rc = count;
}
}
mutex_unlock(&bd->ops_lock);
+ backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
+
return rc;
}
+static DEVICE_ATTR_RW(brightness);
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct backlight_device *bd = to_backlight_device(dev);
+
+ return sprintf(buf, "%s\n", backlight_types[bd->props.type]);
+}
+static DEVICE_ATTR_RO(type);
-static ssize_t backlight_show_max_brightness(struct device *dev,
+static ssize_t max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct backlight_device *bd = to_backlight_device(dev);
return sprintf(buf, "%d\n", bd->props.max_brightness);
}
+static DEVICE_ATTR_RO(max_brightness);
-static ssize_t backlight_show_actual_brightness(struct device *dev,
+static ssize_t actual_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = -ENXIO;
@@ -167,24 +227,77 @@ static ssize_t backlight_show_actual_brightness(struct device *dev,
return rc;
}
+static DEVICE_ATTR_RO(actual_brightness);
static struct class *backlight_class;
+#ifdef CONFIG_PM_SLEEP
+static int backlight_suspend(struct device *dev)
+{
+ struct backlight_device *bd = to_backlight_device(dev);
+
+ mutex_lock(&bd->ops_lock);
+ if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
+ bd->props.state |= BL_CORE_SUSPENDED;
+ backlight_update_status(bd);
+ }
+ mutex_unlock(&bd->ops_lock);
+
+ return 0;
+}
+
+static int backlight_resume(struct device *dev)
+{
+ struct backlight_device *bd = to_backlight_device(dev);
+
+ mutex_lock(&bd->ops_lock);
+ if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
+ bd->props.state &= ~BL_CORE_SUSPENDED;
+ backlight_update_status(bd);
+ }
+ mutex_unlock(&bd->ops_lock);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,
+ backlight_resume);
+
static void bl_device_release(struct device *dev)
{
struct backlight_device *bd = to_backlight_device(dev);
kfree(bd);
}
-static struct device_attribute bl_device_attributes[] = {
- __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
- __ATTR(brightness, 0644, backlight_show_brightness,
- backlight_store_brightness),
- __ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
- NULL),
- __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
- __ATTR_NULL,
+static struct attribute *bl_device_attrs[] = {
+ &dev_attr_bl_power.attr,
+ &dev_attr_brightness.attr,
+ &dev_attr_actual_brightness.attr,
+ &dev_attr_max_brightness.attr,
+ &dev_attr_type.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(bl_device);
+
+/**
+ * backlight_force_update - tell the backlight subsystem that hardware state
+ * has changed
+ * @bd: the backlight device to update
+ *
+ * Updates the internal state of the backlight in response to a hardware event,
+ * and generate a uevent to notify userspace
+ */
+void backlight_force_update(struct backlight_device *bd,
+ enum backlight_update_reason reason)
+{
+ mutex_lock(&bd->ops_lock);
+ if (bd->ops && bd->ops->get_brightness)
+ bd->props.brightness = bd->ops->get_brightness(bd);
+ mutex_unlock(&bd->ops_lock);
+ backlight_generate_event(bd, reason);
+}
+EXPORT_SYMBOL(backlight_force_update);
/**
* backlight_device_register - create and register a new object of
@@ -200,7 +313,8 @@ static struct device_attribute bl_device_attributes[] = {
* ERR_PTR() or a pointer to the newly allocated device.
*/
struct backlight_device *backlight_device_register(const char *name,
- struct device *parent, void *devdata, struct backlight_ops *ops)
+ struct device *parent, void *devdata, const struct backlight_ops *ops,
+ const struct backlight_properties *props)
{
struct backlight_device *new_bd;
int rc;
@@ -217,12 +331,24 @@ struct backlight_device *backlight_device_register(const char *name,
new_bd->dev.class = backlight_class;
new_bd->dev.parent = parent;
new_bd->dev.release = bl_device_release;
- strlcpy(new_bd->dev.bus_id, name, BUS_ID_SIZE);
+ dev_set_name(&new_bd->dev, "%s", name);
dev_set_drvdata(&new_bd->dev, devdata);
+ /* Set default properties */
+ if (props) {
+ memcpy(&new_bd->props, props,
+ sizeof(struct backlight_properties));
+ if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {
+ WARN(1, "%s: invalid backlight type", name);
+ new_bd->props.type = BACKLIGHT_RAW;
+ }
+ } else {
+ new_bd->props.type = BACKLIGHT_RAW;
+ }
+
rc = device_register(&new_bd->dev);
if (rc) {
- kfree(new_bd);
+ put_device(&new_bd->dev);
return ERR_PTR(rc);
}
@@ -241,10 +367,35 @@ struct backlight_device *backlight_device_register(const char *name,
mutex_unlock(&pmac_backlight_mutex);
#endif
+ mutex_lock(&backlight_dev_list_mutex);
+ list_add(&new_bd->entry, &backlight_dev_list);
+ mutex_unlock(&backlight_dev_list_mutex);
+
+ blocking_notifier_call_chain(&backlight_notifier,
+ BACKLIGHT_REGISTERED, new_bd);
+
return new_bd;
}
EXPORT_SYMBOL(backlight_device_register);
+bool backlight_device_registered(enum backlight_type type)
+{
+ bool found = false;
+ struct backlight_device *bd;
+
+ mutex_lock(&backlight_dev_list_mutex);
+ list_for_each_entry(bd, &backlight_dev_list, entry) {
+ if (bd->props.type == type) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&backlight_dev_list_mutex);
+
+ return found;
+}
+EXPORT_SYMBOL(backlight_device_registered);
+
/**
* backlight_device_unregister - unregisters a backlight device object.
* @bd: the backlight device object to be unregistered and freed.
@@ -256,12 +407,20 @@ void backlight_device_unregister(struct backlight_device *bd)
if (!bd)
return;
+ mutex_lock(&backlight_dev_list_mutex);
+ list_del(&bd->entry);
+ mutex_unlock(&backlight_dev_list_mutex);
+
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
if (pmac_backlight == bd)
pmac_backlight = NULL;
mutex_unlock(&pmac_backlight_mutex);
#endif
+
+ blocking_notifier_call_chain(&backlight_notifier,
+ BACKLIGHT_UNREGISTERED, bd);
+
mutex_lock(&bd->ops_lock);
bd->ops = NULL;
mutex_unlock(&bd->ops_lock);
@@ -271,6 +430,140 @@ void backlight_device_unregister(struct backlight_device *bd)
}
EXPORT_SYMBOL(backlight_device_unregister);
+static void devm_backlight_device_release(struct device *dev, void *res)
+{
+ struct backlight_device *backlight = *(struct backlight_device **)res;
+
+ backlight_device_unregister(backlight);
+}
+
+static int devm_backlight_device_match(struct device *dev, void *res,
+ void *data)
+{
+ struct backlight_device **r = res;
+
+ return *r == data;
+}
+
+/**
+ * backlight_register_notifier - get notified of backlight (un)registration
+ * @nb: notifier block with the notifier to call on backlight (un)registration
+ *
+ * @return 0 on success, otherwise a negative error code
+ *
+ * Register a notifier to get notified when backlight devices get registered
+ * or unregistered.
+ */
+int backlight_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&backlight_notifier, nb);
+}
+EXPORT_SYMBOL(backlight_register_notifier);
+
+/**
+ * backlight_unregister_notifier - unregister a backlight notifier
+ * @nb: notifier block to unregister
+ *
+ * @return 0 on success, otherwise a negative error code
+ *
+ * Register a notifier to get notified when backlight devices get registered
+ * or unregistered.
+ */
+int backlight_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&backlight_notifier, nb);
+}
+EXPORT_SYMBOL(backlight_unregister_notifier);
+
+/**
+ * devm_backlight_device_register - resource managed backlight_device_register()
+ * @dev: the device to register
+ * @name: the name of the device
+ * @parent: a pointer to the parent device
+ * @devdata: an optional pointer to be stored for private driver use
+ * @ops: the backlight operations structure
+ * @props: the backlight properties
+ *
+ * @return a struct backlight on success, or an ERR_PTR on error
+ *
+ * Managed backlight_device_register(). The backlight_device returned
+ * from this function are automatically freed on driver detach.
+ * See backlight_device_register() for more information.
+ */
+struct backlight_device *devm_backlight_device_register(struct device *dev,
+ const char *name, struct device *parent, void *devdata,
+ const struct backlight_ops *ops,
+ const struct backlight_properties *props)
+{
+ struct backlight_device **ptr, *backlight;
+
+ ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ backlight = backlight_device_register(name, parent, devdata, ops,
+ props);
+ if (!IS_ERR(backlight)) {
+ *ptr = backlight;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return backlight;
+}
+EXPORT_SYMBOL(devm_backlight_device_register);
+
+/**
+ * devm_backlight_device_unregister - resource managed backlight_device_unregister()
+ * @dev: the device to unregister
+ * @bd: the backlight device to unregister
+ *
+ * Deallocated a backlight allocated with devm_backlight_device_register().
+ * Normally this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_backlight_device_unregister(struct device *dev,
+ struct backlight_device *bd)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_backlight_device_release,
+ devm_backlight_device_match, bd);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_backlight_device_unregister);
+
+#ifdef CONFIG_OF
+static int of_parent_match(struct device *dev, const void *data)
+{
+ return dev->parent && dev->parent->of_node == data;
+}
+
+/**
+ * of_find_backlight_by_node() - find backlight device by device-tree node
+ * @node: device-tree node of the backlight device
+ *
+ * Returns a pointer to the backlight device corresponding to the given DT
+ * node or NULL if no such backlight device exists or if the device hasn't
+ * been probed yet.
+ *
+ * This function obtains a reference on the backlight device and it is the
+ * caller's responsibility to drop the reference by calling put_device() on
+ * the backlight device's .dev field.
+ */
+struct backlight_device *of_find_backlight_by_node(struct device_node *node)
+{
+ struct device *dev;
+
+ dev = class_find_device(backlight_class, NULL, node, of_parent_match);
+
+ return dev ? to_backlight_device(dev) : NULL;
+}
+EXPORT_SYMBOL(of_find_backlight_by_node);
+#endif
+
static void __exit backlight_class_exit(void)
{
class_destroy(backlight_class);
@@ -280,12 +573,17 @@ static int __init backlight_class_init(void)
{
backlight_class = class_create(THIS_MODULE, "backlight");
if (IS_ERR(backlight_class)) {
- printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
- PTR_ERR(backlight_class));
+ pr_warn("Unable to create backlight class; errno = %ld\n",
+ PTR_ERR(backlight_class));
return PTR_ERR(backlight_class);
}
- backlight_class->dev_attrs = bl_device_attributes;
+ backlight_class->dev_groups = bl_device_groups;
+ backlight_class->pm = &backlight_class_dev_pm_ops;
+ INIT_LIST_HEAD(&backlight_dev_list);
+ mutex_init(&backlight_dev_list_mutex);
+ BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);
+
return 0;
}
diff --git a/drivers/video/backlight/bd6107.c b/drivers/video/backlight/bd6107.c
new file mode 100644
index 00000000000..16dd9bc625b
--- /dev/null
+++ b/drivers/video/backlight/bd6107.c
@@ -0,0 +1,213 @@
+/*
+ * ROHM Semiconductor BD6107 LED Driver
+ *
+ * Copyright (C) 2013 Ideas on board SPRL
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_data/bd6107.h>
+#include <linux/slab.h>
+
+#define BD6107_PSCNT1 0x00
+#define BD6107_PSCNT1_PSCNTREG2 (1 << 2)
+#define BD6107_PSCNT1_PSCNTREG1 (1 << 0)
+#define BD6107_REGVSET 0x02
+#define BD6107_REGVSET_REG1VSET_2_85V (1 << 2)
+#define BD6107_REGVSET_REG1VSET_2_80V (0 << 2)
+#define BD6107_LEDCNT1 0x03
+#define BD6107_LEDCNT1_LEDONOFF2 (1 << 1)
+#define BD6107_LEDCNT1_LEDONOFF1 (1 << 0)
+#define BD6107_PORTSEL 0x04
+#define BD6107_PORTSEL_LEDM(n) (1 << (n))
+#define BD6107_RGB1CNT1 0x05
+#define BD6107_RGB1CNT2 0x06
+#define BD6107_RGB1CNT3 0x07
+#define BD6107_RGB1CNT4 0x08
+#define BD6107_RGB1CNT5 0x09
+#define BD6107_RGB1FLM 0x0a
+#define BD6107_RGB2CNT1 0x0b
+#define BD6107_RGB2CNT2 0x0c
+#define BD6107_RGB2CNT3 0x0d
+#define BD6107_RGB2CNT4 0x0e
+#define BD6107_RGB2CNT5 0x0f
+#define BD6107_RGB2FLM 0x10
+#define BD6107_PSCONT3 0x11
+#define BD6107_SMMONCNT 0x12
+#define BD6107_DCDCCNT 0x13
+#define BD6107_IOSEL 0x14
+#define BD6107_OUT1 0x15
+#define BD6107_OUT2 0x16
+#define BD6107_MASK1 0x17
+#define BD6107_MASK2 0x18
+#define BD6107_FACTOR1 0x19
+#define BD6107_FACTOR2 0x1a
+#define BD6107_CLRFACT1 0x1b
+#define BD6107_CLRFACT2 0x1c
+#define BD6107_STATE1 0x1d
+#define BD6107_LSIVER 0x1e
+#define BD6107_GRPSEL 0x1f
+#define BD6107_LEDCNT2 0x20
+#define BD6107_LEDCNT3 0x21
+#define BD6107_MCURRENT 0x22
+#define BD6107_MAINCNT1 0x23
+#define BD6107_MAINCNT2 0x24
+#define BD6107_SLOPECNT 0x25
+#define BD6107_MSLOPE 0x26
+#define BD6107_RGBSLOPE 0x27
+#define BD6107_TEST 0x29
+#define BD6107_SFTRST 0x2a
+#define BD6107_SFTRSTGD 0x2b
+
+struct bd6107 {
+ struct i2c_client *client;
+ struct backlight_device *backlight;
+ struct bd6107_platform_data *pdata;
+};
+
+static int bd6107_write(struct bd6107 *bd, u8 reg, u8 data)
+{
+ return i2c_smbus_write_byte_data(bd->client, reg, data);
+}
+
+static int bd6107_backlight_update_status(struct backlight_device *backlight)
+{
+ struct bd6107 *bd = bl_get_data(backlight);
+ int brightness = backlight->props.brightness;
+
+ if (backlight->props.power != FB_BLANK_UNBLANK ||
+ backlight->props.fb_blank != FB_BLANK_UNBLANK ||
+ backlight->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+ brightness = 0;
+
+ if (brightness) {
+ bd6107_write(bd, BD6107_PORTSEL, BD6107_PORTSEL_LEDM(2) |
+ BD6107_PORTSEL_LEDM(1) | BD6107_PORTSEL_LEDM(0));
+ bd6107_write(bd, BD6107_MAINCNT1, brightness);
+ bd6107_write(bd, BD6107_LEDCNT1, BD6107_LEDCNT1_LEDONOFF1);
+ } else {
+ gpio_set_value(bd->pdata->reset, 0);
+ msleep(24);
+ gpio_set_value(bd->pdata->reset, 1);
+ }
+
+ return 0;
+}
+
+static int bd6107_backlight_get_brightness(struct backlight_device *backlight)
+{
+ return backlight->props.brightness;
+}
+
+static int bd6107_backlight_check_fb(struct backlight_device *backlight,
+ struct fb_info *info)
+{
+ struct bd6107 *bd = bl_get_data(backlight);
+
+ return bd->pdata->fbdev == NULL || bd->pdata->fbdev == info->dev;
+}
+
+static const struct backlight_ops bd6107_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = bd6107_backlight_update_status,
+ .get_brightness = bd6107_backlight_get_brightness,
+ .check_fb = bd6107_backlight_check_fb,
+};
+
+static int bd6107_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bd6107_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct backlight_device *backlight;
+ struct backlight_properties props;
+ struct bd6107 *bd;
+ int ret;
+
+ if (pdata == NULL || !pdata->reset) {
+ dev_err(&client->dev, "No reset GPIO in platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&client->dev,
+ "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL);
+ if (!bd)
+ return -ENOMEM;
+
+ bd->client = client;
+ bd->pdata = pdata;
+
+ ret = devm_gpio_request_one(&client->dev, pdata->reset,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "reset");
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to request reset GPIO\n");
+ return ret;
+ }
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 128;
+ props.brightness = clamp_t(unsigned int, pdata->def_value, 0,
+ props.max_brightness);
+
+ backlight = devm_backlight_device_register(&client->dev,
+ dev_name(&client->dev),
+ &bd->client->dev, bd,
+ &bd6107_backlight_ops, &props);
+ if (IS_ERR(backlight)) {
+ dev_err(&client->dev, "failed to register backlight\n");
+ return PTR_ERR(backlight);
+ }
+
+ backlight_update_status(backlight);
+ i2c_set_clientdata(client, backlight);
+
+ return 0;
+}
+
+static int bd6107_remove(struct i2c_client *client)
+{
+ struct backlight_device *backlight = i2c_get_clientdata(client);
+
+ backlight->props.brightness = 0;
+ backlight_update_status(backlight);
+
+ return 0;
+}
+
+static const struct i2c_device_id bd6107_ids[] = {
+ { "bd6107", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bd6107_ids);
+
+static struct i2c_driver bd6107_driver = {
+ .driver = {
+ .name = "bd6107",
+ },
+ .probe = bd6107_probe,
+ .remove = bd6107_remove,
+ .id_table = bd6107_ids,
+};
+
+module_i2c_driver(bd6107_driver);
+
+MODULE_DESCRIPTION("Rohm BD6107 Backlight Driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c
deleted file mode 100644
index 4d4d037e3ec..00000000000
--- a/drivers/video/backlight/corgi_bl.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Backlight Driver for Sharp Zaurus Handhelds (various models)
- *
- * Copyright (c) 2004-2006 Richard Purdie
- *
- * Based on Sharp's 2.4 Backlight Driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/fb.h>
-#include <linux/backlight.h>
-
-static int corgibl_intensity;
-static struct backlight_properties corgibl_data;
-static struct backlight_device *corgi_backlight_device;
-static struct generic_bl_info *bl_machinfo;
-
-static unsigned long corgibl_flags;
-#define CORGIBL_SUSPENDED 0x01
-#define CORGIBL_BATTLOW 0x02
-
-static int corgibl_send_intensity(struct backlight_device *bd)
-{
- int intensity = bd->props.brightness;
-
- if (bd->props.power != FB_BLANK_UNBLANK)
- intensity = 0;
- if (bd->props.fb_blank != FB_BLANK_UNBLANK)
- intensity = 0;
- if (corgibl_flags & CORGIBL_SUSPENDED)
- intensity = 0;
- if (corgibl_flags & CORGIBL_BATTLOW)
- intensity &= bl_machinfo->limit_mask;
-
- bl_machinfo->set_bl_intensity(intensity);
-
- corgibl_intensity = intensity;
-
- if (bl_machinfo->kick_battery)
- bl_machinfo->kick_battery();
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int corgibl_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct backlight_device *bd = platform_get_drvdata(pdev);
-
- corgibl_flags |= CORGIBL_SUSPENDED;
- backlight_update_status(bd);
- return 0;
-}
-
-static int corgibl_resume(struct platform_device *pdev)
-{
- struct backlight_device *bd = platform_get_drvdata(pdev);
-
- corgibl_flags &= ~CORGIBL_SUSPENDED;
- backlight_update_status(bd);
- return 0;
-}
-#else
-#define corgibl_suspend NULL
-#define corgibl_resume NULL
-#endif
-
-static int corgibl_get_intensity(struct backlight_device *bd)
-{
- return corgibl_intensity;
-}
-
-/*
- * Called when the battery is low to limit the backlight intensity.
- * If limit==0 clear any limit, otherwise limit the intensity
- */
-void corgibl_limit_intensity(int limit)
-{
- if (limit)
- corgibl_flags |= CORGIBL_BATTLOW;
- else
- corgibl_flags &= ~CORGIBL_BATTLOW;
- backlight_update_status(corgi_backlight_device);
-}
-EXPORT_SYMBOL(corgibl_limit_intensity);
-
-
-static struct backlight_ops corgibl_ops = {
- .get_brightness = corgibl_get_intensity,
- .update_status = corgibl_send_intensity,
-};
-
-static int corgibl_probe(struct platform_device *pdev)
-{
- struct generic_bl_info *machinfo = pdev->dev.platform_data;
- const char *name = "generic-bl";
-
- bl_machinfo = machinfo;
- if (!machinfo->limit_mask)
- machinfo->limit_mask = -1;
-
- if (machinfo->name)
- name = machinfo->name;
-
- corgi_backlight_device = backlight_device_register (name,
- &pdev->dev, NULL, &corgibl_ops);
- if (IS_ERR (corgi_backlight_device))
- return PTR_ERR (corgi_backlight_device);
-
- platform_set_drvdata(pdev, corgi_backlight_device);
-
- corgi_backlight_device->props.max_brightness = machinfo->max_intensity;
- corgi_backlight_device->props.power = FB_BLANK_UNBLANK;
- corgi_backlight_device->props.brightness = machinfo->default_intensity;
- backlight_update_status(corgi_backlight_device);
-
- printk("Corgi Backlight Driver Initialized.\n");
- return 0;
-}
-
-static int corgibl_remove(struct platform_device *pdev)
-{
- struct backlight_device *bd = platform_get_drvdata(pdev);
-
- corgibl_data.power = 0;
- corgibl_data.brightness = 0;
- backlight_update_status(bd);
-
- backlight_device_unregister(bd);
-
- printk("Corgi Backlight Driver Unloaded\n");
- return 0;
-}
-
-static struct platform_driver corgibl_driver = {
- .probe = corgibl_probe,
- .remove = corgibl_remove,
- .suspend = corgibl_suspend,
- .resume = corgibl_resume,
- .driver = {
- .name = "generic-bl",
- },
-};
-
-static int __init corgibl_init(void)
-{
- return platform_driver_register(&corgibl_driver);
-}
-
-static void __exit corgibl_exit(void)
-{
- platform_driver_unregister(&corgibl_driver);
-}
-
-module_init(corgibl_init);
-module_exit(corgibl_exit);
-
-MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
-MODULE_DESCRIPTION("Corgi Backlight Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
new file mode 100644
index 00000000000..51d18d637e2
--- /dev/null
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -0,0 +1,612 @@
+/*
+ * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models)
+ *
+ * Copyright (c) 2004-2006 Richard Purdie
+ *
+ * Based on Sharp's 2.4 Backlight Driver
+ *
+ * Copyright (c) 2008 Marvell International Ltd.
+ * Converted to SPI device based LCD/Backlight device driver
+ * by Eric Miao <eric.miao@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/fb.h>
+#include <linux/lcd.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/corgi_lcd.h>
+#include <linux/slab.h>
+#include <asm/mach/sharpsl_param.h>
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+/* Register Addresses */
+#define RESCTL_ADRS 0x00
+#define PHACTRL_ADRS 0x01
+#define DUTYCTRL_ADRS 0x02
+#define POWERREG0_ADRS 0x03
+#define POWERREG1_ADRS 0x04
+#define GPOR3_ADRS 0x05
+#define PICTRL_ADRS 0x06
+#define POLCTRL_ADRS 0x07
+
+/* Register Bit Definitions */
+#define RESCTL_QVGA 0x01
+#define RESCTL_VGA 0x00
+
+#define POWER1_VW_ON 0x01 /* VW Supply FET ON */
+#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */
+#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */
+
+#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */
+#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */
+#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */
+
+#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */
+#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */
+#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */
+#define POWER0_COM_ON 0x08 /* COM Power Supply ON */
+#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */
+
+#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */
+#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */
+#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */
+
+#define PICTRL_INIT_STATE 0x01
+#define PICTRL_INIOFF 0x02
+#define PICTRL_POWER_DOWN 0x04
+#define PICTRL_COM_SIGNAL_OFF 0x08
+#define PICTRL_DAC_SIGNAL_OFF 0x10
+
+#define POLCTRL_SYNC_POL_FALL 0x01
+#define POLCTRL_EN_POL_FALL 0x02
+#define POLCTRL_DATA_POL_FALL 0x04
+#define POLCTRL_SYNC_ACT_H 0x08
+#define POLCTRL_EN_ACT_L 0x10
+
+#define POLCTRL_SYNC_POL_RISE 0x00
+#define POLCTRL_EN_POL_RISE 0x00
+#define POLCTRL_DATA_POL_RISE 0x00
+#define POLCTRL_SYNC_ACT_L 0x00
+#define POLCTRL_EN_ACT_H 0x00
+
+#define PHACTRL_PHASE_MANUAL 0x01
+#define DEFAULT_PHAD_QVGA (9)
+#define DEFAULT_COMADJ (125)
+
+struct corgi_lcd {
+ struct spi_device *spi_dev;
+ struct lcd_device *lcd_dev;
+ struct backlight_device *bl_dev;
+
+ int limit_mask;
+ int intensity;
+ int power;
+ int mode;
+ char buf[2];
+
+ int gpio_backlight_on;
+ int gpio_backlight_cont;
+ int gpio_backlight_cont_inverted;
+
+ void (*kick_battery)(void);
+};
+
+static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val);
+
+static struct corgi_lcd *the_corgi_lcd;
+static unsigned long corgibl_flags;
+#define CORGIBL_SUSPENDED 0x01
+#define CORGIBL_BATTLOW 0x02
+
+/*
+ * This is only a pseudo I2C interface. We can't use the standard kernel
+ * routines as the interface is write only. We just assume the data is acked...
+ */
+static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data)
+{
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data);
+ udelay(10);
+}
+
+static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data)
+{
+ lcdtg_ssp_i2c_send(lcd, data);
+ lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK);
+ lcdtg_ssp_i2c_send(lcd, data);
+}
+
+static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base)
+{
+ lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
+ lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
+ lcdtg_ssp_i2c_send(lcd, base);
+}
+
+static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base)
+{
+ lcdtg_ssp_i2c_send(lcd, base);
+ lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
+ lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
+}
+
+static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd,
+ uint8_t base, uint8_t data)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ if (data & 0x80)
+ lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
+ else
+ lcdtg_i2c_send_bit(lcd, base);
+ data <<= 1;
+ }
+}
+
+static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base)
+{
+ lcdtg_i2c_send_bit(lcd, base);
+}
+
+static void lcdtg_set_common_voltage(struct corgi_lcd *lcd,
+ uint8_t base_data, uint8_t data)
+{
+ /* Set Common Voltage to M62332FP via I2C */
+ lcdtg_i2c_send_start(lcd, base_data);
+ lcdtg_i2c_send_byte(lcd, base_data, 0x9c);
+ lcdtg_i2c_wait_ack(lcd, base_data);
+ lcdtg_i2c_send_byte(lcd, base_data, 0x00);
+ lcdtg_i2c_wait_ack(lcd, base_data);
+ lcdtg_i2c_send_byte(lcd, base_data, data);
+ lcdtg_i2c_wait_ack(lcd, base_data);
+ lcdtg_i2c_send_stop(lcd, base_data);
+}
+
+static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 1,
+ .cs_change = 1,
+ .tx_buf = lcd->buf,
+ };
+
+ lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ return spi_sync(lcd->spi_dev, &msg);
+}
+
+/* Set Phase Adjust */
+static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode)
+{
+ int adj;
+
+ switch (mode) {
+ case CORGI_LCD_MODE_VGA:
+ /* Setting for VGA */
+ adj = sharpsl_param.phadadj;
+ adj = (adj < 0) ? PHACTRL_PHASE_MANUAL :
+ PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1);
+ break;
+ case CORGI_LCD_MODE_QVGA:
+ default:
+ /* Setting for QVGA */
+ adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL;
+ break;
+ }
+
+ corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj);
+}
+
+static void corgi_lcd_power_on(struct corgi_lcd *lcd)
+{
+ int comadj;
+
+ /* Initialize Internal Logic & Port */
+ corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
+ PICTRL_POWER_DOWN | PICTRL_INIOFF |
+ PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF |
+ PICTRL_DAC_SIGNAL_OFF);
+
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF |
+ POWER0_COM_OFF | POWER0_VCC5_OFF);
+
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
+
+ /* VDD(+8V), SVSS(-4V) ON */
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
+ mdelay(3);
+
+ /* DAC ON */
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
+ POWER0_COM_OFF | POWER0_VCC5_OFF);
+
+ /* INIB = H, INI = L */
+ /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
+ corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
+ PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF);
+
+ /* Set Common Voltage */
+ comadj = sharpsl_param.comadj;
+ if (comadj < 0)
+ comadj = DEFAULT_COMADJ;
+
+ lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
+ POWER0_VCC5_OFF, comadj);
+
+ /* VCC5 ON, DAC ON */
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
+ POWER0_COM_OFF | POWER0_VCC5_ON);
+
+ /* GVSS(-8V) ON, VDD ON */
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
+ mdelay(2);
+
+ /* COM SIGNAL ON (PICTL[3] = L) */
+ corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE);
+
+ /* COM ON, DAC ON, VCC5_ON */
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
+ POWER0_COM_ON | POWER0_VCC5_ON);
+
+ /* VW ON, GVSS ON, VDD ON */
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON);
+
+ /* Signals output enable */
+ corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0);
+
+ /* Set Phase Adjust */
+ lcdtg_set_phadadj(lcd, lcd->mode);
+
+ /* Initialize for Input Signals from ATI */
+ corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS,
+ POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE |
+ POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L |
+ POLCTRL_EN_ACT_H);
+ udelay(1000);
+
+ switch (lcd->mode) {
+ case CORGI_LCD_MODE_VGA:
+ corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
+ break;
+ case CORGI_LCD_MODE_QVGA:
+ default:
+ corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
+ break;
+ }
+}
+
+static void corgi_lcd_power_off(struct corgi_lcd *lcd)
+{
+ /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
+ msleep(34);
+
+ /* (1)VW OFF */
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
+
+ /* (2)COM OFF */
+ corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
+
+ /* (3)Set Common Voltage Bias 0V */
+ lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
+ POWER0_VCC5_ON, 0);
+
+ /* (4)GVSS OFF */
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
+
+ /* (5)VCC5 OFF */
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
+
+ /* (6)Set PDWN, INIOFF, DACOFF */
+ corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
+ PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
+ PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
+
+ /* (7)DAC OFF */
+ corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
+ POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
+
+ /* (8)VDD OFF */
+ corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
+ POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
+}
+
+static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
+{
+ struct corgi_lcd *lcd = lcd_get_data(ld);
+ int mode = CORGI_LCD_MODE_QVGA;
+
+ if (m->xres == 640 || m->xres == 480)
+ mode = CORGI_LCD_MODE_VGA;
+
+ if (lcd->mode == mode)
+ return 0;
+
+ lcdtg_set_phadadj(lcd, mode);
+
+ switch (mode) {
+ case CORGI_LCD_MODE_VGA:
+ corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
+ break;
+ case CORGI_LCD_MODE_QVGA:
+ default:
+ corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
+ break;
+ }
+
+ lcd->mode = mode;
+ return 0;
+}
+
+static int corgi_lcd_set_power(struct lcd_device *ld, int power)
+{
+ struct corgi_lcd *lcd = lcd_get_data(ld);
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
+ corgi_lcd_power_on(lcd);
+
+ if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
+ corgi_lcd_power_off(lcd);
+
+ lcd->power = power;
+ return 0;
+}
+
+static int corgi_lcd_get_power(struct lcd_device *ld)
+{
+ struct corgi_lcd *lcd = lcd_get_data(ld);
+
+ return lcd->power;
+}
+
+static struct lcd_ops corgi_lcd_ops = {
+ .get_power = corgi_lcd_get_power,
+ .set_power = corgi_lcd_set_power,
+ .set_mode = corgi_lcd_set_mode,
+};
+
+static int corgi_bl_get_intensity(struct backlight_device *bd)
+{
+ struct corgi_lcd *lcd = bl_get_data(bd);
+
+ return lcd->intensity;
+}
+
+static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
+{
+ int cont;
+
+ if (intensity > 0x10)
+ intensity += 0x10;
+
+ corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity);
+
+ /* Bit 5 via GPIO_BACKLIGHT_CONT */
+ cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted;
+
+ if (gpio_is_valid(lcd->gpio_backlight_cont))
+ gpio_set_value_cansleep(lcd->gpio_backlight_cont, cont);
+
+ if (gpio_is_valid(lcd->gpio_backlight_on))
+ gpio_set_value_cansleep(lcd->gpio_backlight_on, intensity);
+
+ if (lcd->kick_battery)
+ lcd->kick_battery();
+
+ lcd->intensity = intensity;
+ return 0;
+}
+
+static int corgi_bl_update_status(struct backlight_device *bd)
+{
+ struct corgi_lcd *lcd = bl_get_data(bd);
+ int intensity = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+
+ if (corgibl_flags & CORGIBL_SUSPENDED)
+ intensity = 0;
+
+ if ((corgibl_flags & CORGIBL_BATTLOW) && intensity > lcd->limit_mask)
+ intensity = lcd->limit_mask;
+
+ return corgi_bl_set_intensity(lcd, intensity);
+}
+
+void corgi_lcd_limit_intensity(int limit)
+{
+ if (limit)
+ corgibl_flags |= CORGIBL_BATTLOW;
+ else
+ corgibl_flags &= ~CORGIBL_BATTLOW;
+
+ backlight_update_status(the_corgi_lcd->bl_dev);
+}
+EXPORT_SYMBOL(corgi_lcd_limit_intensity);
+
+static const struct backlight_ops corgi_bl_ops = {
+ .get_brightness = corgi_bl_get_intensity,
+ .update_status = corgi_bl_update_status,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int corgi_lcd_suspend(struct device *dev)
+{
+ struct corgi_lcd *lcd = dev_get_drvdata(dev);
+
+ corgibl_flags |= CORGIBL_SUSPENDED;
+ corgi_bl_set_intensity(lcd, 0);
+ corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+static int corgi_lcd_resume(struct device *dev)
+{
+ struct corgi_lcd *lcd = dev_get_drvdata(dev);
+
+ corgibl_flags &= ~CORGIBL_SUSPENDED;
+ corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
+ backlight_update_status(lcd->bl_dev);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(corgi_lcd_pm_ops, corgi_lcd_suspend, corgi_lcd_resume);
+
+static int setup_gpio_backlight(struct corgi_lcd *lcd,
+ struct corgi_lcd_platform_data *pdata)
+{
+ struct spi_device *spi = lcd->spi_dev;
+ int err;
+
+ lcd->gpio_backlight_on = -1;
+ lcd->gpio_backlight_cont = -1;
+
+ if (gpio_is_valid(pdata->gpio_backlight_on)) {
+ err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_on,
+ "BL_ON");
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to request GPIO%d for backlight_on\n",
+ pdata->gpio_backlight_on);
+ return err;
+ }
+
+ lcd->gpio_backlight_on = pdata->gpio_backlight_on;
+ gpio_direction_output(lcd->gpio_backlight_on, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_backlight_cont)) {
+ err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_cont,
+ "BL_CONT");
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to request GPIO%d for backlight_cont\n",
+ pdata->gpio_backlight_cont);
+ return err;
+ }
+
+ lcd->gpio_backlight_cont = pdata->gpio_backlight_cont;
+
+ /* spitz and akita use both GPIOs for backlight, and
+ * have inverted polarity of GPIO_BACKLIGHT_CONT
+ */
+ if (gpio_is_valid(lcd->gpio_backlight_on)) {
+ lcd->gpio_backlight_cont_inverted = 1;
+ gpio_direction_output(lcd->gpio_backlight_cont, 1);
+ } else {
+ lcd->gpio_backlight_cont_inverted = 0;
+ gpio_direction_output(lcd->gpio_backlight_cont, 0);
+ }
+ }
+ return 0;
+}
+
+static int corgi_lcd_probe(struct spi_device *spi)
+{
+ struct backlight_properties props;
+ struct corgi_lcd_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct corgi_lcd *lcd;
+ int ret = 0;
+
+ if (pdata == NULL) {
+ dev_err(&spi->dev, "platform data not available\n");
+ return -EINVAL;
+ }
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(struct corgi_lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ lcd->spi_dev = spi;
+
+ lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "corgi_lcd",
+ &spi->dev, lcd, &corgi_lcd_ops);
+ if (IS_ERR(lcd->lcd_dev))
+ return PTR_ERR(lcd->lcd_dev);
+
+ lcd->power = FB_BLANK_POWERDOWN;
+ lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = pdata->max_intensity;
+ lcd->bl_dev = devm_backlight_device_register(&spi->dev, "corgi_bl",
+ &spi->dev, lcd, &corgi_bl_ops,
+ &props);
+ if (IS_ERR(lcd->bl_dev))
+ return PTR_ERR(lcd->bl_dev);
+
+ lcd->bl_dev->props.brightness = pdata->default_intensity;
+ lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
+
+ ret = setup_gpio_backlight(lcd, pdata);
+ if (ret)
+ return ret;
+
+ lcd->kick_battery = pdata->kick_battery;
+
+ spi_set_drvdata(spi, lcd);
+ corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
+ backlight_update_status(lcd->bl_dev);
+
+ lcd->limit_mask = pdata->limit_mask;
+ the_corgi_lcd = lcd;
+ return 0;
+}
+
+static int corgi_lcd_remove(struct spi_device *spi)
+{
+ struct corgi_lcd *lcd = spi_get_drvdata(spi);
+
+ lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
+ lcd->bl_dev->props.brightness = 0;
+ backlight_update_status(lcd->bl_dev);
+ corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+static struct spi_driver corgi_lcd_driver = {
+ .driver = {
+ .name = "corgi-lcd",
+ .owner = THIS_MODULE,
+ .pm = &corgi_lcd_pm_ops,
+ },
+ .probe = corgi_lcd_probe,
+ .remove = corgi_lcd_remove,
+};
+
+module_spi_driver(corgi_lcd_driver);
+
+MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:corgi-lcd");
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c
index 26add889860..f3fed9ef745 100644
--- a/drivers/video/backlight/cr_bllcd.c
+++ b/drivers/video/backlight/cr_bllcd.c
@@ -27,6 +27,8 @@
* Alan Hourihane <alanh-at-tungstengraphics-dot-com>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -36,6 +38,7 @@
#include <linux/backlight.h>
#include <linux/lcd.h>
#include <linux/pci.h>
+#include <linux/slab.h>
/* The LVDS- and panel power controls sits on the
* GPIO port of the ISA bridge.
@@ -108,7 +111,7 @@ static int cr_backlight_get_intensity(struct backlight_device *bd)
return intensity;
}
-static struct backlight_ops cr_backlight_ops = {
+static const struct backlight_ops cr_backlight_ops = {
.get_brightness = cr_backlight_get_intensity,
.update_status = cr_backlight_set_intensity,
};
@@ -170,6 +173,7 @@ static struct lcd_ops cr_lcd_ops = {
static int cr_backlight_probe(struct platform_device *pdev)
{
+ struct backlight_properties props;
struct backlight_device *bdp;
struct lcd_device *ldp;
struct cr_panel *crp;
@@ -178,40 +182,40 @@ static int cr_backlight_probe(struct platform_device *pdev)
lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
CRVML_DEVICE_LPC, NULL);
if (!lpc_dev) {
- printk("INTEL CARILLO RANCH LPC not found.\n");
+ pr_err("INTEL CARILLO RANCH LPC not found.\n");
return -ENODEV;
}
pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en);
if (!(dev_en & CRVML_GPIOEN_BIT)) {
- printk(KERN_ERR
- "Carillo Ranch GPIO device was not enabled.\n");
+ pr_err("Carillo Ranch GPIO device was not enabled.\n");
pci_dev_put(lpc_dev);
return -ENODEV;
}
- bdp = backlight_device_register("cr-backlight",
- &pdev->dev, NULL, &cr_backlight_ops);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ bdp = devm_backlight_device_register(&pdev->dev, "cr-backlight",
+ &pdev->dev, NULL, &cr_backlight_ops,
+ &props);
if (IS_ERR(bdp)) {
pci_dev_put(lpc_dev);
return PTR_ERR(bdp);
}
- ldp = lcd_device_register("cr-lcd", &pdev->dev, NULL, &cr_lcd_ops);
+ ldp = devm_lcd_device_register(&pdev->dev, "cr-lcd", &pdev->dev, NULL,
+ &cr_lcd_ops);
if (IS_ERR(ldp)) {
- backlight_device_unregister(bdp);
pci_dev_put(lpc_dev);
- return PTR_ERR(bdp);
+ return PTR_ERR(ldp);
}
pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
&gpio_bar);
gpio_bar &= ~0x3F;
- crp = kzalloc(sizeof(*crp), GFP_KERNEL);
+ crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL);
if (!crp) {
- lcd_device_unregister(ldp);
- backlight_device_unregister(bdp);
pci_dev_put(lpc_dev);
return -ENOMEM;
}
@@ -220,9 +224,7 @@ static int cr_backlight_probe(struct platform_device *pdev)
crp->cr_lcd_device = ldp;
crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK;
crp->cr_backlight_device->props.brightness = 0;
- crp->cr_backlight_device->props.max_brightness = 0;
cr_backlight_set_intensity(crp->cr_backlight_device);
-
cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK);
platform_set_drvdata(pdev, crp);
@@ -238,8 +240,6 @@ static int cr_backlight_remove(struct platform_device *pdev)
crp->cr_backlight_device->props.max_brightness = 0;
cr_backlight_set_intensity(crp->cr_backlight_device);
cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN);
- backlight_device_unregister(crp->cr_backlight_device);
- lcd_device_unregister(crp->cr_lcd_device);
pci_dev_put(lpc_dev);
return 0;
@@ -259,22 +259,18 @@ static int __init cr_backlight_init(void)
{
int ret = platform_driver_register(&cr_backlight_driver);
- if (!ret) {
- crp = platform_device_alloc("cr_backlight", -1);
- if (!crp)
- return -ENOMEM;
+ if (ret)
+ return ret;
- ret = platform_device_add(crp);
-
- if (ret) {
- platform_device_put(crp);
- platform_driver_unregister(&cr_backlight_driver);
- }
+ crp = platform_device_register_simple("cr_backlight", -1, NULL, 0);
+ if (IS_ERR(crp)) {
+ platform_driver_unregister(&cr_backlight_driver);
+ return PTR_ERR(crp);
}
- printk("Carillo Ranch Backlight Driver Initialized.\n");
+ pr_info("Carillo Ranch Backlight Driver Initialized.\n");
- return ret;
+ return 0;
}
static void __exit cr_backlight_exit(void)
diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c
new file mode 100644
index 00000000000..12c5d840c59
--- /dev/null
+++ b/drivers/video/backlight/da903x_bl.c
@@ -0,0 +1,176 @@
+/*
+ * Backlight driver for Dialog Semiconductor DA9030/DA9034
+ *
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/mfd/da903x.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DA9030_WLED_CONTROL 0x25
+#define DA9030_WLED_CP_EN (1 << 6)
+#define DA9030_WLED_TRIM(x) ((x) & 0x7)
+
+#define DA9034_WLED_CONTROL1 0x3C
+#define DA9034_WLED_CONTROL2 0x3D
+#define DA9034_WLED_ISET(x) ((x) & 0x1f)
+
+#define DA9034_WLED_BOOST_EN (1 << 5)
+
+#define DA9030_MAX_BRIGHTNESS 7
+#define DA9034_MAX_BRIGHTNESS 0x7f
+
+struct da903x_backlight_data {
+ struct device *da903x_dev;
+ int id;
+ int current_brightness;
+};
+
+static int da903x_backlight_set(struct backlight_device *bl, int brightness)
+{
+ struct da903x_backlight_data *data = bl_get_data(bl);
+ struct device *dev = data->da903x_dev;
+ uint8_t val;
+ int ret = 0;
+
+ switch (data->id) {
+ case DA9034_ID_WLED:
+ ret = da903x_update(dev, DA9034_WLED_CONTROL1,
+ brightness, 0x7f);
+ if (ret)
+ return ret;
+
+ if (data->current_brightness && brightness == 0)
+ ret = da903x_clr_bits(dev,
+ DA9034_WLED_CONTROL2,
+ DA9034_WLED_BOOST_EN);
+
+ if (data->current_brightness == 0 && brightness)
+ ret = da903x_set_bits(dev,
+ DA9034_WLED_CONTROL2,
+ DA9034_WLED_BOOST_EN);
+ break;
+ case DA9030_ID_WLED:
+ val = DA9030_WLED_TRIM(brightness);
+ val |= brightness ? DA9030_WLED_CP_EN : 0;
+ ret = da903x_write(dev, DA9030_WLED_CONTROL, val);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ data->current_brightness = brightness;
+ return 0;
+}
+
+static int da903x_backlight_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;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ return da903x_backlight_set(bl, brightness);
+}
+
+static int da903x_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct da903x_backlight_data *data = bl_get_data(bl);
+
+ return data->current_brightness;
+}
+
+static const struct backlight_ops da903x_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = da903x_backlight_update_status,
+ .get_brightness = da903x_backlight_get_brightness,
+};
+
+static int da903x_backlight_probe(struct platform_device *pdev)
+{
+ struct da9034_backlight_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct da903x_backlight_data *data;
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ int max_brightness;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ switch (pdev->id) {
+ case DA9030_ID_WLED:
+ max_brightness = DA9030_MAX_BRIGHTNESS;
+ break;
+ case DA9034_ID_WLED:
+ max_brightness = DA9034_MAX_BRIGHTNESS;
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid backlight device ID(%d)\n",
+ pdev->id);
+ return -EINVAL;
+ }
+
+ data->id = pdev->id;
+ data->da903x_dev = pdev->dev.parent;
+ data->current_brightness = 0;
+
+ /* adjust the WLED output current */
+ if (pdata)
+ da903x_write(data->da903x_dev, DA9034_WLED_CONTROL2,
+ DA9034_WLED_ISET(pdata->output_current));
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = max_brightness;
+ bl = devm_backlight_device_register(&pdev->dev, pdev->name,
+ data->da903x_dev, data,
+ &da903x_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ bl->props.brightness = max_brightness;
+
+ platform_set_drvdata(pdev, bl);
+ backlight_update_status(bl);
+ return 0;
+}
+
+static struct platform_driver da903x_backlight_driver = {
+ .driver = {
+ .name = "da903x-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = da903x_backlight_probe,
+};
+
+module_platform_driver(da903x_backlight_driver);
+
+MODULE_DESCRIPTION("Backlight Driver for Dialog Semiconductor DA9030/DA9034");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da903x-backlight");
diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c
new file mode 100644
index 00000000000..20d55becaa7
--- /dev/null
+++ b/drivers/video/backlight/da9052_bl.c
@@ -0,0 +1,185 @@
+/*
+ * Backlight Driver for Dialog DA9052 PMICs
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+#define DA9052_MAX_BRIGHTNESS 0xFF
+
+enum {
+ DA9052_WLEDS_OFF,
+ DA9052_WLEDS_ON,
+};
+
+enum {
+ DA9052_TYPE_WLED1,
+ DA9052_TYPE_WLED2,
+ DA9052_TYPE_WLED3,
+};
+
+static const unsigned char wled_bank[] = {
+ DA9052_LED1_CONF_REG,
+ DA9052_LED2_CONF_REG,
+ DA9052_LED3_CONF_REG,
+};
+
+struct da9052_bl {
+ struct da9052 *da9052;
+ uint brightness;
+ uint state;
+ uint led_reg;
+};
+
+static int da9052_adjust_wled_brightness(struct da9052_bl *wleds)
+{
+ unsigned char boost_en;
+ unsigned char i_sink;
+ int ret;
+
+ boost_en = 0x3F;
+ i_sink = 0xFF;
+ if (wleds->state == DA9052_WLEDS_OFF) {
+ boost_en = 0x00;
+ i_sink = 0x00;
+ }
+
+ ret = da9052_reg_write(wleds->da9052, DA9052_BOOST_REG, boost_en);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_reg_write(wleds->da9052, DA9052_LED_CONT_REG, i_sink);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg], 0x0);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(10000, 11000);
+
+ if (wleds->brightness) {
+ ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg],
+ wleds->brightness);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int da9052_backlight_update_status(struct backlight_device *bl)
+{
+ int brightness = bl->props.brightness;
+ struct da9052_bl *wleds = bl_get_data(bl);
+
+ wleds->brightness = brightness;
+ wleds->state = DA9052_WLEDS_ON;
+
+ return da9052_adjust_wled_brightness(wleds);
+}
+
+static int da9052_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct da9052_bl *wleds = bl_get_data(bl);
+
+ return wleds->brightness;
+}
+
+static const struct backlight_ops da9052_backlight_ops = {
+ .update_status = da9052_backlight_update_status,
+ .get_brightness = da9052_backlight_get_brightness,
+};
+
+static int da9052_backlight_probe(struct platform_device *pdev)
+{
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ struct da9052_bl *wleds;
+
+ wleds = devm_kzalloc(&pdev->dev, sizeof(struct da9052_bl), GFP_KERNEL);
+ if (!wleds)
+ return -ENOMEM;
+
+ wleds->da9052 = dev_get_drvdata(pdev->dev.parent);
+ wleds->brightness = 0;
+ wleds->led_reg = platform_get_device_id(pdev)->driver_data;
+ wleds->state = DA9052_WLEDS_OFF;
+
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = DA9052_MAX_BRIGHTNESS;
+
+ bl = devm_backlight_device_register(&pdev->dev, pdev->name,
+ wleds->da9052->dev, wleds,
+ &da9052_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "Failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ bl->props.max_brightness = DA9052_MAX_BRIGHTNESS;
+ bl->props.brightness = 0;
+ platform_set_drvdata(pdev, bl);
+
+ return da9052_adjust_wled_brightness(wleds);
+}
+
+static int da9052_backlight_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct da9052_bl *wleds = bl_get_data(bl);
+
+ wleds->brightness = 0;
+ wleds->state = DA9052_WLEDS_OFF;
+ da9052_adjust_wled_brightness(wleds);
+
+ return 0;
+}
+
+static struct platform_device_id da9052_wled_ids[] = {
+ {
+ .name = "da9052-wled1",
+ .driver_data = DA9052_TYPE_WLED1,
+ },
+ {
+ .name = "da9052-wled2",
+ .driver_data = DA9052_TYPE_WLED2,
+ },
+ {
+ .name = "da9052-wled3",
+ .driver_data = DA9052_TYPE_WLED3,
+ },
+};
+
+static struct platform_driver da9052_wled_driver = {
+ .probe = da9052_backlight_probe,
+ .remove = da9052_backlight_remove,
+ .id_table = da9052_wled_ids,
+ .driver = {
+ .name = "da9052-wled",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(da9052_wled_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Backlight driver for DA9052 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-backlight");
diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c
new file mode 100644
index 00000000000..0d1f633c648
--- /dev/null
+++ b/drivers/video/backlight/ep93xx_bl.c
@@ -0,0 +1,142 @@
+/*
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#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);
+
+ 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 ep93xxbl_probe(struct platform_device *dev)
+{
+ struct ep93xxbl *ep93xxbl;
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ struct resource *res;
+
+ ep93xxbl = devm_kzalloc(&dev->dev, sizeof(*ep93xxbl), GFP_KERNEL);
+ if (!ep93xxbl)
+ return -ENOMEM;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ /*
+ * FIXME - We don't do a request_mem_region here because we are
+ * sharing the register space with the framebuffer driver (see
+ * drivers/video/ep93xx-fb.c) and doing so will cause the second
+ * loaded driver to return -EBUSY.
+ *
+ * NOTE: No locking is required; the framebuffer does not touch
+ * this register.
+ */
+ ep93xxbl->mmio = devm_ioremap(&dev->dev, res->start,
+ resource_size(res));
+ if (!ep93xxbl->mmio)
+ return -ENXIO;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = EP93XX_MAX_BRIGHT;
+ bl = devm_backlight_device_register(&dev->dev, 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;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ep93xxbl_suspend(struct device *dev)
+{
+ struct backlight_device *bl = dev_get_drvdata(dev);
+
+ return ep93xxbl_set(bl, 0);
+}
+
+static int ep93xxbl_resume(struct device *dev)
+{
+ struct backlight_device *bl = dev_get_drvdata(dev);
+
+ backlight_update_status(bl);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ep93xxbl_pm_ops, ep93xxbl_suspend, ep93xxbl_resume);
+
+static struct platform_driver ep93xxbl_driver = {
+ .driver = {
+ .name = "ep93xx-bl",
+ .owner = THIS_MODULE,
+ .pm = &ep93xxbl_pm_ops,
+ },
+ .probe = ep93xxbl_probe,
+};
+
+module_platform_driver(ep93xxbl_driver);
+
+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/generic_bl.c b/drivers/video/backlight/generic_bl.c
new file mode 100644
index 00000000000..5d8d65200db
--- /dev/null
+++ b/drivers/video/backlight/generic_bl.c
@@ -0,0 +1,137 @@
+/*
+ * Generic Backlight Driver
+ *
+ * Copyright (c) 2004-2008 Richard Purdie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+static int genericbl_intensity;
+static struct backlight_device *generic_backlight_device;
+static struct generic_bl_info *bl_machinfo;
+
+/* Flag to signal when the battery is low */
+#define GENERICBL_BATTLOW BL_CORE_DRIVER1
+
+static int genericbl_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.state & BL_CORE_FBBLANK)
+ intensity = 0;
+ if (bd->props.state & BL_CORE_SUSPENDED)
+ intensity = 0;
+ if (bd->props.state & GENERICBL_BATTLOW)
+ intensity &= bl_machinfo->limit_mask;
+
+ bl_machinfo->set_bl_intensity(intensity);
+
+ genericbl_intensity = intensity;
+
+ if (bl_machinfo->kick_battery)
+ bl_machinfo->kick_battery();
+
+ return 0;
+}
+
+static int genericbl_get_intensity(struct backlight_device *bd)
+{
+ return genericbl_intensity;
+}
+
+/*
+ * Called when the battery is low to limit the backlight intensity.
+ * If limit==0 clear any limit, otherwise limit the intensity
+ */
+void genericbl_limit_intensity(int limit)
+{
+ struct backlight_device *bd = generic_backlight_device;
+
+ mutex_lock(&bd->ops_lock);
+ if (limit)
+ bd->props.state |= GENERICBL_BATTLOW;
+ else
+ bd->props.state &= ~GENERICBL_BATTLOW;
+ backlight_update_status(generic_backlight_device);
+ mutex_unlock(&bd->ops_lock);
+}
+EXPORT_SYMBOL(genericbl_limit_intensity);
+
+static const struct backlight_ops genericbl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = genericbl_get_intensity,
+ .update_status = genericbl_send_intensity,
+};
+
+static int genericbl_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ struct generic_bl_info *machinfo = dev_get_platdata(&pdev->dev);
+ const char *name = "generic-bl";
+ struct backlight_device *bd;
+
+ bl_machinfo = machinfo;
+ if (!machinfo->limit_mask)
+ machinfo->limit_mask = -1;
+
+ if (machinfo->name)
+ name = machinfo->name;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = machinfo->max_intensity;
+ bd = devm_backlight_device_register(&pdev->dev, name, &pdev->dev,
+ NULL, &genericbl_ops, &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ platform_set_drvdata(pdev, bd);
+
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.brightness = machinfo->default_intensity;
+ backlight_update_status(bd);
+
+ generic_backlight_device = bd;
+
+ dev_info(&pdev->dev, "Generic Backlight Driver Initialized.\n");
+ return 0;
+}
+
+static int genericbl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+
+ bd->props.power = 0;
+ bd->props.brightness = 0;
+ backlight_update_status(bd);
+
+ dev_info(&pdev->dev, "Generic Backlight Driver Unloaded\n");
+ return 0;
+}
+
+static struct platform_driver genericbl_driver = {
+ .probe = genericbl_probe,
+ .remove = genericbl_remove,
+ .driver = {
+ .name = "generic-bl",
+ },
+};
+
+module_platform_driver(genericbl_driver);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Generic Backlight Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c
new file mode 100644
index 00000000000..1cea68848f1
--- /dev/null
+++ b/drivers/video/backlight/gpio_backlight.c
@@ -0,0 +1,171 @@
+/*
+ * gpio_backlight.c - Simple GPIO-controlled backlight
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/gpio_backlight.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct gpio_backlight {
+ struct device *dev;
+ struct device *fbdev;
+
+ int gpio;
+ int active;
+ int def_value;
+};
+
+static int gpio_backlight_update_status(struct backlight_device *bl)
+{
+ struct gpio_backlight *gbl = bl_get_data(bl);
+ int brightness = bl->props.brightness;
+
+ if (bl->props.power != FB_BLANK_UNBLANK ||
+ bl->props.fb_blank != FB_BLANK_UNBLANK ||
+ bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+ brightness = 0;
+
+ gpio_set_value_cansleep(gbl->gpio,
+ brightness ? gbl->active : !gbl->active);
+
+ return 0;
+}
+
+static int gpio_backlight_get_brightness(struct backlight_device *bl)
+{
+ return bl->props.brightness;
+}
+
+static int gpio_backlight_check_fb(struct backlight_device *bl,
+ struct fb_info *info)
+{
+ struct gpio_backlight *gbl = bl_get_data(bl);
+
+ return gbl->fbdev == NULL || gbl->fbdev == info->dev;
+}
+
+static const struct backlight_ops gpio_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = gpio_backlight_update_status,
+ .get_brightness = gpio_backlight_get_brightness,
+ .check_fb = gpio_backlight_check_fb,
+};
+
+static int gpio_backlight_probe_dt(struct platform_device *pdev,
+ struct gpio_backlight *gbl)
+{
+ struct device_node *np = pdev->dev.of_node;
+ enum of_gpio_flags gpio_flags;
+
+ gbl->gpio = of_get_gpio_flags(np, 0, &gpio_flags);
+
+ if (!gpio_is_valid(gbl->gpio)) {
+ if (gbl->gpio != -EPROBE_DEFER) {
+ dev_err(&pdev->dev,
+ "Error: The gpios parameter is missing or invalid.\n");
+ }
+ return gbl->gpio;
+ }
+
+ gbl->active = (gpio_flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ gbl->def_value = of_property_read_bool(np, "default-on");
+
+ return 0;
+}
+
+static int gpio_backlight_probe(struct platform_device *pdev)
+{
+ struct gpio_backlight_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ struct gpio_backlight *gbl;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!pdata && !np) {
+ dev_err(&pdev->dev,
+ "failed to find platform data or device tree node.\n");
+ return -ENODEV;
+ }
+
+ gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL);
+ if (gbl == NULL)
+ return -ENOMEM;
+
+ gbl->dev = &pdev->dev;
+
+ if (np) {
+ ret = gpio_backlight_probe_dt(pdev, gbl);
+ if (ret)
+ return ret;
+ } else {
+ gbl->fbdev = pdata->fbdev;
+ gbl->gpio = pdata->gpio;
+ gbl->active = pdata->active_low ? 0 : 1;
+ gbl->def_value = pdata->def_value;
+ }
+
+ ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT |
+ (gbl->active ? GPIOF_INIT_LOW
+ : GPIOF_INIT_HIGH),
+ pdata ? pdata->name : "backlight");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to request GPIO\n");
+ return ret;
+ }
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 1;
+ bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev),
+ &pdev->dev, gbl, &gpio_backlight_ops,
+ &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ bl->props.brightness = gbl->def_value;
+ backlight_update_status(bl);
+
+ platform_set_drvdata(pdev, bl);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpio_backlight_of_match[] = {
+ { .compatible = "gpio-backlight" },
+ { /* sentinel */ }
+};
+#endif
+
+static struct platform_driver gpio_backlight_driver = {
+ .driver = {
+ .name = "gpio-backlight",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gpio_backlight_of_match),
+ },
+ .probe = gpio_backlight_probe,
+};
+
+module_platform_driver(gpio_backlight_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("GPIO-based Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-backlight");
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index fbea2bd129c..8ea42b8d9bc 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -18,15 +18,15 @@
#include <linux/fb.h>
#include <linux/backlight.h>
-#include <asm/cpu/dac.h>
-#include <asm/hp6xx.h>
+#include <cpu/dac.h>
+#include <mach/hp6xx.h>
#include <asm/hd64461.h>
#define HP680_MAX_INTENSITY 255
#define HP680_DEFAULT_INTENSITY 10
static int hp680bl_suspended;
-static int current_intensity = 0;
+static int current_intensity;
static DEFINE_SPINLOCK(bl_lock);
static void hp680bl_send_intensity(struct backlight_device *bd)
@@ -64,29 +64,28 @@ static void hp680bl_send_intensity(struct backlight_device *bd)
}
-#ifdef CONFIG_PM
-static int hp680bl_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int hp680bl_suspend(struct device *dev)
{
- struct backlight_device *bd = platform_get_drvdata(pdev);
+ struct backlight_device *bd = dev_get_drvdata(dev);
hp680bl_suspended = 1;
hp680bl_send_intensity(bd);
return 0;
}
-static int hp680bl_resume(struct platform_device *pdev)
+static int hp680bl_resume(struct device *dev)
{
- struct backlight_device *bd = platform_get_drvdata(pdev);
+ struct backlight_device *bd = dev_get_drvdata(dev);
hp680bl_suspended = 0;
hp680bl_send_intensity(bd);
return 0;
}
-#else
-#define hp680bl_suspend NULL
-#define hp680bl_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(hp680bl_pm_ops, hp680bl_suspend, hp680bl_resume);
+
static int hp680bl_set_intensity(struct backlight_device *bd)
{
hp680bl_send_intensity(bd);
@@ -98,23 +97,26 @@ static int hp680bl_get_intensity(struct backlight_device *bd)
return current_intensity;
}
-static struct backlight_ops hp680bl_ops = {
+static const struct backlight_ops hp680bl_ops = {
.get_brightness = hp680bl_get_intensity,
.update_status = hp680bl_set_intensity,
};
-static int __init hp680bl_probe(struct platform_device *pdev)
+static int hp680bl_probe(struct platform_device *pdev)
{
+ struct backlight_properties props;
struct backlight_device *bd;
- bd = backlight_device_register ("hp680-bl", &pdev->dev, NULL,
- &hp680bl_ops);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = HP680_MAX_INTENSITY;
+ bd = devm_backlight_device_register(&pdev->dev, "hp680-bl", &pdev->dev,
+ NULL, &hp680bl_ops, &props);
if (IS_ERR(bd))
return PTR_ERR(bd);
platform_set_drvdata(pdev, bd);
- bd->props.max_brightness = HP680_MAX_INTENSITY;
bd->props.brightness = HP680_DEFAULT_INTENSITY;
hp680bl_send_intensity(bd);
@@ -129,18 +131,15 @@ static int hp680bl_remove(struct platform_device *pdev)
bd->props.power = 0;
hp680bl_send_intensity(bd);
- backlight_device_unregister(bd);
-
return 0;
}
static struct platform_driver hp680bl_driver = {
.probe = hp680bl_probe,
.remove = hp680bl_remove,
- .suspend = hp680bl_suspend,
- .resume = hp680bl_resume,
.driver = {
.name = "hp680-bl",
+ .pm = &hp680bl_pm_ops,
},
};
@@ -151,25 +150,21 @@ static int __init hp680bl_init(void)
int ret;
ret = platform_driver_register(&hp680bl_driver);
- if (!ret) {
- hp680bl_device = platform_device_alloc("hp680-bl", -1);
- if (!hp680bl_device)
- return -ENOMEM;
-
- ret = platform_device_add(hp680bl_device);
-
- if (ret) {
- platform_device_put(hp680bl_device);
- platform_driver_unregister(&hp680bl_driver);
- }
+ if (ret)
+ return ret;
+ hp680bl_device = platform_device_register_simple("hp680-bl", -1,
+ NULL, 0);
+ if (IS_ERR(hp680bl_device)) {
+ platform_driver_unregister(&hp680bl_driver);
+ return PTR_ERR(hp680bl_device);
}
- return ret;
+ return 0;
}
static void __exit hp680bl_exit(void)
{
platform_device_unregister(hp680bl_device);
- platform_driver_unregister(&hp680bl_driver);
+ platform_driver_unregister(&hp680bl_driver);
}
module_init(hp680bl_init);
diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c
new file mode 100644
index 00000000000..23f50b92a93
--- /dev/null
+++ b/drivers/video/backlight/hx8357.c
@@ -0,0 +1,682 @@
+/*
+ * Driver for the Himax HX-8357 LCD Controller
+ *
+ * Copyright 2012 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+
+#define HX8357_NUM_IM_PINS 3
+
+#define HX8357_SWRESET 0x01
+#define HX8357_GET_RED_CHANNEL 0x06
+#define HX8357_GET_GREEN_CHANNEL 0x07
+#define HX8357_GET_BLUE_CHANNEL 0x08
+#define HX8357_GET_POWER_MODE 0x0a
+#define HX8357_GET_MADCTL 0x0b
+#define HX8357_GET_PIXEL_FORMAT 0x0c
+#define HX8357_GET_DISPLAY_MODE 0x0d
+#define HX8357_GET_SIGNAL_MODE 0x0e
+#define HX8357_GET_DIAGNOSTIC_RESULT 0x0f
+#define HX8357_ENTER_SLEEP_MODE 0x10
+#define HX8357_EXIT_SLEEP_MODE 0x11
+#define HX8357_ENTER_PARTIAL_MODE 0x12
+#define HX8357_ENTER_NORMAL_MODE 0x13
+#define HX8357_EXIT_INVERSION_MODE 0x20
+#define HX8357_ENTER_INVERSION_MODE 0x21
+#define HX8357_SET_DISPLAY_OFF 0x28
+#define HX8357_SET_DISPLAY_ON 0x29
+#define HX8357_SET_COLUMN_ADDRESS 0x2a
+#define HX8357_SET_PAGE_ADDRESS 0x2b
+#define HX8357_WRITE_MEMORY_START 0x2c
+#define HX8357_READ_MEMORY_START 0x2e
+#define HX8357_SET_PARTIAL_AREA 0x30
+#define HX8357_SET_SCROLL_AREA 0x33
+#define HX8357_SET_TEAR_OFF 0x34
+#define HX8357_SET_TEAR_ON 0x35
+#define HX8357_SET_ADDRESS_MODE 0x36
+#define HX8357_SET_SCROLL_START 0x37
+#define HX8357_EXIT_IDLE_MODE 0x38
+#define HX8357_ENTER_IDLE_MODE 0x39
+#define HX8357_SET_PIXEL_FORMAT 0x3a
+#define HX8357_SET_PIXEL_FORMAT_DBI_3BIT (0x1)
+#define HX8357_SET_PIXEL_FORMAT_DBI_16BIT (0x5)
+#define HX8357_SET_PIXEL_FORMAT_DBI_18BIT (0x6)
+#define HX8357_SET_PIXEL_FORMAT_DPI_3BIT (0x1 << 4)
+#define HX8357_SET_PIXEL_FORMAT_DPI_16BIT (0x5 << 4)
+#define HX8357_SET_PIXEL_FORMAT_DPI_18BIT (0x6 << 4)
+#define HX8357_WRITE_MEMORY_CONTINUE 0x3c
+#define HX8357_READ_MEMORY_CONTINUE 0x3e
+#define HX8357_SET_TEAR_SCAN_LINES 0x44
+#define HX8357_GET_SCAN_LINES 0x45
+#define HX8357_READ_DDB_START 0xa1
+#define HX8357_SET_DISPLAY_MODE 0xb4
+#define HX8357_SET_DISPLAY_MODE_RGB_THROUGH (0x3)
+#define HX8357_SET_DISPLAY_MODE_RGB_INTERFACE (1 << 4)
+#define HX8357_SET_PANEL_DRIVING 0xc0
+#define HX8357_SET_DISPLAY_FRAME 0xc5
+#define HX8357_SET_RGB 0xc6
+#define HX8357_SET_RGB_ENABLE_HIGH (1 << 1)
+#define HX8357_SET_GAMMA 0xc8
+#define HX8357_SET_POWER 0xd0
+#define HX8357_SET_VCOM 0xd1
+#define HX8357_SET_POWER_NORMAL 0xd2
+#define HX8357_SET_PANEL_RELATED 0xe9
+
+#define HX8369_SET_DISPLAY_BRIGHTNESS 0x51
+#define HX8369_WRITE_CABC_DISPLAY_VALUE 0x53
+#define HX8369_WRITE_CABC_BRIGHT_CTRL 0x55
+#define HX8369_WRITE_CABC_MIN_BRIGHTNESS 0x5e
+#define HX8369_SET_POWER 0xb1
+#define HX8369_SET_DISPLAY_MODE 0xb2
+#define HX8369_SET_DISPLAY_WAVEFORM_CYC 0xb4
+#define HX8369_SET_VCOM 0xb6
+#define HX8369_SET_EXTENSION_COMMAND 0xb9
+#define HX8369_SET_GIP 0xd5
+#define HX8369_SET_GAMMA_CURVE_RELATED 0xe0
+
+struct hx8357_data {
+ unsigned im_pins[HX8357_NUM_IM_PINS];
+ unsigned reset;
+ struct spi_device *spi;
+ int state;
+ bool use_im_pins;
+};
+
+static u8 hx8357_seq_power[] = {
+ HX8357_SET_POWER, 0x44, 0x41, 0x06,
+};
+
+static u8 hx8357_seq_vcom[] = {
+ HX8357_SET_VCOM, 0x40, 0x10,
+};
+
+static u8 hx8357_seq_power_normal[] = {
+ HX8357_SET_POWER_NORMAL, 0x05, 0x12,
+};
+
+static u8 hx8357_seq_panel_driving[] = {
+ HX8357_SET_PANEL_DRIVING, 0x14, 0x3b, 0x00, 0x02, 0x11,
+};
+
+static u8 hx8357_seq_display_frame[] = {
+ HX8357_SET_DISPLAY_FRAME, 0x0c,
+};
+
+static u8 hx8357_seq_panel_related[] = {
+ HX8357_SET_PANEL_RELATED, 0x01,
+};
+
+static u8 hx8357_seq_undefined1[] = {
+ 0xea, 0x03, 0x00, 0x00,
+};
+
+static u8 hx8357_seq_undefined2[] = {
+ 0xeb, 0x40, 0x54, 0x26, 0xdb,
+};
+
+static u8 hx8357_seq_gamma[] = {
+ HX8357_SET_GAMMA, 0x00, 0x15, 0x00, 0x22, 0x00,
+ 0x08, 0x77, 0x26, 0x77, 0x22, 0x04, 0x00,
+};
+
+static u8 hx8357_seq_address_mode[] = {
+ HX8357_SET_ADDRESS_MODE, 0xc0,
+};
+
+static u8 hx8357_seq_pixel_format[] = {
+ HX8357_SET_PIXEL_FORMAT,
+ HX8357_SET_PIXEL_FORMAT_DPI_18BIT |
+ HX8357_SET_PIXEL_FORMAT_DBI_18BIT,
+};
+
+static u8 hx8357_seq_column_address[] = {
+ HX8357_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0x3f,
+};
+
+static u8 hx8357_seq_page_address[] = {
+ HX8357_SET_PAGE_ADDRESS, 0x00, 0x00, 0x01, 0xdf,
+};
+
+static u8 hx8357_seq_rgb[] = {
+ HX8357_SET_RGB, 0x02,
+};
+
+static u8 hx8357_seq_display_mode[] = {
+ HX8357_SET_DISPLAY_MODE,
+ HX8357_SET_DISPLAY_MODE_RGB_THROUGH |
+ HX8357_SET_DISPLAY_MODE_RGB_INTERFACE,
+};
+
+static u8 hx8369_seq_write_CABC_min_brightness[] = {
+ HX8369_WRITE_CABC_MIN_BRIGHTNESS, 0x00,
+};
+
+static u8 hx8369_seq_write_CABC_control[] = {
+ HX8369_WRITE_CABC_DISPLAY_VALUE, 0x24,
+};
+
+static u8 hx8369_seq_set_display_brightness[] = {
+ HX8369_SET_DISPLAY_BRIGHTNESS, 0xFF,
+};
+
+static u8 hx8369_seq_write_CABC_control_setting[] = {
+ HX8369_WRITE_CABC_BRIGHT_CTRL, 0x02,
+};
+
+static u8 hx8369_seq_extension_command[] = {
+ HX8369_SET_EXTENSION_COMMAND, 0xff, 0x83, 0x69,
+};
+
+static u8 hx8369_seq_display_related[] = {
+ HX8369_SET_DISPLAY_MODE, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
+};
+
+static u8 hx8369_seq_panel_waveform_cycle[] = {
+ HX8369_SET_DISPLAY_WAVEFORM_CYC, 0x0a, 0x1d, 0x80, 0x06, 0x02,
+};
+
+static u8 hx8369_seq_set_address_mode[] = {
+ HX8357_SET_ADDRESS_MODE, 0x00,
+};
+
+static u8 hx8369_seq_vcom[] = {
+ HX8369_SET_VCOM, 0x3e, 0x3e,
+};
+
+static u8 hx8369_seq_gip[] = {
+ HX8369_SET_GIP, 0x00, 0x01, 0x03, 0x25, 0x01, 0x02, 0x28, 0x70,
+ 0x11, 0x13, 0x00, 0x00, 0x40, 0x26, 0x51, 0x37, 0x00, 0x00, 0x71,
+ 0x35, 0x60, 0x24, 0x07, 0x0f, 0x04, 0x04,
+};
+
+static u8 hx8369_seq_power[] = {
+ HX8369_SET_POWER, 0x01, 0x00, 0x34, 0x03, 0x00, 0x11, 0x11, 0x32,
+ 0x2f, 0x3f, 0x3f, 0x01, 0x3a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+};
+
+static u8 hx8369_seq_gamma_curve_related[] = {
+ HX8369_SET_GAMMA_CURVE_RELATED, 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d,
+ 0x2e, 0x4a, 0x08, 0x0e, 0x0f, 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e,
+ 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, 0x2e, 0x4a, 0x08, 0x0e, 0x0f,
+ 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e,
+};
+
+static int hx8357_spi_write_then_read(struct lcd_device *lcdev,
+ u8 *txbuf, u16 txlen,
+ u8 *rxbuf, u16 rxlen)
+{
+ struct hx8357_data *lcd = lcd_get_data(lcdev);
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ u16 *local_txbuf = NULL;
+ int ret = 0;
+
+ memset(xfer, 0, sizeof(xfer));
+ spi_message_init(&msg);
+
+ if (txlen) {
+ int i;
+
+ local_txbuf = kcalloc(txlen, sizeof(*local_txbuf), GFP_KERNEL);
+
+ if (!local_txbuf)
+ return -ENOMEM;
+
+ for (i = 0; i < txlen; i++) {
+ local_txbuf[i] = txbuf[i];
+ if (i > 0)
+ local_txbuf[i] |= 1 << 8;
+ }
+
+ xfer[0].len = 2 * txlen;
+ xfer[0].bits_per_word = 9;
+ xfer[0].tx_buf = local_txbuf;
+ spi_message_add_tail(&xfer[0], &msg);
+ }
+
+ if (rxlen) {
+ xfer[1].len = rxlen;
+ xfer[1].bits_per_word = 8;
+ xfer[1].rx_buf = rxbuf;
+ spi_message_add_tail(&xfer[1], &msg);
+ }
+
+ ret = spi_sync(lcd->spi, &msg);
+ if (ret < 0)
+ dev_err(&lcdev->dev, "Couldn't send SPI data\n");
+
+ if (txlen)
+ kfree(local_txbuf);
+
+ return ret;
+}
+
+static inline int hx8357_spi_write_array(struct lcd_device *lcdev,
+ u8 *value, u8 len)
+{
+ return hx8357_spi_write_then_read(lcdev, value, len, NULL, 0);
+}
+
+static inline int hx8357_spi_write_byte(struct lcd_device *lcdev,
+ u8 value)
+{
+ return hx8357_spi_write_then_read(lcdev, &value, 1, NULL, 0);
+}
+
+static int hx8357_enter_standby(struct lcd_device *lcdev)
+{
+ int ret;
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_OFF);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(10000, 12000);
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_ENTER_SLEEP_MODE);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The controller needs 120ms when entering in sleep mode before we can
+ * send the command to go off sleep mode
+ */
+ msleep(120);
+
+ return 0;
+}
+
+static int hx8357_exit_standby(struct lcd_device *lcdev)
+{
+ int ret;
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The controller needs 120ms when exiting from sleep mode before we
+ * can send the command to enter in sleep mode
+ */
+ msleep(120);
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void hx8357_lcd_reset(struct lcd_device *lcdev)
+{
+ struct hx8357_data *lcd = lcd_get_data(lcdev);
+
+ /* Reset the screen */
+ gpio_set_value(lcd->reset, 1);
+ usleep_range(10000, 12000);
+ gpio_set_value(lcd->reset, 0);
+ usleep_range(10000, 12000);
+ gpio_set_value(lcd->reset, 1);
+
+ /* The controller needs 120ms to recover from reset */
+ msleep(120);
+}
+
+static int hx8357_lcd_init(struct lcd_device *lcdev)
+{
+ struct hx8357_data *lcd = lcd_get_data(lcdev);
+ int ret;
+
+ /*
+ * Set the interface selection pins to SPI mode, with three
+ * wires
+ */
+ if (lcd->use_im_pins) {
+ gpio_set_value_cansleep(lcd->im_pins[0], 1);
+ gpio_set_value_cansleep(lcd->im_pins[1], 0);
+ gpio_set_value_cansleep(lcd->im_pins[2], 1);
+ }
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_power,
+ ARRAY_SIZE(hx8357_seq_power));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_vcom,
+ ARRAY_SIZE(hx8357_seq_vcom));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_power_normal,
+ ARRAY_SIZE(hx8357_seq_power_normal));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_driving,
+ ARRAY_SIZE(hx8357_seq_panel_driving));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_frame,
+ ARRAY_SIZE(hx8357_seq_display_frame));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_related,
+ ARRAY_SIZE(hx8357_seq_panel_related));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined1,
+ ARRAY_SIZE(hx8357_seq_undefined1));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined2,
+ ARRAY_SIZE(hx8357_seq_undefined2));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_gamma,
+ ARRAY_SIZE(hx8357_seq_gamma));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_address_mode,
+ ARRAY_SIZE(hx8357_seq_address_mode));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_pixel_format,
+ ARRAY_SIZE(hx8357_seq_pixel_format));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_column_address,
+ ARRAY_SIZE(hx8357_seq_column_address));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_page_address,
+ ARRAY_SIZE(hx8357_seq_page_address));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_rgb,
+ ARRAY_SIZE(hx8357_seq_rgb));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_mode,
+ ARRAY_SIZE(hx8357_seq_display_mode));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The controller needs 120ms to fully recover from exiting sleep mode
+ */
+ msleep(120);
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(5000, 7000);
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_WRITE_MEMORY_START);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int hx8369_lcd_init(struct lcd_device *lcdev)
+{
+ int ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_extension_command,
+ ARRAY_SIZE(hx8369_seq_extension_command));
+ if (ret < 0)
+ return ret;
+ usleep_range(10000, 12000);
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_display_related,
+ ARRAY_SIZE(hx8369_seq_display_related));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_panel_waveform_cycle,
+ ARRAY_SIZE(hx8369_seq_panel_waveform_cycle));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_set_address_mode,
+ ARRAY_SIZE(hx8369_seq_set_address_mode));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_vcom,
+ ARRAY_SIZE(hx8369_seq_vcom));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_gip,
+ ARRAY_SIZE(hx8369_seq_gip));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_power,
+ ARRAY_SIZE(hx8369_seq_power));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The controller needs 120ms to fully recover from exiting sleep mode
+ */
+ msleep(120);
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_gamma_curve_related,
+ ARRAY_SIZE(hx8369_seq_gamma_curve_related));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE);
+ if (ret < 0)
+ return ret;
+ usleep_range(1000, 1200);
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_write_CABC_control,
+ ARRAY_SIZE(hx8369_seq_write_CABC_control));
+ if (ret < 0)
+ return ret;
+ usleep_range(10000, 12000);
+
+ ret = hx8357_spi_write_array(lcdev,
+ hx8369_seq_write_CABC_control_setting,
+ ARRAY_SIZE(hx8369_seq_write_CABC_control_setting));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_array(lcdev,
+ hx8369_seq_write_CABC_min_brightness,
+ ARRAY_SIZE(hx8369_seq_write_CABC_min_brightness));
+ if (ret < 0)
+ return ret;
+ usleep_range(10000, 12000);
+
+ ret = hx8357_spi_write_array(lcdev, hx8369_seq_set_display_brightness,
+ ARRAY_SIZE(hx8369_seq_set_display_brightness));
+ if (ret < 0)
+ return ret;
+
+ ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+static int hx8357_set_power(struct lcd_device *lcdev, int power)
+{
+ struct hx8357_data *lcd = lcd_get_data(lcdev);
+ int ret = 0;
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->state))
+ ret = hx8357_exit_standby(lcdev);
+ else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->state))
+ ret = hx8357_enter_standby(lcdev);
+
+ if (ret == 0)
+ lcd->state = power;
+ else
+ dev_warn(&lcdev->dev, "failed to set power mode %d\n", power);
+
+ return ret;
+}
+
+static int hx8357_get_power(struct lcd_device *lcdev)
+{
+ struct hx8357_data *lcd = lcd_get_data(lcdev);
+
+ return lcd->state;
+}
+
+static struct lcd_ops hx8357_ops = {
+ .set_power = hx8357_set_power,
+ .get_power = hx8357_get_power,
+};
+
+static const struct of_device_id hx8357_dt_ids[] = {
+ {
+ .compatible = "himax,hx8357",
+ .data = hx8357_lcd_init,
+ },
+ {
+ .compatible = "himax,hx8369",
+ .data = hx8369_lcd_init,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hx8357_dt_ids);
+
+static int hx8357_probe(struct spi_device *spi)
+{
+ struct lcd_device *lcdev;
+ struct hx8357_data *lcd;
+ const struct of_device_id *match;
+ int i, ret;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "SPI setup failed.\n");
+ return ret;
+ }
+
+ lcd->spi = spi;
+
+ match = of_match_device(hx8357_dt_ids, &spi->dev);
+ if (!match || !match->data)
+ return -EINVAL;
+
+ lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0);
+ if (!gpio_is_valid(lcd->reset)) {
+ dev_err(&spi->dev, "Missing dt property: gpios-reset\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, lcd->reset,
+ GPIOF_OUT_INIT_HIGH,
+ "hx8357-reset");
+ if (ret) {
+ dev_err(&spi->dev,
+ "failed to request gpio %d: %d\n",
+ lcd->reset, ret);
+ return -EINVAL;
+ }
+
+ if (of_find_property(spi->dev.of_node, "im-gpios", NULL)) {
+ lcd->use_im_pins = 1;
+
+ for (i = 0; i < HX8357_NUM_IM_PINS; i++) {
+ lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node,
+ "im-gpios", i);
+ if (lcd->im_pins[i] == -EPROBE_DEFER) {
+ dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n");
+ return -EPROBE_DEFER;
+ }
+ if (!gpio_is_valid(lcd->im_pins[i])) {
+ dev_err(&spi->dev, "Missing dt property: im-gpios\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i],
+ GPIOF_OUT_INIT_LOW,
+ "im_pins");
+ if (ret) {
+ dev_err(&spi->dev, "failed to request gpio %d: %d\n",
+ lcd->im_pins[i], ret);
+ return -EINVAL;
+ }
+ }
+ } else {
+ lcd->use_im_pins = 0;
+ }
+
+ lcdev = devm_lcd_device_register(&spi->dev, "mxsfb", &spi->dev, lcd,
+ &hx8357_ops);
+ if (IS_ERR(lcdev)) {
+ ret = PTR_ERR(lcdev);
+ return ret;
+ }
+ spi_set_drvdata(spi, lcdev);
+
+ hx8357_lcd_reset(lcdev);
+
+ ret = ((int (*)(struct lcd_device *))match->data)(lcdev);
+ if (ret) {
+ dev_err(&spi->dev, "Couldn't initialize panel\n");
+ return ret;
+ }
+
+ dev_info(&spi->dev, "Panel probed\n");
+
+ return 0;
+}
+
+static struct spi_driver hx8357_driver = {
+ .probe = hx8357_probe,
+ .driver = {
+ .name = "hx8357",
+ .of_match_table = hx8357_dt_ids,
+ },
+};
+
+module_spi_driver(hx8357_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Himax HX-8357 LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/ili922x.c b/drivers/video/backlight/ili922x.c
new file mode 100644
index 00000000000..ea67fe199e3
--- /dev/null
+++ b/drivers/video/backlight/ili922x.c
@@ -0,0 +1,550 @@
+/*
+ * (C) Copyright 2008
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * 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 driver implements a lcd device for the ILITEK 922x display
+ * controller. The interface to the display is SPI and the display's
+ * memory is cyclically updated over the RGB interface.
+ */
+
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string.h>
+
+/* Register offset, see manual section 8.2 */
+#define REG_START_OSCILLATION 0x00
+#define REG_DRIVER_CODE_READ 0x00
+#define REG_DRIVER_OUTPUT_CONTROL 0x01
+#define REG_LCD_AC_DRIVEING_CONTROL 0x02
+#define REG_ENTRY_MODE 0x03
+#define REG_COMPARE_1 0x04
+#define REG_COMPARE_2 0x05
+#define REG_DISPLAY_CONTROL_1 0x07
+#define REG_DISPLAY_CONTROL_2 0x08
+#define REG_DISPLAY_CONTROL_3 0x09
+#define REG_FRAME_CYCLE_CONTROL 0x0B
+#define REG_EXT_INTF_CONTROL 0x0C
+#define REG_POWER_CONTROL_1 0x10
+#define REG_POWER_CONTROL_2 0x11
+#define REG_POWER_CONTROL_3 0x12
+#define REG_POWER_CONTROL_4 0x13
+#define REG_RAM_ADDRESS_SET 0x21
+#define REG_WRITE_DATA_TO_GRAM 0x22
+#define REG_RAM_WRITE_MASK1 0x23
+#define REG_RAM_WRITE_MASK2 0x24
+#define REG_GAMMA_CONTROL_1 0x30
+#define REG_GAMMA_CONTROL_2 0x31
+#define REG_GAMMA_CONTROL_3 0x32
+#define REG_GAMMA_CONTROL_4 0x33
+#define REG_GAMMA_CONTROL_5 0x34
+#define REG_GAMMA_CONTROL_6 0x35
+#define REG_GAMMA_CONTROL_7 0x36
+#define REG_GAMMA_CONTROL_8 0x37
+#define REG_GAMMA_CONTROL_9 0x38
+#define REG_GAMMA_CONTROL_10 0x39
+#define REG_GATE_SCAN_CONTROL 0x40
+#define REG_VERT_SCROLL_CONTROL 0x41
+#define REG_FIRST_SCREEN_DRIVE_POS 0x42
+#define REG_SECOND_SCREEN_DRIVE_POS 0x43
+#define REG_RAM_ADDR_POS_H 0x44
+#define REG_RAM_ADDR_POS_V 0x45
+#define REG_OSCILLATOR_CONTROL 0x4F
+#define REG_GPIO 0x60
+#define REG_OTP_VCM_PROGRAMMING 0x61
+#define REG_OTP_VCM_STATUS_ENABLE 0x62
+#define REG_OTP_PROGRAMMING_ID_KEY 0x65
+
+/*
+ * maximum frequency for register access
+ * (not for the GRAM access)
+ */
+#define ILITEK_MAX_FREQ_REG 4000000
+
+/*
+ * Device ID as found in the datasheet (supports 9221 and 9222)
+ */
+#define ILITEK_DEVICE_ID 0x9220
+#define ILITEK_DEVICE_ID_MASK 0xFFF0
+
+/* Last two bits in the START BYTE */
+#define START_RS_INDEX 0
+#define START_RS_REG 1
+#define START_RW_WRITE 0
+#define START_RW_READ 1
+
+/**
+ * START_BYTE(id, rs, rw)
+ *
+ * Set the start byte according to the required operation.
+ * The start byte is defined as:
+ * ----------------------------------
+ * | 0 | 1 | 1 | 1 | 0 | ID | RS | RW |
+ * ----------------------------------
+ * @id: display's id as set by the manufacturer
+ * @rs: operation type bit, one of:
+ * - START_RS_INDEX set the index register
+ * - START_RS_REG write/read registers/GRAM
+ * @rw: read/write operation
+ * - START_RW_WRITE write
+ * - START_RW_READ read
+ */
+#define START_BYTE(id, rs, rw) \
+ (0x70 | (((id) & 0x01) << 2) | (((rs) & 0x01) << 1) | ((rw) & 0x01))
+
+/**
+ * CHECK_FREQ_REG(spi_device s, spi_transfer x) - Check the frequency
+ * for the SPI transfer. According to the datasheet, the controller
+ * accept higher frequency for the GRAM transfer, but it requires
+ * lower frequency when the registers are read/written.
+ * The macro sets the frequency in the spi_transfer structure if
+ * the frequency exceeds the maximum value.
+ */
+#define CHECK_FREQ_REG(s, x) \
+ do { \
+ if (s->max_speed_hz > ILITEK_MAX_FREQ_REG) \
+ ((struct spi_transfer *)x)->speed_hz = \
+ ILITEK_MAX_FREQ_REG; \
+ } while (0)
+
+#define CMD_BUFSIZE 16
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+#define set_tx_byte(b) (tx_invert ? ~(b) : b)
+
+/**
+ * ili922x_id - id as set by manufacturer
+ */
+static int ili922x_id = 1;
+module_param(ili922x_id, int, 0);
+
+static int tx_invert;
+module_param(tx_invert, int, 0);
+
+/**
+ * driver's private structure
+ */
+struct ili922x {
+ struct spi_device *spi;
+ struct lcd_device *ld;
+ int power;
+};
+
+/**
+ * ili922x_read_status - read status register from display
+ * @spi: spi device
+ * @rs: output value
+ */
+static int ili922x_read_status(struct spi_device *spi, u16 *rs)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer;
+ unsigned char tbuf[CMD_BUFSIZE];
+ unsigned char rbuf[CMD_BUFSIZE];
+ int ret, i;
+
+ memset(&xfer, 0, sizeof(struct spi_transfer));
+ spi_message_init(&msg);
+ xfer.tx_buf = tbuf;
+ xfer.rx_buf = rbuf;
+ xfer.cs_change = 1;
+ CHECK_FREQ_REG(spi, &xfer);
+
+ tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_INDEX,
+ START_RW_READ));
+ /*
+ * we need 4-byte xfer here due to invalid dummy byte
+ * received after start byte
+ */
+ for (i = 1; i < 4; i++)
+ tbuf[i] = set_tx_byte(0); /* dummy */
+
+ xfer.bits_per_word = 8;
+ xfer.len = 4;
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(spi, &msg);
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "Error sending SPI message 0x%x", ret);
+ return ret;
+ }
+
+ *rs = (rbuf[2] << 8) + rbuf[3];
+ return 0;
+}
+
+/**
+ * ili922x_read - read register from display
+ * @spi: spi device
+ * @reg: offset of the register to be read
+ * @rx: output value
+ */
+static int ili922x_read(struct spi_device *spi, u8 reg, u16 *rx)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer_regindex, xfer_regvalue;
+ unsigned char tbuf[CMD_BUFSIZE];
+ unsigned char rbuf[CMD_BUFSIZE];
+ int ret, len = 0, send_bytes;
+
+ memset(&xfer_regindex, 0, sizeof(struct spi_transfer));
+ memset(&xfer_regvalue, 0, sizeof(struct spi_transfer));
+ spi_message_init(&msg);
+ xfer_regindex.tx_buf = tbuf;
+ xfer_regindex.rx_buf = rbuf;
+ xfer_regindex.cs_change = 1;
+ CHECK_FREQ_REG(spi, &xfer_regindex);
+
+ tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_INDEX,
+ START_RW_WRITE));
+ tbuf[1] = set_tx_byte(0);
+ tbuf[2] = set_tx_byte(reg);
+ xfer_regindex.bits_per_word = 8;
+ len = xfer_regindex.len = 3;
+ spi_message_add_tail(&xfer_regindex, &msg);
+
+ send_bytes = len;
+
+ tbuf[len++] = set_tx_byte(START_BYTE(ili922x_id, START_RS_REG,
+ START_RW_READ));
+ tbuf[len++] = set_tx_byte(0);
+ tbuf[len] = set_tx_byte(0);
+
+ xfer_regvalue.cs_change = 1;
+ xfer_regvalue.len = 3;
+ xfer_regvalue.tx_buf = &tbuf[send_bytes];
+ xfer_regvalue.rx_buf = &rbuf[send_bytes];
+ CHECK_FREQ_REG(spi, &xfer_regvalue);
+
+ spi_message_add_tail(&xfer_regvalue, &msg);
+ ret = spi_sync(spi, &msg);
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "Error sending SPI message 0x%x", ret);
+ return ret;
+ }
+
+ *rx = (rbuf[1 + send_bytes] << 8) + rbuf[2 + send_bytes];
+ return 0;
+}
+
+/**
+ * ili922x_write - write a controller register
+ * @spi: struct spi_device *
+ * @reg: offset of the register to be written
+ * @value: value to be written
+ */
+static int ili922x_write(struct spi_device *spi, u8 reg, u16 value)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer_regindex, xfer_regvalue;
+ unsigned char tbuf[CMD_BUFSIZE];
+ unsigned char rbuf[CMD_BUFSIZE];
+ int ret, len = 0;
+
+ memset(&xfer_regindex, 0, sizeof(struct spi_transfer));
+ memset(&xfer_regvalue, 0, sizeof(struct spi_transfer));
+
+ spi_message_init(&msg);
+ xfer_regindex.tx_buf = tbuf;
+ xfer_regindex.rx_buf = rbuf;
+ xfer_regindex.cs_change = 1;
+ CHECK_FREQ_REG(spi, &xfer_regindex);
+
+ tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_INDEX,
+ START_RW_WRITE));
+ tbuf[1] = set_tx_byte(0);
+ tbuf[2] = set_tx_byte(reg);
+ xfer_regindex.bits_per_word = 8;
+ xfer_regindex.len = 3;
+ spi_message_add_tail(&xfer_regindex, &msg);
+
+ ret = spi_sync(spi, &msg);
+
+ spi_message_init(&msg);
+ len = 0;
+ tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_REG,
+ START_RW_WRITE));
+ tbuf[1] = set_tx_byte((value & 0xFF00) >> 8);
+ tbuf[2] = set_tx_byte(value & 0x00FF);
+
+ xfer_regvalue.cs_change = 1;
+ xfer_regvalue.len = 3;
+ xfer_regvalue.tx_buf = tbuf;
+ xfer_regvalue.rx_buf = rbuf;
+ CHECK_FREQ_REG(spi, &xfer_regvalue);
+
+ spi_message_add_tail(&xfer_regvalue, &msg);
+
+ ret = spi_sync(spi, &msg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Error sending SPI message 0x%x", ret);
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef DEBUG
+/**
+ * ili922x_reg_dump - dump all registers
+ */
+static void ili922x_reg_dump(struct spi_device *spi)
+{
+ u8 reg;
+ u16 rx;
+
+ dev_dbg(&spi->dev, "ILI922x configuration registers:\n");
+ for (reg = REG_START_OSCILLATION;
+ reg <= REG_OTP_PROGRAMMING_ID_KEY; reg++) {
+ ili922x_read(spi, reg, &rx);
+ dev_dbg(&spi->dev, "reg @ 0x%02X: 0x%04X\n", reg, rx);
+ }
+}
+#else
+static inline void ili922x_reg_dump(struct spi_device *spi) {}
+#endif
+
+/**
+ * set_write_to_gram_reg - initialize the display to write the GRAM
+ * @spi: spi device
+ */
+static void set_write_to_gram_reg(struct spi_device *spi)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer;
+ unsigned char tbuf[CMD_BUFSIZE];
+
+ memset(&xfer, 0, sizeof(struct spi_transfer));
+
+ spi_message_init(&msg);
+ xfer.tx_buf = tbuf;
+ xfer.rx_buf = NULL;
+ xfer.cs_change = 1;
+
+ tbuf[0] = START_BYTE(ili922x_id, START_RS_INDEX, START_RW_WRITE);
+ tbuf[1] = 0;
+ tbuf[2] = REG_WRITE_DATA_TO_GRAM;
+
+ xfer.bits_per_word = 8;
+ xfer.len = 3;
+ spi_message_add_tail(&xfer, &msg);
+ spi_sync(spi, &msg);
+}
+
+/**
+ * ili922x_poweron - turn the display on
+ * @spi: spi device
+ *
+ * The sequence to turn on the display is taken from
+ * the datasheet and/or the example code provided by the
+ * manufacturer.
+ */
+static int ili922x_poweron(struct spi_device *spi)
+{
+ int ret;
+
+ /* Power on */
+ ret = ili922x_write(spi, REG_POWER_CONTROL_1, 0x0000);
+ usleep_range(10000, 10500);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_2, 0x0000);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_3, 0x0000);
+ msleep(40);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_4, 0x0000);
+ msleep(40);
+ /* register 0x56 is not documented in the datasheet */
+ ret += ili922x_write(spi, 0x56, 0x080F);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_1, 0x4240);
+ usleep_range(10000, 10500);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_2, 0x0000);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_3, 0x0014);
+ msleep(40);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_4, 0x1319);
+ msleep(40);
+
+ return ret;
+}
+
+/**
+ * ili922x_poweroff - turn the display off
+ * @spi: spi device
+ */
+static int ili922x_poweroff(struct spi_device *spi)
+{
+ int ret;
+
+ /* Power off */
+ ret = ili922x_write(spi, REG_POWER_CONTROL_1, 0x0000);
+ usleep_range(10000, 10500);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_2, 0x0000);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_3, 0x0000);
+ msleep(40);
+ ret += ili922x_write(spi, REG_POWER_CONTROL_4, 0x0000);
+ msleep(40);
+
+ return ret;
+}
+
+/**
+ * ili922x_display_init - initialize the display by setting
+ * the configuration registers
+ * @spi: spi device
+ */
+static void ili922x_display_init(struct spi_device *spi)
+{
+ ili922x_write(spi, REG_START_OSCILLATION, 1);
+ usleep_range(10000, 10500);
+ ili922x_write(spi, REG_DRIVER_OUTPUT_CONTROL, 0x691B);
+ ili922x_write(spi, REG_LCD_AC_DRIVEING_CONTROL, 0x0700);
+ ili922x_write(spi, REG_ENTRY_MODE, 0x1030);
+ ili922x_write(spi, REG_COMPARE_1, 0x0000);
+ ili922x_write(spi, REG_COMPARE_2, 0x0000);
+ ili922x_write(spi, REG_DISPLAY_CONTROL_1, 0x0037);
+ ili922x_write(spi, REG_DISPLAY_CONTROL_2, 0x0202);
+ ili922x_write(spi, REG_DISPLAY_CONTROL_3, 0x0000);
+ ili922x_write(spi, REG_FRAME_CYCLE_CONTROL, 0x0000);
+
+ /* Set RGB interface */
+ ili922x_write(spi, REG_EXT_INTF_CONTROL, 0x0110);
+
+ ili922x_poweron(spi);
+
+ ili922x_write(spi, REG_GAMMA_CONTROL_1, 0x0302);
+ ili922x_write(spi, REG_GAMMA_CONTROL_2, 0x0407);
+ ili922x_write(spi, REG_GAMMA_CONTROL_3, 0x0304);
+ ili922x_write(spi, REG_GAMMA_CONTROL_4, 0x0203);
+ ili922x_write(spi, REG_GAMMA_CONTROL_5, 0x0706);
+ ili922x_write(spi, REG_GAMMA_CONTROL_6, 0x0407);
+ ili922x_write(spi, REG_GAMMA_CONTROL_7, 0x0706);
+ ili922x_write(spi, REG_GAMMA_CONTROL_8, 0x0000);
+ ili922x_write(spi, REG_GAMMA_CONTROL_9, 0x0C06);
+ ili922x_write(spi, REG_GAMMA_CONTROL_10, 0x0F00);
+ ili922x_write(spi, REG_RAM_ADDRESS_SET, 0x0000);
+ ili922x_write(spi, REG_GATE_SCAN_CONTROL, 0x0000);
+ ili922x_write(spi, REG_VERT_SCROLL_CONTROL, 0x0000);
+ ili922x_write(spi, REG_FIRST_SCREEN_DRIVE_POS, 0xDB00);
+ ili922x_write(spi, REG_SECOND_SCREEN_DRIVE_POS, 0xDB00);
+ ili922x_write(spi, REG_RAM_ADDR_POS_H, 0xAF00);
+ ili922x_write(spi, REG_RAM_ADDR_POS_V, 0xDB00);
+ ili922x_reg_dump(spi);
+ set_write_to_gram_reg(spi);
+}
+
+static int ili922x_lcd_power(struct ili922x *lcd, int power)
+{
+ int ret = 0;
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
+ ret = ili922x_poweron(lcd->spi);
+ else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
+ ret = ili922x_poweroff(lcd->spi);
+
+ if (!ret)
+ lcd->power = power;
+
+ return ret;
+}
+
+static int ili922x_set_power(struct lcd_device *ld, int power)
+{
+ struct ili922x *ili = lcd_get_data(ld);
+
+ return ili922x_lcd_power(ili, power);
+}
+
+static int ili922x_get_power(struct lcd_device *ld)
+{
+ struct ili922x *ili = lcd_get_data(ld);
+
+ return ili->power;
+}
+
+static struct lcd_ops ili922x_ops = {
+ .get_power = ili922x_get_power,
+ .set_power = ili922x_set_power,
+};
+
+static int ili922x_probe(struct spi_device *spi)
+{
+ struct ili922x *ili;
+ struct lcd_device *lcd;
+ int ret;
+ u16 reg = 0;
+
+ ili = devm_kzalloc(&spi->dev, sizeof(*ili), GFP_KERNEL);
+ if (!ili)
+ return -ENOMEM;
+
+ ili->spi = spi;
+ spi_set_drvdata(spi, ili);
+
+ /* check if the device is connected */
+ ret = ili922x_read(spi, REG_DRIVER_CODE_READ, &reg);
+ if (ret || ((reg & ILITEK_DEVICE_ID_MASK) != ILITEK_DEVICE_ID)) {
+ dev_err(&spi->dev,
+ "no LCD found: Chip ID 0x%x, ret %d\n",
+ reg, ret);
+ return -ENODEV;
+ } else {
+ dev_info(&spi->dev, "ILI%x found, SPI freq %d, mode %d\n",
+ reg, spi->max_speed_hz, spi->mode);
+ }
+
+ ret = ili922x_read_status(spi, &reg);
+ if (ret) {
+ dev_err(&spi->dev, "reading RS failed...\n");
+ return ret;
+ } else
+ dev_dbg(&spi->dev, "status: 0x%x\n", reg);
+
+ ili922x_display_init(spi);
+
+ ili->power = FB_BLANK_POWERDOWN;
+
+ lcd = devm_lcd_device_register(&spi->dev, "ili922xlcd", &spi->dev, ili,
+ &ili922x_ops);
+ if (IS_ERR(lcd)) {
+ dev_err(&spi->dev, "cannot register LCD\n");
+ return PTR_ERR(lcd);
+ }
+
+ ili->ld = lcd;
+ spi_set_drvdata(spi, ili);
+
+ ili922x_lcd_power(ili, FB_BLANK_UNBLANK);
+
+ return 0;
+}
+
+static int ili922x_remove(struct spi_device *spi)
+{
+ ili922x_poweroff(spi);
+ return 0;
+}
+
+static struct spi_driver ili922x_driver = {
+ .driver = {
+ .name = "ili922x",
+ .owner = THIS_MODULE,
+ },
+ .probe = ili922x_probe,
+ .remove = ili922x_remove,
+};
+
+module_spi_driver(ili922x_driver);
+
+MODULE_AUTHOR("Stefano Babic <sbabic@denx.de>");
+MODULE_DESCRIPTION("ILI9221/9222 LCD driver");
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(ili922x_id, "set controller identifier (default=1)");
+MODULE_PARM_DESC(tx_invert, "invert bytes before sending");
diff --git a/drivers/video/backlight/ili9320.c b/drivers/video/backlight/ili9320.c
index ba89b41b639..2cf39e6d519 100644
--- a/drivers/video/backlight/ili9320.c
+++ b/drivers/video/backlight/ili9320.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/lcd.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spi/spi.h>
@@ -44,7 +45,7 @@ static inline int ili9320_write_spi(struct ili9320 *ili,
/* second message is the data to transfer */
data[0] = spi->id | ILI9320_SPI_DATA | ILI9320_SPI_WRITE;
- data[1] = value >> 8;
+ data[1] = value >> 8;
data[2] = value;
return spi_sync(spi->dev, &spi->message);
@@ -55,11 +56,10 @@ int ili9320_write(struct ili9320 *ili, unsigned int reg, unsigned int value)
dev_dbg(ili->dev, "write: reg=%02x, val=%04x\n", reg, value);
return ili->write(ili, reg, value);
}
-
EXPORT_SYMBOL_GPL(ili9320_write);
int ili9320_write_regs(struct ili9320 *ili,
- struct ili9320_reg *values,
+ const struct ili9320_reg *values,
int nr_values)
{
int index;
@@ -73,7 +73,6 @@ int ili9320_write_regs(struct ili9320 *ili,
return 0;
}
-
EXPORT_SYMBOL_GPL(ili9320_write_regs);
static void ili9320_reset(struct ili9320 *lcd)
@@ -170,7 +169,7 @@ static struct lcd_ops ili9320_ops = {
.set_power = ili9320_set_power,
};
-static void __devinit ili9320_setup_spi(struct ili9320 *ili,
+static void ili9320_setup_spi(struct ili9320 *ili,
struct spi_device *dev)
{
struct ili9320_spi *spi = &ili->access.spi;
@@ -196,10 +195,10 @@ static void __devinit ili9320_setup_spi(struct ili9320 *ili,
spi_message_add_tail(&spi->xfer[1], &spi->message);
}
-int __devinit ili9320_probe_spi(struct spi_device *spi,
+int ili9320_probe_spi(struct spi_device *spi,
struct ili9320_client *client)
{
- struct ili9320_platdata *cfg = spi->dev.platform_data;
+ struct ili9320_platdata *cfg = dev_get_platdata(&spi->dev);
struct device *dev = &spi->dev;
struct ili9320 *ili;
struct lcd_device *lcd;
@@ -219,11 +218,9 @@ int __devinit ili9320_probe_spi(struct spi_device *spi,
/* allocate and initialse our state */
- ili = kzalloc(sizeof(struct ili9320), GFP_KERNEL);
- if (ili == NULL) {
- dev_err(dev, "no memory for device\n");
+ ili = devm_kzalloc(&spi->dev, sizeof(struct ili9320), GFP_KERNEL);
+ if (ili == NULL)
return -ENOMEM;
- }
ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1);
@@ -232,15 +229,15 @@ int __devinit ili9320_probe_spi(struct spi_device *spi,
ili->power = FB_BLANK_POWERDOWN;
ili->platdata = cfg;
- dev_set_drvdata(&spi->dev, ili);
+ spi_set_drvdata(spi, ili);
ili9320_setup_spi(ili, spi);
- lcd = lcd_device_register("ili9320", dev, ili, &ili9320_ops);
+ lcd = devm_lcd_device_register(&spi->dev, "ili9320", dev, ili,
+ &ili9320_ops);
if (IS_ERR(lcd)) {
dev_err(dev, "failed to register lcd device\n");
- ret = PTR_ERR(lcd);
- goto err_free;
+ return PTR_ERR(lcd);
}
ili->lcd = lcd;
@@ -250,70 +247,47 @@ int __devinit ili9320_probe_spi(struct spi_device *spi,
ret = ili9320_power(ili, FB_BLANK_UNBLANK);
if (ret != 0) {
dev_err(dev, "failed to set lcd power state\n");
- goto err_unregister;
+ return ret;
}
return 0;
-
- err_unregister:
- lcd_device_unregister(lcd);
-
- err_free:
- kfree(ili);
-
- return ret;
}
-
EXPORT_SYMBOL_GPL(ili9320_probe_spi);
-int __devexit ili9320_remove(struct ili9320 *ili)
+int ili9320_remove(struct ili9320 *ili)
{
ili9320_power(ili, FB_BLANK_POWERDOWN);
-
- lcd_device_unregister(ili->lcd);
- kfree(ili);
-
return 0;
}
-
EXPORT_SYMBOL_GPL(ili9320_remove);
-#ifdef CONFIG_PM
-int ili9320_suspend(struct ili9320 *lcd, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+int ili9320_suspend(struct ili9320 *lcd)
{
int ret;
- dev_dbg(lcd->dev, "%s: event %d\n", __func__, state.event);
+ ret = ili9320_power(lcd, FB_BLANK_POWERDOWN);
- if (state.event == PM_EVENT_SUSPEND) {
- ret = ili9320_power(lcd, FB_BLANK_POWERDOWN);
-
- if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
- ili9320_write(lcd, ILI9320_POWER1, lcd->power1 |
- ILI9320_POWER1_SLP |
- ILI9320_POWER1_DSTB);
- lcd->initialised = 0;
- }
-
- return ret;
+ if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
+ ili9320_write(lcd, ILI9320_POWER1, lcd->power1 |
+ ILI9320_POWER1_SLP |
+ ILI9320_POWER1_DSTB);
+ lcd->initialised = 0;
}
- return 0;
+ return ret;
}
-
EXPORT_SYMBOL_GPL(ili9320_suspend);
int ili9320_resume(struct ili9320 *lcd)
{
dev_info(lcd->dev, "resuming from power state %d\n", lcd->power);
- if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
+ if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP)
ili9320_write(lcd, ILI9320_POWER1, 0x00);
- }
return ili9320_power(lcd, FB_BLANK_UNBLANK);
}
-
EXPORT_SYMBOL_GPL(ili9320_resume);
#endif
@@ -322,7 +296,6 @@ void ili9320_shutdown(struct ili9320 *lcd)
{
ili9320_power(lcd, FB_BLANK_POWERDOWN);
}
-
EXPORT_SYMBOL_GPL(ili9320_shutdown);
MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
diff --git a/drivers/video/backlight/ili9320.h b/drivers/video/backlight/ili9320.h
index e388eca7cac..42329e7aa9a 100644
--- a/drivers/video/backlight/ili9320.h
+++ b/drivers/video/backlight/ili9320.h
@@ -63,7 +63,7 @@ extern int ili9320_write(struct ili9320 *ili,
unsigned int reg, unsigned int value);
extern int ili9320_write_regs(struct ili9320 *ili,
- struct ili9320_reg *values,
+ const struct ili9320_reg *values,
int nr_values);
/* Device probe */
@@ -76,5 +76,5 @@ extern void ili9320_shutdown(struct ili9320 *lcd);
/* PM */
-extern int ili9320_suspend(struct ili9320 *lcd, pm_message_t state);
+extern int ili9320_suspend(struct ili9320 *lcd);
extern int ili9320_resume(struct ili9320 *lcd);
diff --git a/drivers/video/backlight/jornada720_bl.c b/drivers/video/backlight/jornada720_bl.c
new file mode 100644
index 00000000000..6ce96b4a879
--- /dev/null
+++ b/drivers/video/backlight/jornada720_bl.c
@@ -0,0 +1,154 @@
+/*
+ *
+ * Backlight driver for HP Jornada 700 series (710/720/728)
+ * Copyright (C) 2006-2009 Kristoffer Ericson <kristoffer.ericson@gmail.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 or any later version as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/backlight.h>
+#include <linux/device.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <mach/jornada720.h>
+#include <mach/hardware.h>
+
+#include <video/s1d13xxxfb.h>
+
+#define BL_MAX_BRIGHT 255
+#define BL_DEF_BRIGHT 25
+
+static int jornada_bl_get_brightness(struct backlight_device *bd)
+{
+ int ret;
+
+ /* check if backlight is on */
+ if (!(PPSR & PPC_LDD1))
+ return 0;
+
+ jornada_ssp_start();
+
+ /* cmd should return txdummy */
+ ret = jornada_ssp_byte(GETBRIGHTNESS);
+
+ if (jornada_ssp_byte(GETBRIGHTNESS) != TXDUMMY) {
+ dev_err(&bd->dev, "get brightness timeout\n");
+ jornada_ssp_end();
+ return -ETIMEDOUT;
+ } else {
+ /* exchange txdummy for value */
+ ret = jornada_ssp_byte(TXDUMMY);
+ }
+
+ jornada_ssp_end();
+
+ return BL_MAX_BRIGHT - ret;
+}
+
+static int jornada_bl_update_status(struct backlight_device *bd)
+{
+ int ret = 0;
+
+ jornada_ssp_start();
+
+ /* If backlight is off then really turn it off */
+ if ((bd->props.power != FB_BLANK_UNBLANK) || (bd->props.fb_blank != FB_BLANK_UNBLANK)) {
+ ret = jornada_ssp_byte(BRIGHTNESSOFF);
+ if (ret != TXDUMMY) {
+ dev_info(&bd->dev, "brightness off timeout\n");
+ /* turn off backlight */
+ PPSR &= ~PPC_LDD1;
+ PPDR |= PPC_LDD1;
+ ret = -ETIMEDOUT;
+ }
+ } else /* turn on backlight */
+ PPSR |= PPC_LDD1;
+
+ /* send command to our mcu */
+ if (jornada_ssp_byte(SETBRIGHTNESS) != TXDUMMY) {
+ dev_info(&bd->dev, "failed to set brightness\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /*
+ * at this point we expect that the mcu has accepted
+ * our command and is waiting for our new value
+ * please note that maximum brightness is 255,
+ * but due to physical layout it is equal to 0, so we simply
+ * invert the value (MAX VALUE - NEW VALUE).
+ */
+ if (jornada_ssp_byte(BL_MAX_BRIGHT - bd->props.brightness)
+ != TXDUMMY) {
+ dev_err(&bd->dev, "set brightness failed\n");
+ ret = -ETIMEDOUT;
+ }
+
+ /*
+ * If infact we get an TXDUMMY as output we are happy and dont
+ * make any further comments about it
+ */
+out:
+ jornada_ssp_end();
+
+ return ret;
+}
+
+static const struct backlight_ops jornada_bl_ops = {
+ .get_brightness = jornada_bl_get_brightness,
+ .update_status = jornada_bl_update_status,
+ .options = BL_CORE_SUSPENDRESUME,
+};
+
+static int jornada_bl_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ int ret;
+ struct backlight_device *bd;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = BL_MAX_BRIGHT;
+
+ bd = devm_backlight_device_register(&pdev->dev, S1D_DEVICENAME,
+ &pdev->dev, NULL, &jornada_bl_ops,
+ &props);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ dev_err(&pdev->dev, "failed to register device, err=%x\n", ret);
+ return ret;
+ }
+
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.brightness = BL_DEF_BRIGHT;
+ /*
+ * note. make sure max brightness is set otherwise
+ * you will get seemingly non-related errors when
+ * trying to change brightness
+ */
+ jornada_bl_update_status(bd);
+
+ platform_set_drvdata(pdev, bd);
+ dev_info(&pdev->dev, "HP Jornada 700 series backlight driver\n");
+
+ return 0;
+}
+
+static struct platform_driver jornada_bl_driver = {
+ .probe = jornada_bl_probe,
+ .driver = {
+ .name = "jornada_bl",
+ },
+};
+
+module_platform_driver(jornada_bl_driver);
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 Backlight driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/jornada720_lcd.c b/drivers/video/backlight/jornada720_lcd.c
new file mode 100644
index 00000000000..da3876c9b3a
--- /dev/null
+++ b/drivers/video/backlight/jornada720_lcd.c
@@ -0,0 +1,134 @@
+/*
+ *
+ * LCD driver for HP Jornada 700 series (710/720/728)
+ * Copyright (C) 2006-2009 Kristoffer Ericson <kristoffer.ericson@gmail.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 or any later version as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <mach/jornada720.h>
+#include <mach/hardware.h>
+
+#include <video/s1d13xxxfb.h>
+
+#define LCD_MAX_CONTRAST 0xff
+#define LCD_DEF_CONTRAST 0x80
+
+static int jornada_lcd_get_power(struct lcd_device *ld)
+{
+ /* LDD2 in PPC = LCD POWER */
+ if (PPSR & PPC_LDD2)
+ return FB_BLANK_UNBLANK; /* PW ON */
+ else
+ return FB_BLANK_POWERDOWN; /* PW OFF */
+}
+
+static int jornada_lcd_get_contrast(struct lcd_device *ld)
+{
+ int ret;
+
+ if (jornada_lcd_get_power(ld) != FB_BLANK_UNBLANK)
+ return 0;
+
+ jornada_ssp_start();
+
+ if (jornada_ssp_byte(GETCONTRAST) != TXDUMMY) {
+ dev_err(&ld->dev, "get contrast failed\n");
+ jornada_ssp_end();
+ return -ETIMEDOUT;
+ } else {
+ ret = jornada_ssp_byte(TXDUMMY);
+ jornada_ssp_end();
+ return ret;
+ }
+}
+
+static int jornada_lcd_set_contrast(struct lcd_device *ld, int value)
+{
+ int ret;
+
+ jornada_ssp_start();
+
+ /* start by sending our set contrast cmd to mcu */
+ ret = jornada_ssp_byte(SETCONTRAST);
+
+ /* push the new value */
+ if (jornada_ssp_byte(value) != TXDUMMY) {
+ dev_err(&ld->dev, "set contrast failed\n");
+ jornada_ssp_end();
+ return -ETIMEDOUT;
+ }
+
+ /* if we get here we can assume everything went well */
+ jornada_ssp_end();
+
+ return 0;
+}
+
+static int jornada_lcd_set_power(struct lcd_device *ld, int power)
+{
+ if (power != FB_BLANK_UNBLANK) {
+ PPSR &= ~PPC_LDD2;
+ PPDR |= PPC_LDD2;
+ } else {
+ PPSR |= PPC_LDD2;
+ }
+
+ return 0;
+}
+
+static struct lcd_ops jornada_lcd_props = {
+ .get_contrast = jornada_lcd_get_contrast,
+ .set_contrast = jornada_lcd_set_contrast,
+ .get_power = jornada_lcd_get_power,
+ .set_power = jornada_lcd_set_power,
+};
+
+static int jornada_lcd_probe(struct platform_device *pdev)
+{
+ struct lcd_device *lcd_device;
+ int ret;
+
+ lcd_device = devm_lcd_device_register(&pdev->dev, S1D_DEVICENAME,
+ &pdev->dev, NULL, &jornada_lcd_props);
+
+ if (IS_ERR(lcd_device)) {
+ ret = PTR_ERR(lcd_device);
+ dev_err(&pdev->dev, "failed to register device\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lcd_device);
+
+ /* lets set our default values */
+ jornada_lcd_set_contrast(lcd_device, LCD_DEF_CONTRAST);
+ jornada_lcd_set_power(lcd_device, FB_BLANK_UNBLANK);
+ /* give it some time to startup */
+ msleep(100);
+
+ return 0;
+}
+
+static struct platform_driver jornada_lcd_driver = {
+ .probe = jornada_lcd_probe,
+ .driver = {
+ .name = "jornada_lcd",
+ },
+};
+
+module_platform_driver(jornada_lcd_driver);
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 LCD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/kb3886_bl.c b/drivers/video/backlight/kb3886_bl.c
new file mode 100644
index 00000000000..84a110a719c
--- /dev/null
+++ b/drivers/video/backlight/kb3886_bl.c
@@ -0,0 +1,197 @@
+/*
+ * Backlight Driver for the KB3886 Backlight
+ *
+ * Copyright (c) 2007-2008 Claudio Nieder
+ *
+ * Based on corgi_bl.c by Richard Purdie and kb3886 driver by Robert Woerle
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+
+#define KB3886_PARENT 0x64
+#define KB3886_IO 0x60
+#define KB3886_ADC_DAC_PWM 0xC4
+#define KB3886_PWM0_WRITE 0x81
+#define KB3886_PWM0_READ 0x41
+
+static DEFINE_MUTEX(bl_mutex);
+
+static void kb3886_bl_set_intensity(int intensity)
+{
+ mutex_lock(&bl_mutex);
+ intensity = intensity&0xff;
+ outb(KB3886_ADC_DAC_PWM, KB3886_PARENT);
+ usleep_range(10000, 11000);
+ outb(KB3886_PWM0_WRITE, KB3886_IO);
+ usleep_range(10000, 11000);
+ outb(intensity, KB3886_IO);
+ mutex_unlock(&bl_mutex);
+}
+
+struct kb3886bl_machinfo {
+ int max_intensity;
+ int default_intensity;
+ int limit_mask;
+ void (*set_bl_intensity)(int intensity);
+};
+
+static struct kb3886bl_machinfo kb3886_bl_machinfo = {
+ .max_intensity = 0xff,
+ .default_intensity = 0xa0,
+ .limit_mask = 0x7f,
+ .set_bl_intensity = kb3886_bl_set_intensity,
+};
+
+static struct platform_device kb3886bl_device = {
+ .name = "kb3886-bl",
+ .dev = {
+ .platform_data = &kb3886_bl_machinfo,
+ },
+ .id = -1,
+};
+
+static struct platform_device *devices[] __initdata = {
+ &kb3886bl_device,
+};
+
+/*
+ * Back to driver
+ */
+
+static int kb3886bl_intensity;
+static struct backlight_device *kb3886_backlight_device;
+static struct kb3886bl_machinfo *bl_machinfo;
+
+static unsigned long kb3886bl_flags;
+#define KB3886BL_SUSPENDED 0x01
+
+static struct dmi_system_id kb3886bl_device_table[] __initdata = {
+ {
+ .ident = "Sahara Touch-iT",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SDV"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iTouch T201"),
+ },
+ },
+ { }
+};
+
+static int kb3886bl_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (kb3886bl_flags & KB3886BL_SUSPENDED)
+ intensity = 0;
+
+ bl_machinfo->set_bl_intensity(intensity);
+
+ kb3886bl_intensity = intensity;
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kb3886bl_suspend(struct device *dev)
+{
+ struct backlight_device *bd = dev_get_drvdata(dev);
+
+ kb3886bl_flags |= KB3886BL_SUSPENDED;
+ backlight_update_status(bd);
+ return 0;
+}
+
+static int kb3886bl_resume(struct device *dev)
+{
+ struct backlight_device *bd = dev_get_drvdata(dev);
+
+ kb3886bl_flags &= ~KB3886BL_SUSPENDED;
+ backlight_update_status(bd);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(kb3886bl_pm_ops, kb3886bl_suspend, kb3886bl_resume);
+
+static int kb3886bl_get_intensity(struct backlight_device *bd)
+{
+ return kb3886bl_intensity;
+}
+
+static const struct backlight_ops kb3886bl_ops = {
+ .get_brightness = kb3886bl_get_intensity,
+ .update_status = kb3886bl_send_intensity,
+};
+
+static int kb3886bl_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ struct kb3886bl_machinfo *machinfo = dev_get_platdata(&pdev->dev);
+
+ bl_machinfo = machinfo;
+ if (!machinfo->limit_mask)
+ machinfo->limit_mask = -1;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = machinfo->max_intensity;
+ kb3886_backlight_device = devm_backlight_device_register(&pdev->dev,
+ "kb3886-bl", &pdev->dev,
+ NULL, &kb3886bl_ops,
+ &props);
+ if (IS_ERR(kb3886_backlight_device))
+ return PTR_ERR(kb3886_backlight_device);
+
+ platform_set_drvdata(pdev, kb3886_backlight_device);
+
+ kb3886_backlight_device->props.power = FB_BLANK_UNBLANK;
+ kb3886_backlight_device->props.brightness = machinfo->default_intensity;
+ backlight_update_status(kb3886_backlight_device);
+
+ return 0;
+}
+
+static struct platform_driver kb3886bl_driver = {
+ .probe = kb3886bl_probe,
+ .driver = {
+ .name = "kb3886-bl",
+ .pm = &kb3886bl_pm_ops,
+ },
+};
+
+static int __init kb3886_init(void)
+{
+ if (!dmi_check_system(kb3886bl_device_table))
+ return -ENODEV;
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+ return platform_driver_register(&kb3886bl_driver);
+}
+
+static void __exit kb3886_exit(void)
+{
+ platform_driver_unregister(&kb3886bl_driver);
+}
+
+module_init(kb3886_init);
+module_exit(kb3886_exit);
+
+MODULE_AUTHOR("Claudio Nieder <private@claudio.ch>");
+MODULE_DESCRIPTION("Tabletkiosk Sahara Touch-iT Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("dmi:*:svnSDV:pniTouchT201:*");
diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c
new file mode 100644
index 00000000000..5fa2649c963
--- /dev/null
+++ b/drivers/video/backlight/l4f00242t03.c
@@ -0,0 +1,269 @@
+/*
+ * l4f00242t03.c -- support for Epson L4F00242T03 LCD
+ *
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Copyright (c) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+ * Inspired by Marek Vasut work in l4f00242t03.c
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/l4f00242t03.h>
+
+struct l4f00242t03_priv {
+ struct spi_device *spi;
+ struct lcd_device *ld;
+ int lcd_state;
+ struct regulator *io_reg;
+ struct regulator *core_reg;
+};
+
+static void l4f00242t03_reset(unsigned int gpio)
+{
+ pr_debug("l4f00242t03_reset.\n");
+ gpio_set_value(gpio, 1);
+ mdelay(100);
+ gpio_set_value(gpio, 0);
+ mdelay(10); /* tRES >= 100us */
+ gpio_set_value(gpio, 1);
+ mdelay(20);
+}
+
+#define param(x) ((x) | 0x100)
+
+static void l4f00242t03_lcd_init(struct spi_device *spi)
+{
+ struct l4f00242t03_pdata *pdata = dev_get_platdata(&spi->dev);
+ struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
+ const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) };
+ int ret;
+
+ dev_dbg(&spi->dev, "initializing LCD\n");
+
+ ret = regulator_set_voltage(priv->io_reg, 1800000, 1800000);
+ if (ret) {
+ dev_err(&spi->dev, "failed to set the IO regulator voltage.\n");
+ return;
+ }
+ ret = regulator_enable(priv->io_reg);
+ if (ret) {
+ dev_err(&spi->dev, "failed to enable the IO regulator.\n");
+ return;
+ }
+
+ ret = regulator_set_voltage(priv->core_reg, 2800000, 2800000);
+ if (ret) {
+ dev_err(&spi->dev, "failed to set the core regulator voltage.\n");
+ regulator_disable(priv->io_reg);
+ return;
+ }
+ ret = regulator_enable(priv->core_reg);
+ if (ret) {
+ dev_err(&spi->dev, "failed to enable the core regulator.\n");
+ regulator_disable(priv->io_reg);
+ return;
+ }
+
+ l4f00242t03_reset(pdata->reset_gpio);
+
+ gpio_set_value(pdata->data_enable_gpio, 1);
+ msleep(60);
+ spi_write(spi, (const u8 *)cmd, ARRAY_SIZE(cmd) * sizeof(u16));
+}
+
+static void l4f00242t03_lcd_powerdown(struct spi_device *spi)
+{
+ struct l4f00242t03_pdata *pdata = dev_get_platdata(&spi->dev);
+ struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
+
+ dev_dbg(&spi->dev, "Powering down LCD\n");
+
+ gpio_set_value(pdata->data_enable_gpio, 0);
+
+ regulator_disable(priv->io_reg);
+ regulator_disable(priv->core_reg);
+}
+
+static int l4f00242t03_lcd_power_get(struct lcd_device *ld)
+{
+ struct l4f00242t03_priv *priv = lcd_get_data(ld);
+
+ return priv->lcd_state;
+}
+
+static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power)
+{
+ struct l4f00242t03_priv *priv = lcd_get_data(ld);
+ struct spi_device *spi = priv->spi;
+
+ const u16 slpout = 0x11;
+ const u16 dison = 0x29;
+
+ const u16 slpin = 0x10;
+ const u16 disoff = 0x28;
+
+ if (power <= FB_BLANK_NORMAL) {
+ if (priv->lcd_state <= FB_BLANK_NORMAL) {
+ /* Do nothing, the LCD is running */
+ } else if (priv->lcd_state < FB_BLANK_POWERDOWN) {
+ dev_dbg(&spi->dev, "Resuming LCD\n");
+
+ spi_write(spi, (const u8 *)&slpout, sizeof(u16));
+ msleep(60);
+ spi_write(spi, (const u8 *)&dison, sizeof(u16));
+ } else {
+ /* priv->lcd_state == FB_BLANK_POWERDOWN */
+ l4f00242t03_lcd_init(spi);
+ priv->lcd_state = FB_BLANK_VSYNC_SUSPEND;
+ l4f00242t03_lcd_power_set(priv->ld, power);
+ }
+ } else if (power < FB_BLANK_POWERDOWN) {
+ if (priv->lcd_state <= FB_BLANK_NORMAL) {
+ /* Send the display in standby */
+ dev_dbg(&spi->dev, "Standby the LCD\n");
+
+ spi_write(spi, (const u8 *)&disoff, sizeof(u16));
+ msleep(60);
+ spi_write(spi, (const u8 *)&slpin, sizeof(u16));
+ } else if (priv->lcd_state < FB_BLANK_POWERDOWN) {
+ /* Do nothing, the LCD is already in standby */
+ } else {
+ /* priv->lcd_state == FB_BLANK_POWERDOWN */
+ l4f00242t03_lcd_init(spi);
+ priv->lcd_state = FB_BLANK_UNBLANK;
+ l4f00242t03_lcd_power_set(ld, power);
+ }
+ } else {
+ /* power == FB_BLANK_POWERDOWN */
+ if (priv->lcd_state != FB_BLANK_POWERDOWN) {
+ /* Clear the screen before shutting down */
+ spi_write(spi, (const u8 *)&disoff, sizeof(u16));
+ msleep(60);
+ l4f00242t03_lcd_powerdown(spi);
+ }
+ }
+
+ priv->lcd_state = power;
+
+ return 0;
+}
+
+static struct lcd_ops l4f_ops = {
+ .set_power = l4f00242t03_lcd_power_set,
+ .get_power = l4f00242t03_lcd_power_get,
+};
+
+static int l4f00242t03_probe(struct spi_device *spi)
+{
+ struct l4f00242t03_priv *priv;
+ struct l4f00242t03_pdata *pdata = dev_get_platdata(&spi->dev);
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&spi->dev, "Uninitialized platform data.\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&spi->dev, sizeof(struct l4f00242t03_priv),
+ GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, priv);
+ spi->bits_per_word = 9;
+ spi_setup(spi);
+
+ priv->spi = spi;
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->reset_gpio,
+ GPIOF_OUT_INIT_HIGH, "lcd l4f00242t03 reset");
+ if (ret) {
+ dev_err(&spi->dev,
+ "Unable to get the lcd l4f00242t03 reset gpio.\n");
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->data_enable_gpio,
+ GPIOF_OUT_INIT_LOW, "lcd l4f00242t03 data enable");
+ if (ret) {
+ dev_err(&spi->dev,
+ "Unable to get the lcd l4f00242t03 data en gpio.\n");
+ return ret;
+ }
+
+ priv->io_reg = devm_regulator_get(&spi->dev, "vdd");
+ if (IS_ERR(priv->io_reg)) {
+ dev_err(&spi->dev, "%s: Unable to get the IO regulator\n",
+ __func__);
+ return PTR_ERR(priv->io_reg);
+ }
+
+ priv->core_reg = devm_regulator_get(&spi->dev, "vcore");
+ if (IS_ERR(priv->core_reg)) {
+ dev_err(&spi->dev, "%s: Unable to get the core regulator\n",
+ __func__);
+ return PTR_ERR(priv->core_reg);
+ }
+
+ priv->ld = devm_lcd_device_register(&spi->dev, "l4f00242t03", &spi->dev,
+ priv, &l4f_ops);
+ if (IS_ERR(priv->ld))
+ return PTR_ERR(priv->ld);
+
+ /* Init the LCD */
+ l4f00242t03_lcd_init(spi);
+ priv->lcd_state = FB_BLANK_VSYNC_SUSPEND;
+ l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_UNBLANK);
+
+ dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n");
+
+ return 0;
+}
+
+static int l4f00242t03_remove(struct spi_device *spi)
+{
+ struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
+
+ l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+static void l4f00242t03_shutdown(struct spi_device *spi)
+{
+ struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
+
+ if (priv)
+ l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
+
+}
+
+static struct spi_driver l4f00242t03_driver = {
+ .driver = {
+ .name = "l4f00242t03",
+ .owner = THIS_MODULE,
+ },
+ .probe = l4f00242t03_probe,
+ .remove = l4f00242t03_remove,
+ .shutdown = l4f00242t03_shutdown,
+};
+
+module_spi_driver(l4f00242t03_driver);
+
+MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>");
+MODULE_DESCRIPTION("EPSON L4F00242T03 LCD");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c
index b15b2b84a6f..7de847df224 100644
--- a/drivers/video/backlight/lcd.c
+++ b/drivers/video/backlight/lcd.c
@@ -5,6 +5,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -13,6 +15,7 @@
#include <linux/ctype.h>
#include <linux/err.h>
#include <linux/fb.h>
+#include <linux/slab.h>
#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
defined(CONFIG_LCD_CLASS_DEVICE_MODULE))
@@ -27,21 +30,46 @@ static int fb_notifier_callback(struct notifier_block *self,
struct fb_event *evdata = data;
/* If we aren't interested in this event, skip it immediately ... */
- if (event != FB_EVENT_BLANK)
+ switch (event) {
+ case FB_EVENT_BLANK:
+ case FB_EVENT_MODE_CHANGE:
+ case FB_EVENT_MODE_CHANGE_ALL:
+ case FB_EARLY_EVENT_BLANK:
+ case FB_R_EARLY_EVENT_BLANK:
+ break;
+ default:
return 0;
+ }
ld = container_of(self, struct lcd_device, fb_notif);
+ if (!ld->ops)
+ return 0;
+
mutex_lock(&ld->ops_lock);
- if (ld->ops)
- if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info))
- ld->ops->set_power(ld, *(int *)evdata->data);
+ if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) {
+ if (event == FB_EVENT_BLANK) {
+ if (ld->ops->set_power)
+ ld->ops->set_power(ld, *(int *)evdata->data);
+ } else if (event == FB_EARLY_EVENT_BLANK) {
+ if (ld->ops->early_set_power)
+ ld->ops->early_set_power(ld,
+ *(int *)evdata->data);
+ } else if (event == FB_R_EARLY_EVENT_BLANK) {
+ if (ld->ops->r_early_set_power)
+ ld->ops->r_early_set_power(ld,
+ *(int *)evdata->data);
+ } else {
+ if (ld->ops->set_mode)
+ ld->ops->set_mode(ld, evdata->data);
+ }
+ }
mutex_unlock(&ld->ops_lock);
return 0;
}
static int lcd_register_fb(struct lcd_device *ld)
{
- memset(&ld->fb_notif, 0, sizeof(&ld->fb_notif));
+ memset(&ld->fb_notif, 0, sizeof(ld->fb_notif));
ld->fb_notif.notifier_call = fb_notifier_callback;
return fb_register_client(&ld->fb_notif);
}
@@ -61,7 +89,7 @@ static inline void lcd_unregister_fb(struct lcd_device *ld)
}
#endif /* CONFIG_FB */
-static ssize_t lcd_show_power(struct device *dev, struct device_attribute *attr,
+static ssize_t lcd_power_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int rc;
@@ -77,23 +105,22 @@ static ssize_t lcd_show_power(struct device *dev, struct device_attribute *attr,
return rc;
}
-static ssize_t lcd_store_power(struct device *dev,
+static ssize_t lcd_power_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- int rc = -ENXIO;
- char *endp;
+ int rc;
struct lcd_device *ld = to_lcd_device(dev);
- int power = simple_strtoul(buf, &endp, 0);
- size_t size = endp - buf;
+ unsigned long power;
+
+ rc = kstrtoul(buf, 0, &power);
+ if (rc)
+ return rc;
- if (*endp && isspace(*endp))
- size++;
- if (size != count)
- return -EINVAL;
+ rc = -ENXIO;
mutex_lock(&ld->ops_lock);
if (ld->ops && ld->ops->set_power) {
- pr_debug("lcd: set power to %d\n", power);
+ pr_debug("set power to %lu\n", power);
ld->ops->set_power(ld, power);
rc = count;
}
@@ -101,8 +128,9 @@ static ssize_t lcd_store_power(struct device *dev,
return rc;
}
+static DEVICE_ATTR_RW(lcd_power);
-static ssize_t lcd_show_contrast(struct device *dev,
+static ssize_t contrast_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc = -ENXIO;
@@ -116,23 +144,22 @@ static ssize_t lcd_show_contrast(struct device *dev,
return rc;
}
-static ssize_t lcd_store_contrast(struct device *dev,
+static ssize_t contrast_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- int rc = -ENXIO;
- char *endp;
+ int rc;
struct lcd_device *ld = to_lcd_device(dev);
- int contrast = simple_strtoul(buf, &endp, 0);
- size_t size = endp - buf;
+ unsigned long contrast;
+
+ rc = kstrtoul(buf, 0, &contrast);
+ if (rc)
+ return rc;
- if (*endp && isspace(*endp))
- size++;
- if (size != count)
- return -EINVAL;
+ rc = -ENXIO;
mutex_lock(&ld->ops_lock);
if (ld->ops && ld->ops->set_contrast) {
- pr_debug("lcd: set contrast to %d\n", contrast);
+ pr_debug("set contrast to %lu\n", contrast);
ld->ops->set_contrast(ld, contrast);
rc = count;
}
@@ -140,14 +167,16 @@ static ssize_t lcd_store_contrast(struct device *dev,
return rc;
}
+static DEVICE_ATTR_RW(contrast);
-static ssize_t lcd_show_max_contrast(struct device *dev,
+static ssize_t max_contrast_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lcd_device *ld = to_lcd_device(dev);
return sprintf(buf, "%d\n", ld->props.max_contrast);
}
+static DEVICE_ATTR_RO(max_contrast);
static struct class *lcd_class;
@@ -157,12 +186,13 @@ static void lcd_device_release(struct device *dev)
kfree(ld);
}
-static struct device_attribute lcd_device_attributes[] = {
- __ATTR(lcd_power, 0644, lcd_show_power, lcd_store_power),
- __ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast),
- __ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL),
- __ATTR_NULL,
+static struct attribute *lcd_device_attrs[] = {
+ &dev_attr_lcd_power.attr,
+ &dev_attr_contrast.attr,
+ &dev_attr_max_contrast.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(lcd_device);
/**
* lcd_device_register - register a new object of lcd_device class.
@@ -193,12 +223,12 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent,
new_ld->dev.class = lcd_class;
new_ld->dev.parent = parent;
new_ld->dev.release = lcd_device_release;
- strlcpy(new_ld->dev.bus_id, name, BUS_ID_SIZE);
+ dev_set_name(&new_ld->dev, "%s", name);
dev_set_drvdata(&new_ld->dev, devdata);
rc = device_register(&new_ld->dev);
if (rc) {
- kfree(new_ld);
+ put_device(&new_ld->dev);
return ERR_PTR(rc);
}
@@ -234,6 +264,76 @@ void lcd_device_unregister(struct lcd_device *ld)
}
EXPORT_SYMBOL(lcd_device_unregister);
+static void devm_lcd_device_release(struct device *dev, void *res)
+{
+ struct lcd_device *lcd = *(struct lcd_device **)res;
+
+ lcd_device_unregister(lcd);
+}
+
+static int devm_lcd_device_match(struct device *dev, void *res, void *data)
+{
+ struct lcd_device **r = res;
+
+ return *r == data;
+}
+
+/**
+ * devm_lcd_device_register - resource managed lcd_device_register()
+ * @dev: the device to register
+ * @name: the name of the device
+ * @parent: a pointer to the parent device
+ * @devdata: an optional pointer to be stored for private driver use
+ * @ops: the lcd operations structure
+ *
+ * @return a struct lcd on success, or an ERR_PTR on error
+ *
+ * Managed lcd_device_register(). The lcd_device returned from this function
+ * are automatically freed on driver detach. See lcd_device_register()
+ * for more information.
+ */
+struct lcd_device *devm_lcd_device_register(struct device *dev,
+ const char *name, struct device *parent,
+ void *devdata, struct lcd_ops *ops)
+{
+ struct lcd_device **ptr, *lcd;
+
+ ptr = devres_alloc(devm_lcd_device_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ lcd = lcd_device_register(name, parent, devdata, ops);
+ if (!IS_ERR(lcd)) {
+ *ptr = lcd;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return lcd;
+}
+EXPORT_SYMBOL(devm_lcd_device_register);
+
+/**
+ * devm_lcd_device_unregister - resource managed lcd_device_unregister()
+ * @dev: the device to unregister
+ * @ld: the lcd device to unregister
+ *
+ * Deallocated a lcd allocated with devm_lcd_device_register(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_lcd_device_unregister(struct device *dev, struct lcd_device *ld)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_lcd_device_release,
+ devm_lcd_device_match, ld);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_lcd_device_unregister);
+
+
static void __exit lcd_class_exit(void)
{
class_destroy(lcd_class);
@@ -243,12 +343,12 @@ static int __init lcd_class_init(void)
{
lcd_class = class_create(THIS_MODULE, "lcd");
if (IS_ERR(lcd_class)) {
- printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
- PTR_ERR(lcd_class));
+ pr_warn("Unable to create backlight class; errno = %ld\n",
+ PTR_ERR(lcd_class));
return PTR_ERR(lcd_class);
}
- lcd_class->dev_attrs = lcd_device_attributes;
+ lcd_class->dev_groups = lcd_device_groups;
return 0;
}
diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c
new file mode 100644
index 00000000000..506a6c23603
--- /dev/null
+++ b/drivers/video/backlight/ld9040.c
@@ -0,0 +1,818 @@
+/*
+ * ld9040 AMOLED LCD panel driver.
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ * Derived from drivers/video/backlight/s6e63m0.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.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+
+#include "ld9040_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 24
+
+struct ld9040 {
+ struct device *dev;
+ struct spi_device *spi;
+ unsigned int power;
+ unsigned int current_brightness;
+
+ struct lcd_device *ld;
+ struct backlight_device *bd;
+ struct lcd_platform_data *lcd_pd;
+
+ struct mutex lock;
+ bool enabled;
+};
+
+static struct regulator_bulk_data supplies[] = {
+ { .supply = "vdd3", },
+ { .supply = "vci", },
+};
+
+static void ld9040_regulator_enable(struct ld9040 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd = NULL;
+
+ pd = lcd->lcd_pd;
+ mutex_lock(&lcd->lock);
+ if (!lcd->enabled) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ goto out;
+
+ lcd->enabled = true;
+ }
+ msleep(pd->power_on_delay);
+out:
+ mutex_unlock(&lcd->lock);
+}
+
+static void ld9040_regulator_disable(struct ld9040 *lcd)
+{
+ int ret = 0;
+
+ mutex_lock(&lcd->lock);
+ if (lcd->enabled) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ goto out;
+
+ lcd->enabled = false;
+ }
+out:
+ mutex_unlock(&lcd->lock);
+}
+
+static const unsigned short seq_swreset[] = {
+ 0x01, COMMAND_ONLY,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_user_setting[] = {
+ 0xF0, 0x5A,
+
+ DATA_ONLY, 0x5A,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_elvss_on[] = {
+ 0xB1, 0x0D,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x16,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_gtcon[] = {
+ 0xF7, 0x09,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_panel_condition[] = {
+ 0xF8, 0x05,
+
+ DATA_ONLY, 0x65,
+ DATA_ONLY, 0x96,
+ DATA_ONLY, 0x71,
+ DATA_ONLY, 0x7D,
+ DATA_ONLY, 0x19,
+ DATA_ONLY, 0x3B,
+ DATA_ONLY, 0x0D,
+ DATA_ONLY, 0x19,
+ DATA_ONLY, 0x7E,
+ DATA_ONLY, 0x0D,
+ DATA_ONLY, 0xE2,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x7E,
+ DATA_ONLY, 0x7D,
+ DATA_ONLY, 0x07,
+ DATA_ONLY, 0x07,
+ DATA_ONLY, 0x20,
+ DATA_ONLY, 0x20,
+ DATA_ONLY, 0x20,
+ DATA_ONLY, 0x02,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_gamma_set1[] = {
+ 0xF9, 0x00,
+
+ DATA_ONLY, 0xA7,
+ DATA_ONLY, 0xB4,
+ DATA_ONLY, 0xAE,
+ DATA_ONLY, 0xBF,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x91,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0xB2,
+ DATA_ONLY, 0xB4,
+ DATA_ONLY, 0xAA,
+ DATA_ONLY, 0xBB,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0xAC,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0xB3,
+ DATA_ONLY, 0xB1,
+ DATA_ONLY, 0xAA,
+ DATA_ONLY, 0xBC,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0xB3,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_gamma_ctrl[] = {
+ 0xFB, 0x02,
+
+ DATA_ONLY, 0x5A,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_gamma_start[] = {
+ 0xF9, COMMAND_ONLY,
+
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_apon[] = {
+ 0xF3, 0x00,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x0A,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_display_ctrl[] = {
+ 0xF2, 0x02,
+
+ DATA_ONLY, 0x08,
+ DATA_ONLY, 0x08,
+ DATA_ONLY, 0x10,
+ DATA_ONLY, 0x10,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_manual_pwr[] = {
+ 0xB0, 0x04,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_pwr_ctrl[] = {
+ 0xF4, 0x0A,
+
+ DATA_ONLY, 0x87,
+ DATA_ONLY, 0x25,
+ DATA_ONLY, 0x6A,
+ DATA_ONLY, 0x44,
+ DATA_ONLY, 0x02,
+ DATA_ONLY, 0x88,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_sleep_out[] = {
+ 0x11, COMMAND_ONLY,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_sleep_in[] = {
+ 0x10, COMMAND_ONLY,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_display_on[] = {
+ 0x29, COMMAND_ONLY,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_display_off[] = {
+ 0x28, COMMAND_ONLY,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vci1_1st_en[] = {
+ 0xF3, 0x10,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vl1_en[] = {
+ 0xF3, 0x11,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vl2_en[] = {
+ 0xF3, 0x13,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vci1_2nd_en[] = {
+ 0xF3, 0x33,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vl3_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vreg1_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0x01,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vgh_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0x11,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vgl_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0x31,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x02,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vmos_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xB1,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vint_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xF1,
+ /* DATA_ONLY, 0x71, VMOS/VBL/VBH not used */
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vbh_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xF9,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_vbl_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xFD,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_gam_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xFF,
+ /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_sd_amp_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xFF,
+ /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
+ DATA_ONLY, 0x80,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_gls_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xFF,
+ /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
+ DATA_ONLY, 0x81,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_els_en[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xFF,
+ /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
+ DATA_ONLY, 0x83,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
+ ENDDEF, 0x00
+};
+
+static const unsigned short seq_el_on[] = {
+ 0xF3, 0x37,
+
+ DATA_ONLY, 0xFF,
+ /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
+ DATA_ONLY, 0x87,
+ DATA_ONLY, 0x00,
+ DATA_ONLY, 0x03,
+ /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
+ ENDDEF, 0x00
+};
+
+static int ld9040_spi_write_byte(struct ld9040 *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 ld9040_spi_write(struct ld9040 *lcd, unsigned char address,
+ unsigned char command)
+{
+ int ret = 0;
+
+ if (address != DATA_ONLY)
+ ret = ld9040_spi_write_byte(lcd, 0x0, address);
+ if (command != COMMAND_ONLY)
+ ret = ld9040_spi_write_byte(lcd, 0x1, command);
+
+ return ret;
+}
+
+static int ld9040_panel_send_sequence(struct ld9040 *lcd,
+ const unsigned short *wbuf)
+{
+ int ret = 0, i = 0;
+
+ while ((wbuf[i] & DEFMASK) != ENDDEF) {
+ if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
+ ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]);
+ if (ret)
+ break;
+ } else {
+ msleep(wbuf[i+1]);
+ }
+ i += 2;
+ }
+
+ return ret;
+}
+
+static int _ld9040_gamma_ctl(struct ld9040 *lcd, const unsigned int *gamma)
+{
+ unsigned int i = 0;
+ int ret = 0;
+
+ /* start gamma table updating. */
+ ret = ld9040_panel_send_sequence(lcd, seq_gamma_start);
+ 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 = ld9040_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 = ld9040_panel_send_sequence(lcd, seq_gamma_ctrl);
+ if (ret)
+ dev_err(lcd->dev, "failed to update gamma table.\n");
+
+gamma_err:
+ return ret;
+}
+
+static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma)
+{
+ return _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
+}
+
+static int ld9040_ldi_init(struct ld9040 *lcd)
+{
+ int ret, i;
+ static const unsigned short *init_seq[] = {
+ seq_user_setting,
+ seq_panel_condition,
+ seq_display_ctrl,
+ seq_manual_pwr,
+ seq_elvss_on,
+ seq_gtcon,
+ seq_gamma_set1,
+ seq_gamma_ctrl,
+ seq_sleep_out,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = ld9040_panel_send_sequence(lcd, init_seq[i]);
+ /* workaround: minimum delay time for transferring CMD */
+ usleep_range(300, 310);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ld9040_ldi_enable(struct ld9040 *lcd)
+{
+ return ld9040_panel_send_sequence(lcd, seq_display_on);
+}
+
+static int ld9040_ldi_disable(struct ld9040 *lcd)
+{
+ int ret;
+
+ ret = ld9040_panel_send_sequence(lcd, seq_display_off);
+ ret = ld9040_panel_send_sequence(lcd, seq_sleep_in);
+
+ return ret;
+}
+
+static int ld9040_power_is_on(int power)
+{
+ return power <= FB_BLANK_NORMAL;
+}
+
+static int ld9040_power_on(struct ld9040 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd;
+
+ pd = lcd->lcd_pd;
+
+ /* lcd power on */
+ ld9040_regulator_enable(lcd);
+
+ if (!pd->reset) {
+ dev_err(lcd->dev, "reset is NULL.\n");
+ return -EINVAL;
+ } else {
+ pd->reset(lcd->ld);
+ msleep(pd->reset_delay);
+ }
+
+ ret = ld9040_ldi_init(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to initialize ldi.\n");
+ return ret;
+ }
+
+ ret = ld9040_ldi_enable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to enable ldi.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ld9040_power_off(struct ld9040 *lcd)
+{
+ int ret;
+ struct lcd_platform_data *pd;
+
+ pd = lcd->lcd_pd;
+
+ ret = ld9040_ldi_disable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "lcd setting failed.\n");
+ return -EIO;
+ }
+
+ msleep(pd->power_off_delay);
+
+ /* lcd power off */
+ ld9040_regulator_disable(lcd);
+
+ return 0;
+}
+
+static int ld9040_power(struct ld9040 *lcd, int power)
+{
+ int ret = 0;
+
+ if (ld9040_power_is_on(power) && !ld9040_power_is_on(lcd->power))
+ ret = ld9040_power_on(lcd);
+ else if (!ld9040_power_is_on(power) && ld9040_power_is_on(lcd->power))
+ ret = ld9040_power_off(lcd);
+
+ if (!ret)
+ lcd->power = power;
+
+ return ret;
+}
+
+static int ld9040_set_power(struct lcd_device *ld, int power)
+{
+ struct ld9040 *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 ld9040_power(lcd, power);
+}
+
+static int ld9040_get_power(struct lcd_device *ld)
+{
+ struct ld9040 *lcd = lcd_get_data(ld);
+
+ return lcd->power;
+}
+
+static int ld9040_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int ld9040_set_brightness(struct backlight_device *bd)
+{
+ int ret = 0, brightness = bd->props.brightness;
+ struct ld9040 *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 = ld9040_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 ld9040_lcd_ops = {
+ .set_power = ld9040_set_power,
+ .get_power = ld9040_get_power,
+};
+
+static const struct backlight_ops ld9040_backlight_ops = {
+ .get_brightness = ld9040_get_brightness,
+ .update_status = ld9040_set_brightness,
+};
+
+static int ld9040_probe(struct spi_device *spi)
+{
+ int ret = 0;
+ struct ld9040 *lcd = NULL;
+ struct lcd_device *ld = NULL;
+ struct backlight_device *bd = NULL;
+ struct backlight_properties props;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(struct ld9040), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ /* ld9040 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");
+ return ret;
+ }
+
+ lcd->spi = spi;
+ lcd->dev = &spi->dev;
+
+ lcd->lcd_pd = dev_get_platdata(&spi->dev);
+ if (!lcd->lcd_pd) {
+ dev_err(&spi->dev, "platform data is NULL.\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&lcd->lock);
+
+ ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ld = devm_lcd_device_register(&spi->dev, "ld9040", &spi->dev, lcd,
+ &ld9040_lcd_ops);
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
+
+ lcd->ld = ld;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = MAX_BRIGHTNESS;
+
+ bd = devm_backlight_device_register(&spi->dev, "ld9040-bl", &spi->dev,
+ lcd, &ld9040_backlight_ops, &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ bd->props.brightness = MAX_BRIGHTNESS;
+ lcd->bd = bd;
+
+ /*
+ * 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;
+
+ ld9040_power(lcd, FB_BLANK_UNBLANK);
+ } else {
+ lcd->power = FB_BLANK_UNBLANK;
+ }
+
+ spi_set_drvdata(spi, lcd);
+
+ dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
+ return 0;
+}
+
+static int ld9040_remove(struct spi_device *spi)
+{
+ struct ld9040 *lcd = spi_get_drvdata(spi);
+
+ ld9040_power(lcd, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ld9040_suspend(struct device *dev)
+{
+ struct ld9040 *lcd = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "lcd->power = %d\n", lcd->power);
+
+ /*
+ * when lcd panel is suspend, lcd panel becomes off
+ * regardless of status.
+ */
+ return ld9040_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static int ld9040_resume(struct device *dev)
+{
+ struct ld9040 *lcd = dev_get_drvdata(dev);
+
+ lcd->power = FB_BLANK_POWERDOWN;
+
+ return ld9040_power(lcd, FB_BLANK_UNBLANK);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ld9040_pm_ops, ld9040_suspend, ld9040_resume);
+
+/* Power down all displays on reboot, poweroff or halt. */
+static void ld9040_shutdown(struct spi_device *spi)
+{
+ struct ld9040 *lcd = spi_get_drvdata(spi);
+
+ ld9040_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver ld9040_driver = {
+ .driver = {
+ .name = "ld9040",
+ .owner = THIS_MODULE,
+ .pm = &ld9040_pm_ops,
+ },
+ .probe = ld9040_probe,
+ .remove = ld9040_remove,
+ .shutdown = ld9040_shutdown,
+};
+
+module_spi_driver(ld9040_driver);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_DESCRIPTION("ld9040 LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/ld9040_gamma.h b/drivers/video/backlight/ld9040_gamma.h
new file mode 100644
index 00000000000..c5e586d9738
--- /dev/null
+++ b/drivers/video/backlight/ld9040_gamma.h
@@ -0,0 +1,202 @@
+/*
+ * Gamma level definitions.
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ * InKi Dae <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@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 _LD9040_BRIGHTNESS_H
+#define _LD9040_BRIGHTNESS_H
+
+#define MAX_GAMMA_LEVEL 25
+#define GAMMA_TABLE_COUNT 21
+
+/* gamma value: 2.2 */
+static const unsigned int ld9040_22_300[] = {
+ 0x00, 0xa7, 0xb4, 0xae, 0xbf, 0x00, 0x91,
+ 0x00, 0xb2, 0xb4, 0xaa, 0xbb, 0x00, 0xac,
+ 0x00, 0xb3, 0xb1, 0xaa, 0xbc, 0x00, 0xb3
+};
+
+static const unsigned int ld9040_22_290[] = {
+ 0x00, 0xa9, 0xb7, 0xae, 0xbd, 0x00, 0x89,
+ 0x00, 0xb7, 0xb6, 0xa8, 0xba, 0x00, 0xa4,
+ 0x00, 0xb1, 0xb4, 0xaa, 0xbb, 0x00, 0xaa
+};
+
+static const unsigned int ld9040_22_280[] = {
+ 0x00, 0xa9, 0xb6, 0xad, 0xbf, 0x00, 0x86,
+ 0x00, 0xb8, 0xb5, 0xa8, 0xbc, 0x00, 0xa0,
+ 0x00, 0xb3, 0xb3, 0xa9, 0xbc, 0x00, 0xa7
+};
+
+static const unsigned int ld9040_22_270[] = {
+ 0x00, 0xa8, 0xb8, 0xae, 0xbe, 0x00, 0x84,
+ 0x00, 0xb9, 0xb7, 0xa8, 0xbc, 0x00, 0x9d,
+ 0x00, 0xb2, 0xb5, 0xaa, 0xbc, 0x00, 0xa4
+
+};
+static const unsigned int ld9040_22_260[] = {
+ 0x00, 0xa4, 0xb8, 0xb0, 0xbf, 0x00, 0x80,
+ 0x00, 0xb8, 0xb6, 0xaa, 0xbc, 0x00, 0x9a,
+ 0x00, 0xb0, 0xb5, 0xab, 0xbd, 0x00, 0xa0
+};
+
+static const unsigned int ld9040_22_250[] = {
+ 0x00, 0xa4, 0xb9, 0xaf, 0xc1, 0x00, 0x7d,
+ 0x00, 0xb9, 0xb6, 0xaa, 0xbb, 0x00, 0x97,
+ 0x00, 0xb1, 0xb5, 0xaa, 0xbf, 0x00, 0x9d
+};
+
+static const unsigned int ld9040_22_240[] = {
+ 0x00, 0xa2, 0xb9, 0xaf, 0xc2, 0x00, 0x7a,
+ 0x00, 0xb9, 0xb7, 0xaa, 0xbd, 0x00, 0x94,
+ 0x00, 0xb0, 0xb5, 0xab, 0xbf, 0x00, 0x9a
+};
+
+static const unsigned int ld9040_22_230[] = {
+ 0x00, 0xa0, 0xb9, 0xaf, 0xc3, 0x00, 0x77,
+ 0x00, 0xb9, 0xb7, 0xab, 0xbe, 0x00, 0x90,
+ 0x00, 0xb0, 0xb6, 0xab, 0xbf, 0x00, 0x97
+};
+
+static const unsigned int ld9040_22_220[] = {
+ 0x00, 0x9e, 0xba, 0xb0, 0xc2, 0x00, 0x75,
+ 0x00, 0xb9, 0xb8, 0xab, 0xbe, 0x00, 0x8e,
+ 0x00, 0xb0, 0xb6, 0xac, 0xbf, 0x00, 0x94
+};
+
+static const unsigned int ld9040_22_210[] = {
+ 0x00, 0x9c, 0xb9, 0xb0, 0xc4, 0x00, 0x72,
+ 0x00, 0xb8, 0xb8, 0xac, 0xbf, 0x00, 0x8a,
+ 0x00, 0xb0, 0xb6, 0xac, 0xc0, 0x00, 0x91
+};
+
+static const unsigned int ld9040_22_200[] = {
+ 0x00, 0x9a, 0xba, 0xb1, 0xc4, 0x00, 0x6f,
+ 0x00, 0xb8, 0xb8, 0xad, 0xc0, 0x00, 0x86,
+ 0x00, 0xb0, 0xb7, 0xad, 0xc0, 0x00, 0x8d
+};
+
+static const unsigned int ld9040_22_190[] = {
+ 0x00, 0x97, 0xba, 0xb2, 0xc5, 0x00, 0x6c,
+ 0x00, 0xb8, 0xb8, 0xae, 0xc1, 0x00, 0x82,
+ 0x00, 0xb0, 0xb6, 0xae, 0xc2, 0x00, 0x89
+};
+
+static const unsigned int ld9040_22_180[] = {
+ 0x00, 0x93, 0xba, 0xb3, 0xc5, 0x00, 0x69,
+ 0x00, 0xb8, 0xb9, 0xae, 0xc1, 0x00, 0x7f,
+ 0x00, 0xb0, 0xb6, 0xae, 0xc3, 0x00, 0x85
+};
+
+static const unsigned int ld9040_22_170[] = {
+ 0x00, 0x8b, 0xb9, 0xb3, 0xc7, 0x00, 0x65,
+ 0x00, 0xb7, 0xb8, 0xaf, 0xc3, 0x00, 0x7a,
+ 0x00, 0x80, 0xb6, 0xae, 0xc4, 0x00, 0x81
+};
+
+static const unsigned int ld9040_22_160[] = {
+ 0x00, 0x89, 0xba, 0xb3, 0xc8, 0x00, 0x62,
+ 0x00, 0xb6, 0xba, 0xaf, 0xc3, 0x00, 0x76,
+ 0x00, 0xaf, 0xb7, 0xae, 0xc4, 0x00, 0x7e
+};
+
+static const unsigned int ld9040_22_150[] = {
+ 0x00, 0x82, 0xba, 0xb4, 0xc7, 0x00, 0x5f,
+ 0x00, 0xb5, 0xba, 0xb0, 0xc3, 0x00, 0x72,
+ 0x00, 0xae, 0xb8, 0xb0, 0xc3, 0x00, 0x7a
+};
+
+static const unsigned int ld9040_22_140[] = {
+ 0x00, 0x7b, 0xbb, 0xb4, 0xc8, 0x00, 0x5b,
+ 0x00, 0xb5, 0xba, 0xb1, 0xc4, 0x00, 0x6e,
+ 0x00, 0xae, 0xb9, 0xb0, 0xc5, 0x00, 0x75
+};
+
+static const unsigned int ld9040_22_130[] = {
+ 0x00, 0x71, 0xbb, 0xb5, 0xc8, 0x00, 0x57,
+ 0x00, 0xb5, 0xbb, 0xb0, 0xc5, 0x00, 0x6a,
+ 0x00, 0xae, 0xb9, 0xb1, 0xc6, 0x00, 0x70
+};
+
+static const unsigned int ld9040_22_120[] = {
+ 0x00, 0x47, 0xba, 0xb6, 0xca, 0x00, 0x53,
+ 0x00, 0xb5, 0xbb, 0xb3, 0xc6, 0x00, 0x65,
+ 0x00, 0xae, 0xb8, 0xb3, 0xc7, 0x00, 0x6c
+};
+
+static const unsigned int ld9040_22_110[] = {
+ 0x00, 0x13, 0xbb, 0xb7, 0xca, 0x00, 0x4f,
+ 0x00, 0xb4, 0xbb, 0xb3, 0xc7, 0x00, 0x60,
+ 0x00, 0xad, 0xb8, 0xb4, 0xc7, 0x00, 0x67
+};
+
+static const unsigned int ld9040_22_100[] = {
+ 0x00, 0x13, 0xba, 0xb8, 0xcb, 0x00, 0x4b,
+ 0x00, 0xb3, 0xbc, 0xb4, 0xc7, 0x00, 0x5c,
+ 0x00, 0xac, 0xb8, 0xb4, 0xc8, 0x00, 0x62
+};
+
+static const unsigned int ld9040_22_90[] = {
+ 0x00, 0x13, 0xb9, 0xb8, 0xcd, 0x00, 0x46,
+ 0x00, 0xb1, 0xbc, 0xb5, 0xc8, 0x00, 0x56,
+ 0x00, 0xaa, 0xb8, 0xb4, 0xc9, 0x00, 0x5d
+};
+
+static const unsigned int ld9040_22_80[] = {
+ 0x00, 0x13, 0xba, 0xb9, 0xcd, 0x00, 0x41,
+ 0x00, 0xb0, 0xbe, 0xb5, 0xc9, 0x00, 0x51,
+ 0x00, 0xa9, 0xb9, 0xb5, 0xca, 0x00, 0x57
+};
+
+static const unsigned int ld9040_22_70[] = {
+ 0x00, 0x13, 0xb9, 0xb9, 0xd0, 0x00, 0x3c,
+ 0x00, 0xaf, 0xbf, 0xb6, 0xcb, 0x00, 0x4b,
+ 0x00, 0xa8, 0xb9, 0xb5, 0xcc, 0x00, 0x52
+};
+
+static const unsigned int ld9040_22_50[] = {
+ 0x00, 0x13, 0xb2, 0xba, 0xd2, 0x00, 0x30,
+ 0x00, 0xaf, 0xc0, 0xb8, 0xcd, 0x00, 0x3d,
+ 0x00, 0xa8, 0xb8, 0xb7, 0xcd, 0x00, 0x44
+};
+
+struct ld9040_gamma {
+ unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
+};
+
+static struct ld9040_gamma gamma_table = {
+ .gamma_22_table[0] = (unsigned int *)&ld9040_22_50,
+ .gamma_22_table[1] = (unsigned int *)&ld9040_22_70,
+ .gamma_22_table[2] = (unsigned int *)&ld9040_22_80,
+ .gamma_22_table[3] = (unsigned int *)&ld9040_22_90,
+ .gamma_22_table[4] = (unsigned int *)&ld9040_22_100,
+ .gamma_22_table[5] = (unsigned int *)&ld9040_22_110,
+ .gamma_22_table[6] = (unsigned int *)&ld9040_22_120,
+ .gamma_22_table[7] = (unsigned int *)&ld9040_22_130,
+ .gamma_22_table[8] = (unsigned int *)&ld9040_22_140,
+ .gamma_22_table[9] = (unsigned int *)&ld9040_22_150,
+ .gamma_22_table[10] = (unsigned int *)&ld9040_22_160,
+ .gamma_22_table[11] = (unsigned int *)&ld9040_22_170,
+ .gamma_22_table[12] = (unsigned int *)&ld9040_22_180,
+ .gamma_22_table[13] = (unsigned int *)&ld9040_22_190,
+ .gamma_22_table[14] = (unsigned int *)&ld9040_22_200,
+ .gamma_22_table[15] = (unsigned int *)&ld9040_22_210,
+ .gamma_22_table[16] = (unsigned int *)&ld9040_22_220,
+ .gamma_22_table[17] = (unsigned int *)&ld9040_22_230,
+ .gamma_22_table[18] = (unsigned int *)&ld9040_22_240,
+ .gamma_22_table[19] = (unsigned int *)&ld9040_22_250,
+ .gamma_22_table[20] = (unsigned int *)&ld9040_22_260,
+ .gamma_22_table[21] = (unsigned int *)&ld9040_22_270,
+ .gamma_22_table[22] = (unsigned int *)&ld9040_22_280,
+ .gamma_22_table[23] = (unsigned int *)&ld9040_22_290,
+ .gamma_22_table[24] = (unsigned int *)&ld9040_22_300,
+};
+
+#endif
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
new file mode 100644
index 00000000000..cff1fbe89a1
--- /dev/null
+++ b/drivers/video/backlight/lm3533_bl.c
@@ -0,0 +1,412 @@
+/*
+ * lm3533-bl.c -- LM3533 Backlight driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_HVCTRLBANK_COUNT 2
+#define LM3533_BL_MAX_BRIGHTNESS 255
+
+#define LM3533_REG_CTRLBANK_AB_BCONF 0x1a
+
+
+struct lm3533_bl {
+ struct lm3533 *lm3533;
+ struct lm3533_ctrlbank cb;
+ struct backlight_device *bd;
+ int id;
+};
+
+
+static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl)
+{
+ return bl->id;
+}
+
+static int lm3533_bl_update_status(struct backlight_device *bd)
+{
+ struct lm3533_bl *bl = bl_get_data(bd);
+ int brightness = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ return lm3533_ctrlbank_set_brightness(&bl->cb, (u8)brightness);
+}
+
+static int lm3533_bl_get_brightness(struct backlight_device *bd)
+{
+ struct lm3533_bl *bl = bl_get_data(bd);
+ u8 val;
+ int ret;
+
+ ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static const struct backlight_ops lm3533_bl_ops = {
+ .get_brightness = lm3533_bl_get_brightness,
+ .update_status = lm3533_bl_update_status,
+};
+
+static ssize_t show_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id);
+}
+
+static ssize_t show_als_channel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ unsigned channel = lm3533_bl_get_ctrlbank_id(bl);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", channel);
+}
+
+static ssize_t show_als_en(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+ u8 val;
+ u8 mask;
+ bool enable;
+ int ret;
+
+ ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
+ if (ret)
+ return ret;
+
+ mask = 1 << (2 * ctrlbank);
+ enable = val & mask;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", enable);
+}
+
+static ssize_t store_als_en(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+ int enable;
+ u8 val;
+ u8 mask;
+ int ret;
+
+ if (kstrtoint(buf, 0, &enable))
+ return -EINVAL;
+
+ mask = 1 << (2 * ctrlbank);
+
+ if (enable)
+ val = mask;
+ else
+ val = 0;
+
+ ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
+ mask);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t show_linear(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ u8 val;
+ u8 mask;
+ int linear;
+ int ret;
+
+ ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
+ if (ret)
+ return ret;
+
+ mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
+
+ if (val & mask)
+ linear = 1;
+ else
+ linear = 0;
+
+ return scnprintf(buf, PAGE_SIZE, "%x\n", linear);
+}
+
+static ssize_t store_linear(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ unsigned long linear;
+ u8 mask;
+ u8 val;
+ int ret;
+
+ if (kstrtoul(buf, 0, &linear))
+ return -EINVAL;
+
+ mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
+
+ if (linear)
+ val = mask;
+ else
+ val = 0;
+
+ ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
+ mask);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t show_pwm(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ u8 val;
+ int ret;
+
+ ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val);
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_pwm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ u8 val;
+ int ret;
+
+ if (kstrtou8(buf, 0, &val))
+ return -EINVAL;
+
+ ret = lm3533_ctrlbank_set_pwm(&bl->cb, val);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static LM3533_ATTR_RO(als_channel);
+static LM3533_ATTR_RW(als_en);
+static LM3533_ATTR_RO(id);
+static LM3533_ATTR_RW(linear);
+static LM3533_ATTR_RW(pwm);
+
+static struct attribute *lm3533_bl_attributes[] = {
+ &dev_attr_als_channel.attr,
+ &dev_attr_als_en.attr,
+ &dev_attr_id.attr,
+ &dev_attr_linear.attr,
+ &dev_attr_pwm.attr,
+ NULL,
+};
+
+static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_als_channel.attr ||
+ attr == &dev_attr_als_en.attr) {
+ if (!bl->lm3533->have_als)
+ mode = 0;
+ }
+
+ return mode;
+};
+
+static struct attribute_group lm3533_bl_attribute_group = {
+ .is_visible = lm3533_bl_attr_is_visible,
+ .attrs = lm3533_bl_attributes
+};
+
+static int lm3533_bl_setup(struct lm3533_bl *bl,
+ struct lm3533_bl_platform_data *pdata)
+{
+ int ret;
+
+ ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current);
+ if (ret)
+ return ret;
+
+ return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm);
+}
+
+static int lm3533_bl_probe(struct platform_device *pdev)
+{
+ struct lm3533 *lm3533;
+ struct lm3533_bl_platform_data *pdata;
+ struct lm3533_bl *bl;
+ struct backlight_device *bd;
+ struct backlight_properties props;
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ lm3533 = dev_get_drvdata(pdev->dev.parent);
+ if (!lm3533)
+ return -EINVAL;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) {
+ dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id);
+ return -EINVAL;
+ }
+
+ bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL);
+ if (!bl)
+ return -ENOMEM;
+
+ bl->lm3533 = lm3533;
+ bl->id = pdev->id;
+
+ bl->cb.lm3533 = lm3533;
+ bl->cb.id = lm3533_bl_get_ctrlbank_id(bl);
+ bl->cb.dev = NULL; /* until registered */
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = LM3533_BL_MAX_BRIGHTNESS;
+ props.brightness = pdata->default_brightness;
+ bd = devm_backlight_device_register(&pdev->dev, pdata->name,
+ pdev->dev.parent, bl, &lm3533_bl_ops,
+ &props);
+ if (IS_ERR(bd)) {
+ dev_err(&pdev->dev, "failed to register backlight device\n");
+ return PTR_ERR(bd);
+ }
+
+ bl->bd = bd;
+ bl->cb.dev = &bl->bd->dev;
+
+ platform_set_drvdata(pdev, bl);
+
+ ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create sysfs attributes\n");
+ return ret;
+ }
+
+ backlight_update_status(bd);
+
+ ret = lm3533_bl_setup(bl, pdata);
+ if (ret)
+ goto err_sysfs_remove;
+
+ ret = lm3533_ctrlbank_enable(&bl->cb);
+ if (ret)
+ goto err_sysfs_remove;
+
+ return 0;
+
+err_sysfs_remove:
+ sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+
+ return ret;
+}
+
+static int lm3533_bl_remove(struct platform_device *pdev)
+{
+ struct lm3533_bl *bl = platform_get_drvdata(pdev);
+ struct backlight_device *bd = bl->bd;
+
+ dev_dbg(&bd->dev, "%s\n", __func__);
+
+ bd->props.power = FB_BLANK_POWERDOWN;
+ bd->props.brightness = 0;
+
+ lm3533_ctrlbank_disable(&bl->cb);
+ sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int lm3533_bl_suspend(struct device *dev)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return lm3533_ctrlbank_disable(&bl->cb);
+}
+
+static int lm3533_bl_resume(struct device *dev)
+{
+ struct lm3533_bl *bl = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return lm3533_ctrlbank_enable(&bl->cb);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lm3533_bl_pm_ops, lm3533_bl_suspend, lm3533_bl_resume);
+
+static void lm3533_bl_shutdown(struct platform_device *pdev)
+{
+ struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ lm3533_ctrlbank_disable(&bl->cb);
+}
+
+static struct platform_driver lm3533_bl_driver = {
+ .driver = {
+ .name = "lm3533-backlight",
+ .owner = THIS_MODULE,
+ .pm = &lm3533_bl_pm_ops,
+ },
+ .probe = lm3533_bl_probe,
+ .remove = lm3533_bl_remove,
+ .shutdown = lm3533_bl_shutdown,
+};
+module_platform_driver(lm3533_bl_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Backlight driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-backlight");
diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c
new file mode 100644
index 00000000000..35fe4825a45
--- /dev/null
+++ b/drivers/video/backlight/lm3630a_bl.c
@@ -0,0 +1,483 @@
+/*
+* Simple driver for Texas Instruments LM3630A Backlight driver chip
+* Copyright (C) 2012 Texas Instruments
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+*/
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/pwm.h>
+#include <linux/platform_data/lm3630a_bl.h>
+
+#define REG_CTRL 0x00
+#define REG_BOOST 0x02
+#define REG_CONFIG 0x01
+#define REG_BRT_A 0x03
+#define REG_BRT_B 0x04
+#define REG_I_A 0x05
+#define REG_I_B 0x06
+#define REG_INT_STATUS 0x09
+#define REG_INT_EN 0x0A
+#define REG_FAULT 0x0B
+#define REG_PWM_OUTLOW 0x12
+#define REG_PWM_OUTHIGH 0x13
+#define REG_MAX 0x1F
+
+#define INT_DEBOUNCE_MSEC 10
+struct lm3630a_chip {
+ struct device *dev;
+ struct delayed_work work;
+
+ int irq;
+ struct workqueue_struct *irqthread;
+ struct lm3630a_platform_data *pdata;
+ struct backlight_device *bleda;
+ struct backlight_device *bledb;
+ struct regmap *regmap;
+ struct pwm_device *pwmd;
+};
+
+/* i2c access */
+static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg)
+{
+ int rval;
+ unsigned int reg_val;
+
+ rval = regmap_read(pchip->regmap, reg, &reg_val);
+ if (rval < 0)
+ return rval;
+ return reg_val & 0xFF;
+}
+
+static int lm3630a_write(struct lm3630a_chip *pchip,
+ unsigned int reg, unsigned int data)
+{
+ return regmap_write(pchip->regmap, reg, data);
+}
+
+static int lm3630a_update(struct lm3630a_chip *pchip,
+ unsigned int reg, unsigned int mask,
+ unsigned int data)
+{
+ return regmap_update_bits(pchip->regmap, reg, mask, data);
+}
+
+/* initialize chip */
+static int lm3630a_chip_init(struct lm3630a_chip *pchip)
+{
+ int rval;
+ struct lm3630a_platform_data *pdata = pchip->pdata;
+
+ usleep_range(1000, 2000);
+ /* set Filter Strength Register */
+ rval = lm3630a_write(pchip, 0x50, 0x03);
+ /* set Cofig. register */
+ rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl);
+ /* set boost control */
+ rval |= lm3630a_write(pchip, REG_BOOST, 0x38);
+ /* set current A */
+ rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F);
+ /* set current B */
+ rval |= lm3630a_write(pchip, REG_I_B, 0x1F);
+ /* set control */
+ rval |= lm3630a_update(pchip, REG_CTRL, 0x14, pdata->leda_ctrl);
+ rval |= lm3630a_update(pchip, REG_CTRL, 0x0B, pdata->ledb_ctrl);
+ usleep_range(1000, 2000);
+ /* set brightness A and B */
+ rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt);
+ rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt);
+
+ if (rval < 0)
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return rval;
+}
+
+/* interrupt handling */
+static void lm3630a_delayed_func(struct work_struct *work)
+{
+ int rval;
+ struct lm3630a_chip *pchip;
+
+ pchip = container_of(work, struct lm3630a_chip, work.work);
+
+ rval = lm3630a_read(pchip, REG_INT_STATUS);
+ if (rval < 0) {
+ dev_err(pchip->dev,
+ "i2c failed to access REG_INT_STATUS Register\n");
+ return;
+ }
+
+ dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval);
+}
+
+static irqreturn_t lm3630a_isr_func(int irq, void *chip)
+{
+ int rval;
+ struct lm3630a_chip *pchip = chip;
+ unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC);
+
+ queue_delayed_work(pchip->irqthread, &pchip->work, delay);
+
+ rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
+ if (rval < 0) {
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return IRQ_NONE;
+ }
+ return IRQ_HANDLED;
+}
+
+static int lm3630a_intr_config(struct lm3630a_chip *pchip)
+{
+ int rval;
+
+ rval = lm3630a_write(pchip, REG_INT_EN, 0x87);
+ if (rval < 0)
+ return rval;
+
+ INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func);
+ pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd");
+ if (!pchip->irqthread) {
+ dev_err(pchip->dev, "create irq thread fail\n");
+ return -ENOMEM;
+ }
+ if (request_threaded_irq
+ (pchip->irq, NULL, lm3630a_isr_func,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) {
+ dev_err(pchip->dev, "request threaded irq fail\n");
+ destroy_workqueue(pchip->irqthread);
+ return -ENOMEM;
+ }
+ return rval;
+}
+
+static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
+{
+ unsigned int period = pwm_get_period(pchip->pwmd);
+ unsigned int duty = br * period / br_max;
+
+ pwm_config(pchip->pwmd, duty, period);
+ if (duty)
+ pwm_enable(pchip->pwmd);
+ else
+ pwm_disable(pchip->pwmd);
+}
+
+/* update and get brightness */
+static int lm3630a_bank_a_update_status(struct backlight_device *bl)
+{
+ int ret;
+ struct lm3630a_chip *pchip = bl_get_data(bl);
+ enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
+
+ /* pwm control */
+ if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
+ lm3630a_pwm_ctrl(pchip, bl->props.brightness,
+ bl->props.max_brightness);
+ return bl->props.brightness;
+ }
+
+ /* disable sleep */
+ ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
+ if (ret < 0)
+ goto out_i2c_err;
+ usleep_range(1000, 2000);
+ /* minimum brightness is 0x04 */
+ ret = lm3630a_write(pchip, REG_BRT_A, bl->props.brightness);
+ if (bl->props.brightness < 0x4)
+ ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0);
+ else
+ ret |= lm3630a_update(pchip, REG_CTRL,
+ LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE);
+ if (ret < 0)
+ goto out_i2c_err;
+ return bl->props.brightness;
+
+out_i2c_err:
+ dev_err(pchip->dev, "i2c failed to access\n");
+ return bl->props.brightness;
+}
+
+static int lm3630a_bank_a_get_brightness(struct backlight_device *bl)
+{
+ int brightness, rval;
+ struct lm3630a_chip *pchip = bl_get_data(bl);
+ enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
+
+ if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
+ rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
+ if (rval < 0)
+ goto out_i2c_err;
+ brightness = (rval & 0x01) << 8;
+ rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
+ if (rval < 0)
+ goto out_i2c_err;
+ brightness |= rval;
+ goto out;
+ }
+
+ /* disable sleep */
+ rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
+ if (rval < 0)
+ goto out_i2c_err;
+ usleep_range(1000, 2000);
+ rval = lm3630a_read(pchip, REG_BRT_A);
+ if (rval < 0)
+ goto out_i2c_err;
+ brightness = rval;
+
+out:
+ bl->props.brightness = brightness;
+ return bl->props.brightness;
+out_i2c_err:
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return 0;
+}
+
+static const struct backlight_ops lm3630a_bank_a_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lm3630a_bank_a_update_status,
+ .get_brightness = lm3630a_bank_a_get_brightness,
+};
+
+/* update and get brightness */
+static int lm3630a_bank_b_update_status(struct backlight_device *bl)
+{
+ int ret;
+ struct lm3630a_chip *pchip = bl_get_data(bl);
+ enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
+
+ /* pwm control */
+ if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
+ lm3630a_pwm_ctrl(pchip, bl->props.brightness,
+ bl->props.max_brightness);
+ return bl->props.brightness;
+ }
+
+ /* disable sleep */
+ ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
+ if (ret < 0)
+ goto out_i2c_err;
+ usleep_range(1000, 2000);
+ /* minimum brightness is 0x04 */
+ ret = lm3630a_write(pchip, REG_BRT_B, bl->props.brightness);
+ if (bl->props.brightness < 0x4)
+ ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0);
+ else
+ ret |= lm3630a_update(pchip, REG_CTRL,
+ LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE);
+ if (ret < 0)
+ goto out_i2c_err;
+ return bl->props.brightness;
+
+out_i2c_err:
+ dev_err(pchip->dev, "i2c failed to access REG_CTRL\n");
+ return bl->props.brightness;
+}
+
+static int lm3630a_bank_b_get_brightness(struct backlight_device *bl)
+{
+ int brightness, rval;
+ struct lm3630a_chip *pchip = bl_get_data(bl);
+ enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
+
+ if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
+ rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
+ if (rval < 0)
+ goto out_i2c_err;
+ brightness = (rval & 0x01) << 8;
+ rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
+ if (rval < 0)
+ goto out_i2c_err;
+ brightness |= rval;
+ goto out;
+ }
+
+ /* disable sleep */
+ rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
+ if (rval < 0)
+ goto out_i2c_err;
+ usleep_range(1000, 2000);
+ rval = lm3630a_read(pchip, REG_BRT_B);
+ if (rval < 0)
+ goto out_i2c_err;
+ brightness = rval;
+
+out:
+ bl->props.brightness = brightness;
+ return bl->props.brightness;
+out_i2c_err:
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return 0;
+}
+
+static const struct backlight_ops lm3630a_bank_b_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lm3630a_bank_b_update_status,
+ .get_brightness = lm3630a_bank_b_get_brightness,
+};
+
+static int lm3630a_backlight_register(struct lm3630a_chip *pchip)
+{
+ struct backlight_properties props;
+ struct lm3630a_platform_data *pdata = pchip->pdata;
+
+ props.type = BACKLIGHT_RAW;
+ if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) {
+ props.brightness = pdata->leda_init_brt;
+ props.max_brightness = pdata->leda_max_brt;
+ pchip->bleda =
+ devm_backlight_device_register(pchip->dev, "lm3630a_leda",
+ pchip->dev, pchip,
+ &lm3630a_bank_a_ops, &props);
+ if (IS_ERR(pchip->bleda))
+ return PTR_ERR(pchip->bleda);
+ }
+
+ if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) &&
+ (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) {
+ props.brightness = pdata->ledb_init_brt;
+ props.max_brightness = pdata->ledb_max_brt;
+ pchip->bledb =
+ devm_backlight_device_register(pchip->dev, "lm3630a_ledb",
+ pchip->dev, pchip,
+ &lm3630a_bank_b_ops, &props);
+ if (IS_ERR(pchip->bledb))
+ return PTR_ERR(pchip->bledb);
+ }
+ return 0;
+}
+
+static const struct regmap_config lm3630a_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+};
+
+static int lm3630a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lm3630a_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct lm3630a_chip *pchip;
+ int rval;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "fail : i2c functionality check\n");
+ return -EOPNOTSUPP;
+ }
+
+ pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630a_chip),
+ GFP_KERNEL);
+ if (!pchip)
+ return -ENOMEM;
+ pchip->dev = &client->dev;
+
+ pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap);
+ if (IS_ERR(pchip->regmap)) {
+ rval = PTR_ERR(pchip->regmap);
+ dev_err(&client->dev, "fail : allocate reg. map: %d\n", rval);
+ return rval;
+ }
+
+ i2c_set_clientdata(client, pchip);
+ if (pdata == NULL) {
+ pdata = devm_kzalloc(pchip->dev,
+ sizeof(struct lm3630a_platform_data),
+ GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+ /* default values */
+ pdata->leda_ctrl = LM3630A_LEDA_ENABLE;
+ pdata->ledb_ctrl = LM3630A_LEDB_ENABLE;
+ pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS;
+ pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS;
+ pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS;
+ pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS;
+ }
+ pchip->pdata = pdata;
+
+ /* chip initialize */
+ rval = lm3630a_chip_init(pchip);
+ if (rval < 0) {
+ dev_err(&client->dev, "fail : init chip\n");
+ return rval;
+ }
+ /* backlight register */
+ rval = lm3630a_backlight_register(pchip);
+ if (rval < 0) {
+ dev_err(&client->dev, "fail : backlight register.\n");
+ return rval;
+ }
+ /* pwm */
+ if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) {
+ pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm");
+ if (IS_ERR(pchip->pwmd)) {
+ dev_err(&client->dev, "fail : get pwm device\n");
+ return PTR_ERR(pchip->pwmd);
+ }
+ }
+ pchip->pwmd->period = pdata->pwm_period;
+
+ /* interrupt enable : irq 0 is not allowed */
+ pchip->irq = client->irq;
+ if (pchip->irq) {
+ rval = lm3630a_intr_config(pchip);
+ if (rval < 0)
+ return rval;
+ }
+ dev_info(&client->dev, "LM3630A backlight register OK.\n");
+ return 0;
+}
+
+static int lm3630a_remove(struct i2c_client *client)
+{
+ int rval;
+ struct lm3630a_chip *pchip = i2c_get_clientdata(client);
+
+ rval = lm3630a_write(pchip, REG_BRT_A, 0);
+ if (rval < 0)
+ dev_err(pchip->dev, "i2c failed to access register\n");
+
+ rval = lm3630a_write(pchip, REG_BRT_B, 0);
+ if (rval < 0)
+ dev_err(pchip->dev, "i2c failed to access register\n");
+
+ if (pchip->irq) {
+ free_irq(pchip->irq, pchip);
+ flush_workqueue(pchip->irqthread);
+ destroy_workqueue(pchip->irqthread);
+ }
+ return 0;
+}
+
+static const struct i2c_device_id lm3630a_id[] = {
+ {LM3630A_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3630a_id);
+
+static struct i2c_driver lm3630a_i2c_driver = {
+ .driver = {
+ .name = LM3630A_NAME,
+ },
+ .probe = lm3630a_probe,
+ .remove = lm3630a_remove,
+ .id_table = lm3630a_id,
+};
+
+module_i2c_driver(lm3630a_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A");
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c
new file mode 100644
index 00000000000..5f36808d214
--- /dev/null
+++ b/drivers/video/backlight/lm3639_bl.c
@@ -0,0 +1,434 @@
+/*
+* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip
+* Copyright (C) 2012 Texas Instruments
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+*/
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/lm3639_bl.h>
+
+#define REG_DEV_ID 0x00
+#define REG_CHECKSUM 0x01
+#define REG_BL_CONF_1 0x02
+#define REG_BL_CONF_2 0x03
+#define REG_BL_CONF_3 0x04
+#define REG_BL_CONF_4 0x05
+#define REG_FL_CONF_1 0x06
+#define REG_FL_CONF_2 0x07
+#define REG_FL_CONF_3 0x08
+#define REG_IO_CTRL 0x09
+#define REG_ENABLE 0x0A
+#define REG_FLAG 0x0B
+#define REG_MAX REG_FLAG
+
+struct lm3639_chip_data {
+ struct device *dev;
+ struct lm3639_platform_data *pdata;
+
+ struct backlight_device *bled;
+ struct led_classdev cdev_flash;
+ struct led_classdev cdev_torch;
+ struct regmap *regmap;
+
+ unsigned int bled_mode;
+ unsigned int bled_map;
+ unsigned int last_flag;
+};
+
+/* initialize chip */
+static int lm3639_chip_init(struct lm3639_chip_data *pchip)
+{
+ int ret;
+ unsigned int reg_val;
+ struct lm3639_platform_data *pdata = pchip->pdata;
+
+ /* input pins config. */
+ ret =
+ regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08,
+ pdata->pin_pwm);
+ if (ret < 0)
+ goto out;
+
+ reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx;
+ ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val);
+ if (ret < 0)
+ goto out;
+
+ /* init brightness */
+ ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led);
+ if (ret < 0)
+ goto out;
+
+ /* output pins config. */
+ if (!pdata->init_brt_led) {
+ reg_val = pdata->fled_pins;
+ reg_val |= pdata->bled_pins;
+ } else {
+ reg_val = pdata->fled_pins;
+ reg_val |= pdata->bled_pins | 0x01;
+ }
+
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val);
+ if (ret < 0)
+ goto out;
+
+ return ret;
+out:
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return ret;
+}
+
+/* update and get brightness */
+static int lm3639_bled_update_status(struct backlight_device *bl)
+{
+ int ret;
+ unsigned int reg_val;
+ struct lm3639_chip_data *pchip = bl_get_data(bl);
+ struct lm3639_platform_data *pdata = pchip->pdata;
+
+ ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
+ if (ret < 0)
+ goto out;
+
+ if (reg_val != 0)
+ dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
+
+ /* pwm control */
+ if (pdata->pin_pwm) {
+ if (pdata->pwm_set_intensity)
+ pdata->pwm_set_intensity(bl->props.brightness,
+ pdata->max_brt_led);
+ else
+ dev_err(pchip->dev,
+ "No pwm control func. in plat-data\n");
+ return bl->props.brightness;
+ }
+
+ /* i2c control and set brigtness */
+ ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness);
+ if (ret < 0)
+ goto out;
+ ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness);
+ if (ret < 0)
+ goto out;
+
+ if (!bl->props.brightness)
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00);
+ else
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01);
+ if (ret < 0)
+ goto out;
+
+ return bl->props.brightness;
+out:
+ dev_err(pchip->dev, "i2c failed to access registers\n");
+ return bl->props.brightness;
+}
+
+static int lm3639_bled_get_brightness(struct backlight_device *bl)
+{
+ int ret;
+ unsigned int reg_val;
+ struct lm3639_chip_data *pchip = bl_get_data(bl);
+ struct lm3639_platform_data *pdata = pchip->pdata;
+
+ if (pdata->pin_pwm) {
+ if (pdata->pwm_get_intensity)
+ bl->props.brightness = pdata->pwm_get_intensity();
+ else
+ dev_err(pchip->dev,
+ "No pwm control func. in plat-data\n");
+ return bl->props.brightness;
+ }
+
+ ret = regmap_read(pchip->regmap, REG_BL_CONF_1, &reg_val);
+ if (ret < 0)
+ goto out;
+ if (reg_val & 0x10)
+ ret = regmap_read(pchip->regmap, REG_BL_CONF_4, &reg_val);
+ else
+ ret = regmap_read(pchip->regmap, REG_BL_CONF_3, &reg_val);
+ if (ret < 0)
+ goto out;
+ bl->props.brightness = reg_val;
+
+ return bl->props.brightness;
+out:
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops lm3639_bled_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lm3639_bled_update_status,
+ .get_brightness = lm3639_bled_get_brightness,
+};
+
+/* backlight mapping mode */
+static ssize_t lm3639_bled_mode_store(struct device *dev,
+ struct device_attribute *devAttr,
+ const char *buf, size_t size)
+{
+ ssize_t ret;
+ struct lm3639_chip_data *pchip = dev_get_drvdata(dev);
+ unsigned int state;
+
+ ret = kstrtouint(buf, 10, &state);
+ if (ret)
+ goto out_input;
+
+ if (!state)
+ ret =
+ regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
+ 0x00);
+ else
+ ret =
+ regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
+ 0x10);
+
+ if (ret < 0)
+ goto out;
+
+ return size;
+
+out:
+ dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);
+ return ret;
+
+out_input:
+ dev_err(pchip->dev, "%s:input conversion fail\n", __func__);
+ return ret;
+
+}
+
+static DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);
+
+/* torch */
+static void lm3639_torch_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ int ret;
+ unsigned int reg_val;
+ struct lm3639_chip_data *pchip;
+
+ pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch);
+
+ ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
+ if (ret < 0)
+ goto out;
+ if (reg_val != 0)
+ dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
+
+ /* brightness 0 means off state */
+ if (!brightness) {
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
+ if (ret < 0)
+ goto out;
+ return;
+ }
+
+ ret = regmap_update_bits(pchip->regmap,
+ REG_FL_CONF_1, 0x70, (brightness - 1) << 4);
+ if (ret < 0)
+ goto out;
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02);
+ if (ret < 0)
+ goto out;
+
+ return;
+out:
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return;
+}
+
+/* flash */
+static void lm3639_flash_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ int ret;
+ unsigned int reg_val;
+ struct lm3639_chip_data *pchip;
+
+ pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
+
+ ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
+ if (ret < 0)
+ goto out;
+ if (reg_val != 0)
+ dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
+
+ /* torch off before flash control */
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
+ if (ret < 0)
+ goto out;
+
+ /* brightness 0 means off state */
+ if (!brightness)
+ return;
+
+ ret = regmap_update_bits(pchip->regmap,
+ REG_FL_CONF_1, 0x0F, brightness - 1);
+ if (ret < 0)
+ goto out;
+ ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
+ if (ret < 0)
+ goto out;
+
+ return;
+out:
+ dev_err(pchip->dev, "i2c failed to access register\n");
+ return;
+}
+
+static const struct regmap_config lm3639_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+};
+
+static int lm3639_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lm3639_chip_data *pchip;
+ struct lm3639_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct backlight_properties props;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c functionality check fail.\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "Needs Platform Data.\n");
+ return -ENODATA;
+ }
+
+ pchip = devm_kzalloc(&client->dev,
+ sizeof(struct lm3639_chip_data), GFP_KERNEL);
+ if (!pchip)
+ return -ENOMEM;
+
+ pchip->pdata = pdata;
+ pchip->dev = &client->dev;
+
+ pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
+ if (IS_ERR(pchip->regmap)) {
+ ret = PTR_ERR(pchip->regmap);
+ dev_err(&client->dev, "fail : allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ i2c_set_clientdata(client, pchip);
+
+ /* chip initialize */
+ ret = lm3639_chip_init(pchip);
+ if (ret < 0) {
+ dev_err(&client->dev, "fail : chip init\n");
+ goto err_out;
+ }
+
+ /* backlight */
+ props.type = BACKLIGHT_RAW;
+ props.brightness = pdata->init_brt_led;
+ props.max_brightness = pdata->max_brt_led;
+ pchip->bled =
+ devm_backlight_device_register(pchip->dev, "lm3639_bled",
+ pchip->dev, pchip, &lm3639_bled_ops,
+ &props);
+ if (IS_ERR(pchip->bled)) {
+ dev_err(&client->dev, "fail : backlight register\n");
+ ret = PTR_ERR(pchip->bled);
+ goto err_out;
+ }
+
+ ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed : add sysfs entries\n");
+ goto err_out;
+ }
+
+ /* flash */
+ pchip->cdev_flash.name = "lm3639_flash";
+ pchip->cdev_flash.max_brightness = 16;
+ pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
+ ret = led_classdev_register((struct device *)
+ &client->dev, &pchip->cdev_flash);
+ if (ret < 0) {
+ dev_err(&client->dev, "fail : flash register\n");
+ goto err_flash;
+ }
+
+ /* torch */
+ pchip->cdev_torch.name = "lm3639_torch";
+ pchip->cdev_torch.max_brightness = 8;
+ pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
+ ret = led_classdev_register((struct device *)
+ &client->dev, &pchip->cdev_torch);
+ if (ret < 0) {
+ dev_err(&client->dev, "fail : torch register\n");
+ goto err_torch;
+ }
+
+ return 0;
+
+err_torch:
+ led_classdev_unregister(&pchip->cdev_flash);
+err_flash:
+ device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
+err_out:
+ return ret;
+}
+
+static int lm3639_remove(struct i2c_client *client)
+{
+ struct lm3639_chip_data *pchip = i2c_get_clientdata(client);
+
+ regmap_write(pchip->regmap, REG_ENABLE, 0x00);
+
+ if (&pchip->cdev_torch)
+ led_classdev_unregister(&pchip->cdev_torch);
+ if (&pchip->cdev_flash)
+ led_classdev_unregister(&pchip->cdev_flash);
+ if (pchip->bled)
+ device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
+ return 0;
+}
+
+static const struct i2c_device_id lm3639_id[] = {
+ {LM3639_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3639_id);
+static struct i2c_driver lm3639_i2c_driver = {
+ .driver = {
+ .name = LM3639_NAME,
+ },
+ .probe = lm3639_probe,
+ .remove = lm3639_remove,
+ .id_table = lm3639_id,
+};
+
+module_i2c_driver(lm3639_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c
new file mode 100644
index 00000000000..14590c54aed
--- /dev/null
+++ b/drivers/video/backlight/lms283gf05.c
@@ -0,0 +1,204 @@
+/*
+ * lms283gf05.c -- support for Samsung LMS283GF05 LCD
+ *
+ * Copyright (c) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/lms283gf05.h>
+#include <linux/module.h>
+
+struct lms283gf05_state {
+ struct spi_device *spi;
+ struct lcd_device *ld;
+};
+
+struct lms283gf05_seq {
+ unsigned char reg;
+ unsigned short value;
+ unsigned char delay;
+};
+
+/* Magic sequences supplied by manufacturer, for details refer to datasheet */
+static const struct lms283gf05_seq disp_initseq[] = {
+ /* REG, VALUE, DELAY */
+ { 0x07, 0x0000, 0 },
+ { 0x13, 0x0000, 10 },
+
+ { 0x11, 0x3004, 0 },
+ { 0x14, 0x200F, 0 },
+ { 0x10, 0x1a20, 0 },
+ { 0x13, 0x0040, 50 },
+
+ { 0x13, 0x0060, 0 },
+ { 0x13, 0x0070, 200 },
+
+ { 0x01, 0x0127, 0 },
+ { 0x02, 0x0700, 0 },
+ { 0x03, 0x1030, 0 },
+ { 0x08, 0x0208, 0 },
+ { 0x0B, 0x0620, 0 },
+ { 0x0C, 0x0110, 0 },
+ { 0x30, 0x0120, 0 },
+ { 0x31, 0x0127, 0 },
+ { 0x32, 0x0000, 0 },
+ { 0x33, 0x0503, 0 },
+ { 0x34, 0x0727, 0 },
+ { 0x35, 0x0124, 0 },
+ { 0x36, 0x0706, 0 },
+ { 0x37, 0x0701, 0 },
+ { 0x38, 0x0F00, 0 },
+ { 0x39, 0x0F00, 0 },
+ { 0x40, 0x0000, 0 },
+ { 0x41, 0x0000, 0 },
+ { 0x42, 0x013f, 0 },
+ { 0x43, 0x0000, 0 },
+ { 0x44, 0x013f, 0 },
+ { 0x45, 0x0000, 0 },
+ { 0x46, 0xef00, 0 },
+ { 0x47, 0x013f, 0 },
+ { 0x48, 0x0000, 0 },
+ { 0x07, 0x0015, 30 },
+
+ { 0x07, 0x0017, 0 },
+
+ { 0x20, 0x0000, 0 },
+ { 0x21, 0x0000, 0 },
+ { 0x22, 0x0000, 0 }
+};
+
+static const struct lms283gf05_seq disp_pdwnseq[] = {
+ { 0x07, 0x0016, 30 },
+
+ { 0x07, 0x0004, 0 },
+ { 0x10, 0x0220, 20 },
+
+ { 0x13, 0x0060, 50 },
+
+ { 0x13, 0x0040, 50 },
+
+ { 0x13, 0x0000, 0 },
+ { 0x10, 0x0000, 0 }
+};
+
+
+static void lms283gf05_reset(unsigned long gpio, bool inverted)
+{
+ gpio_set_value(gpio, !inverted);
+ mdelay(100);
+ gpio_set_value(gpio, inverted);
+ mdelay(20);
+ gpio_set_value(gpio, !inverted);
+ mdelay(20);
+}
+
+static void lms283gf05_toggle(struct spi_device *spi,
+ const struct lms283gf05_seq *seq, int sz)
+{
+ char buf[3];
+ int i;
+
+ for (i = 0; i < sz; i++) {
+ buf[0] = 0x74;
+ buf[1] = 0x00;
+ buf[2] = seq[i].reg;
+ spi_write(spi, buf, 3);
+
+ buf[0] = 0x76;
+ buf[1] = seq[i].value >> 8;
+ buf[2] = seq[i].value & 0xff;
+ spi_write(spi, buf, 3);
+
+ mdelay(seq[i].delay);
+ }
+}
+
+static int lms283gf05_power_set(struct lcd_device *ld, int power)
+{
+ struct lms283gf05_state *st = lcd_get_data(ld);
+ struct spi_device *spi = st->spi;
+ struct lms283gf05_pdata *pdata = dev_get_platdata(&spi->dev);
+
+ if (power <= FB_BLANK_NORMAL) {
+ if (pdata)
+ lms283gf05_reset(pdata->reset_gpio,
+ pdata->reset_inverted);
+ lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
+ } else {
+ lms283gf05_toggle(spi, disp_pdwnseq, ARRAY_SIZE(disp_pdwnseq));
+ if (pdata)
+ gpio_set_value(pdata->reset_gpio,
+ pdata->reset_inverted);
+ }
+
+ return 0;
+}
+
+static struct lcd_ops lms_ops = {
+ .set_power = lms283gf05_power_set,
+ .get_power = NULL,
+};
+
+static int lms283gf05_probe(struct spi_device *spi)
+{
+ struct lms283gf05_state *st;
+ struct lms283gf05_pdata *pdata = dev_get_platdata(&spi->dev);
+ struct lcd_device *ld;
+ int ret = 0;
+
+ if (pdata != NULL) {
+ ret = devm_gpio_request_one(&spi->dev, pdata->reset_gpio,
+ GPIOF_DIR_OUT | (!pdata->reset_inverted ?
+ GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+ "LMS285GF05 RESET");
+ if (ret)
+ return ret;
+ }
+
+ st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state),
+ GFP_KERNEL);
+ if (st == NULL)
+ return -ENOMEM;
+
+ ld = devm_lcd_device_register(&spi->dev, "lms283gf05", &spi->dev, st,
+ &lms_ops);
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
+
+ st->spi = spi;
+ st->ld = ld;
+
+ spi_set_drvdata(spi, st);
+
+ /* kick in the LCD */
+ if (pdata)
+ lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted);
+ lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
+
+ return 0;
+}
+
+static struct spi_driver lms283gf05_driver = {
+ .driver = {
+ .name = "lms283gf05",
+ .owner = THIS_MODULE,
+ },
+ .probe = lms283gf05_probe,
+};
+
+module_spi_driver(lms283gf05_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("LCD283GF05 LCD");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/lms501kf03.c b/drivers/video/backlight/lms501kf03.c
new file mode 100644
index 00000000000..77258b7b04b
--- /dev/null
+++ b/drivers/video/backlight/lms501kf03.c
@@ -0,0 +1,437 @@
+/*
+ * lms501kf03 TFT LCD panel driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+
+#define COMMAND_ONLY 0x00
+#define DATA_ONLY 0x01
+
+struct lms501kf03 {
+ struct device *dev;
+ struct spi_device *spi;
+ unsigned int power;
+ struct lcd_device *ld;
+ struct lcd_platform_data *lcd_pd;
+};
+
+static const unsigned char seq_password[] = {
+ 0xb9, 0xff, 0x83, 0x69,
+};
+
+static const unsigned char seq_power[] = {
+ 0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
+ 0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+};
+
+static const unsigned char seq_display[] = {
+ 0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
+};
+
+static const unsigned char seq_rgb_if[] = {
+ 0xb3, 0x09,
+};
+
+static const unsigned char seq_display_inv[] = {
+ 0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
+};
+
+static const unsigned char seq_vcom[] = {
+ 0xb6, 0x4c, 0x2e,
+};
+
+static const unsigned char seq_gate[] = {
+ 0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
+ 0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
+ 0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
+};
+
+static const unsigned char seq_panel[] = {
+ 0xcc, 0x02,
+};
+
+static const unsigned char seq_col_mod[] = {
+ 0x3a, 0x77,
+};
+
+static const unsigned char seq_w_gamma[] = {
+ 0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
+ 0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
+ 0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
+ 0x18, 0x16, 0x17, 0x0d, 0x15,
+};
+
+static const unsigned char seq_rgb_gamma[] = {
+ 0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
+ 0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
+ 0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
+ 0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
+ 0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
+ 0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
+ 0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
+ 0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
+ 0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
+ 0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
+ 0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
+ 0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char seq_up_dn[] = {
+ 0x36, 0x10,
+};
+
+static const unsigned char seq_sleep_in[] = {
+ 0x10,
+};
+
+static const unsigned char seq_sleep_out[] = {
+ 0x11,
+};
+
+static const unsigned char seq_display_on[] = {
+ 0x29,
+};
+
+static const unsigned char seq_display_off[] = {
+ 0x10,
+};
+
+static int lms501kf03_spi_write_byte(struct lms501kf03 *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 lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address,
+ unsigned char command)
+{
+ return lms501kf03_spi_write_byte(lcd, address, command);
+}
+
+static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
+ const unsigned char *wbuf,
+ unsigned int len)
+{
+ int ret = 0, i = 0;
+
+ while (i < len) {
+ if (i == 0)
+ ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
+ else
+ ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
+ if (ret)
+ break;
+ i += 1;
+ }
+
+ return ret;
+}
+
+static int lms501kf03_ldi_init(struct lms501kf03 *lcd)
+{
+ int ret, i;
+ static const unsigned char *init_seq[] = {
+ seq_password,
+ seq_power,
+ seq_display,
+ seq_rgb_if,
+ seq_display_inv,
+ seq_vcom,
+ seq_gate,
+ seq_panel,
+ seq_col_mod,
+ seq_w_gamma,
+ seq_rgb_gamma,
+ seq_sleep_out,
+ };
+
+ static const unsigned int size_seq[] = {
+ ARRAY_SIZE(seq_password),
+ ARRAY_SIZE(seq_power),
+ ARRAY_SIZE(seq_display),
+ ARRAY_SIZE(seq_rgb_if),
+ ARRAY_SIZE(seq_display_inv),
+ ARRAY_SIZE(seq_vcom),
+ ARRAY_SIZE(seq_gate),
+ ARRAY_SIZE(seq_panel),
+ ARRAY_SIZE(seq_col_mod),
+ ARRAY_SIZE(seq_w_gamma),
+ ARRAY_SIZE(seq_rgb_gamma),
+ ARRAY_SIZE(seq_sleep_out),
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+ ret = lms501kf03_panel_send_sequence(lcd, init_seq[i],
+ size_seq[i]);
+ if (ret)
+ break;
+ }
+ /*
+ * According to the datasheet, 120ms delay time is required.
+ * After sleep out sequence, command is blocked for 120ms.
+ * Thus, LDI should wait for 120ms.
+ */
+ msleep(120);
+
+ return ret;
+}
+
+static int lms501kf03_ldi_enable(struct lms501kf03 *lcd)
+{
+ return lms501kf03_panel_send_sequence(lcd, seq_display_on,
+ ARRAY_SIZE(seq_display_on));
+}
+
+static int lms501kf03_ldi_disable(struct lms501kf03 *lcd)
+{
+ return lms501kf03_panel_send_sequence(lcd, seq_display_off,
+ ARRAY_SIZE(seq_display_off));
+}
+
+static int lms501kf03_power_is_on(int power)
+{
+ return (power) <= FB_BLANK_NORMAL;
+}
+
+static int lms501kf03_power_on(struct lms501kf03 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd;
+
+ pd = lcd->lcd_pd;
+
+ if (!pd->power_on) {
+ dev_err(lcd->dev, "power_on is NULL.\n");
+ return -EINVAL;
+ } else {
+ pd->power_on(lcd->ld, 1);
+ msleep(pd->power_on_delay);
+ }
+
+ if (!pd->reset) {
+ dev_err(lcd->dev, "reset is NULL.\n");
+ return -EINVAL;
+ } else {
+ pd->reset(lcd->ld);
+ msleep(pd->reset_delay);
+ }
+
+ ret = lms501kf03_ldi_init(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to initialize ldi.\n");
+ return ret;
+ }
+
+ ret = lms501kf03_ldi_enable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "failed to enable ldi.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lms501kf03_power_off(struct lms501kf03 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd;
+
+ pd = lcd->lcd_pd;
+
+ ret = lms501kf03_ldi_disable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "lcd setting failed.\n");
+ return -EIO;
+ }
+
+ msleep(pd->power_off_delay);
+
+ pd->power_on(lcd->ld, 0);
+
+ return 0;
+}
+
+static int lms501kf03_power(struct lms501kf03 *lcd, int power)
+{
+ int ret = 0;
+
+ if (lms501kf03_power_is_on(power) &&
+ !lms501kf03_power_is_on(lcd->power))
+ ret = lms501kf03_power_on(lcd);
+ else if (!lms501kf03_power_is_on(power) &&
+ lms501kf03_power_is_on(lcd->power))
+ ret = lms501kf03_power_off(lcd);
+
+ if (!ret)
+ lcd->power = power;
+
+ return ret;
+}
+
+static int lms501kf03_get_power(struct lcd_device *ld)
+{
+ struct lms501kf03 *lcd = lcd_get_data(ld);
+
+ return lcd->power;
+}
+
+static int lms501kf03_set_power(struct lcd_device *ld, int power)
+{
+ struct lms501kf03 *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 lms501kf03_power(lcd, power);
+}
+
+static struct lcd_ops lms501kf03_lcd_ops = {
+ .get_power = lms501kf03_get_power,
+ .set_power = lms501kf03_set_power,
+};
+
+static int lms501kf03_probe(struct spi_device *spi)
+{
+ struct lms501kf03 *lcd = NULL;
+ struct lcd_device *ld = NULL;
+ int ret = 0;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(struct lms501kf03), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ /* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */
+ spi->bits_per_word = 9;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "spi setup failed.\n");
+ return ret;
+ }
+
+ lcd->spi = spi;
+ lcd->dev = &spi->dev;
+
+ lcd->lcd_pd = dev_get_platdata(&spi->dev);
+ if (!lcd->lcd_pd) {
+ dev_err(&spi->dev, "platform data is NULL\n");
+ return -EINVAL;
+ }
+
+ ld = devm_lcd_device_register(&spi->dev, "lms501kf03", &spi->dev, lcd,
+ &lms501kf03_lcd_ops);
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
+
+ lcd->ld = ld;
+
+ 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;
+
+ lms501kf03_power(lcd, FB_BLANK_UNBLANK);
+ } else {
+ lcd->power = FB_BLANK_UNBLANK;
+ }
+
+ spi_set_drvdata(spi, lcd);
+
+ dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n");
+
+ return 0;
+}
+
+static int lms501kf03_remove(struct spi_device *spi)
+{
+ struct lms501kf03 *lcd = spi_get_drvdata(spi);
+
+ lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int lms501kf03_suspend(struct device *dev)
+{
+ struct lms501kf03 *lcd = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "lcd->power = %d\n", lcd->power);
+
+ /*
+ * when lcd panel is suspend, lcd panel becomes off
+ * regardless of status.
+ */
+ return lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static int lms501kf03_resume(struct device *dev)
+{
+ struct lms501kf03 *lcd = dev_get_drvdata(dev);
+
+ lcd->power = FB_BLANK_POWERDOWN;
+
+ return lms501kf03_power(lcd, FB_BLANK_UNBLANK);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lms501kf03_pm_ops, lms501kf03_suspend,
+ lms501kf03_resume);
+
+static void lms501kf03_shutdown(struct spi_device *spi)
+{
+ struct lms501kf03 *lcd = spi_get_drvdata(spi);
+
+ lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver lms501kf03_driver = {
+ .driver = {
+ .name = "lms501kf03",
+ .owner = THIS_MODULE,
+ .pm = &lms501kf03_pm_ops,
+ },
+ .probe = lms501kf03_probe,
+ .remove = lms501kf03_remove,
+ .shutdown = lms501kf03_shutdown,
+};
+
+module_spi_driver(lms501kf03_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("lms501kf03 LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
index 6b488b8a7ee..6c3ec4259a6 100644
--- a/drivers/video/backlight/locomolcd.c
+++ b/drivers/video/backlight/locomolcd.c
@@ -2,11 +2,11 @@
* Backlight control code for Sharp Zaurus SL-5500
*
* Copyright 2005 John Lenz <lenz@cs.wisc.edu>
- * Maintainer: Pavel Machek <pavel@suse.cz> (unless John wants to :-)
+ * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-)
* GPL v2
*
* This driver assumes single CPU. That's okay, because collie is
- * slightly old hardware, and noone is going to retrofit second CPU to
+ * slightly old hardware, and no one is going to retrofit second CPU to
* old PDA.
*/
@@ -107,7 +107,6 @@ void locomolcd_power(int on)
}
EXPORT_SYMBOL(locomolcd_power);
-
static int current_intensity;
static int locomolcd_set_intensity(struct backlight_device *bd)
@@ -122,13 +121,25 @@ static int locomolcd_set_intensity(struct backlight_device *bd)
intensity = 0;
switch (intensity) {
- /* AC and non-AC are handled differently, but produce same results in sharp code? */
- case 0: locomo_frontlight_set(locomolcd_dev, 0, 0, 161); break;
- case 1: locomo_frontlight_set(locomolcd_dev, 117, 0, 161); break;
- case 2: locomo_frontlight_set(locomolcd_dev, 163, 0, 148); break;
- case 3: locomo_frontlight_set(locomolcd_dev, 194, 0, 161); break;
- case 4: locomo_frontlight_set(locomolcd_dev, 194, 1, 161); break;
-
+ /*
+ * AC and non-AC are handled differently,
+ * but produce same results in sharp code?
+ */
+ case 0:
+ locomo_frontlight_set(locomolcd_dev, 0, 0, 161);
+ break;
+ case 1:
+ locomo_frontlight_set(locomolcd_dev, 117, 0, 161);
+ break;
+ case 2:
+ locomo_frontlight_set(locomolcd_dev, 163, 0, 148);
+ break;
+ case 3:
+ locomo_frontlight_set(locomolcd_dev, 194, 0, 161);
+ break;
+ case 4:
+ locomo_frontlight_set(locomolcd_dev, 194, 1, 161);
+ break;
default:
return -ENODEV;
}
@@ -141,32 +152,32 @@ static int locomolcd_get_intensity(struct backlight_device *bd)
return current_intensity;
}
-static struct backlight_ops locomobl_data = {
+static const struct backlight_ops locomobl_data = {
.get_brightness = locomolcd_get_intensity,
.update_status = locomolcd_set_intensity,
};
-#ifdef CONFIG_PM
-static int locomolcd_suspend(struct locomo_dev *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int locomolcd_suspend(struct device *dev)
{
locomolcd_flags |= LOCOMOLCD_SUSPENDED;
locomolcd_set_intensity(locomolcd_bl_device);
return 0;
}
-static int locomolcd_resume(struct locomo_dev *dev)
+static int locomolcd_resume(struct device *dev)
{
locomolcd_flags &= ~LOCOMOLCD_SUSPENDED;
locomolcd_set_intensity(locomolcd_bl_device);
return 0;
}
-#else
-#define locomolcd_suspend NULL
-#define locomolcd_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(locomolcd_pm_ops, locomolcd_suspend, locomolcd_resume);
+
static int locomolcd_probe(struct locomo_dev *ldev)
{
+ struct backlight_properties props;
unsigned long flags;
local_irq_save(flags);
@@ -174,21 +185,27 @@ static int locomolcd_probe(struct locomo_dev *ldev)
locomo_gpio_set_dir(ldev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
- /* the poodle_lcd_power function is called for the first time
+ /*
+ * the poodle_lcd_power function is called for the first time
* from fs_initcall, which is before locomo is activated.
- * We need to recall poodle_lcd_power here*/
+ * We need to recall poodle_lcd_power here
+ */
if (machine_is_poodle())
locomolcd_power(1);
local_irq_restore(flags);
- locomolcd_bl_device = backlight_device_register("locomo-bl", &ldev->dev, NULL, &locomobl_data);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 4;
+ locomolcd_bl_device = backlight_device_register("locomo-bl",
+ &ldev->dev, NULL,
+ &locomobl_data, &props);
- if (IS_ERR (locomolcd_bl_device))
- return PTR_ERR (locomolcd_bl_device);
+ if (IS_ERR(locomolcd_bl_device))
+ return PTR_ERR(locomolcd_bl_device);
/* Set up frontlight so that screen is readable */
- locomolcd_bl_device->props.max_brightness = 4,
locomolcd_bl_device->props.brightness = 2;
locomolcd_set_intensity(locomolcd_bl_device);
@@ -212,26 +229,17 @@ static int locomolcd_remove(struct locomo_dev *dev)
static struct locomo_driver poodle_lcd_driver = {
.drv = {
- .name = "locomo-backlight",
+ .name = "locomo-backlight",
+ .pm = &locomolcd_pm_ops,
},
.devid = LOCOMO_DEVID_BACKLIGHT,
.probe = locomolcd_probe,
.remove = locomolcd_remove,
- .suspend = locomolcd_suspend,
- .resume = locomolcd_resume,
};
-
static int __init locomolcd_init(void)
{
- int ret = locomo_driver_register(&poodle_lcd_driver);
- if (ret)
- return ret;
-
-#ifdef CONFIG_SA1100_COLLIE
- sa1100fb_lcd_power = locomolcd_power;
-#endif
- return 0;
+ return locomo_driver_register(&poodle_lcd_driver);
}
static void __exit locomolcd_exit(void)
@@ -242,6 +250,6 @@ static void __exit locomolcd_exit(void)
module_init(locomolcd_init);
module_exit(locomolcd_exit);
-MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@suse.cz>");
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@ucw.cz>");
MODULE_DESCRIPTION("Collie LCD driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
new file mode 100644
index 00000000000..2ca3a040007
--- /dev/null
+++ b/drivers/video/backlight/lp855x_bl.c
@@ -0,0 +1,505 @@
+/*
+ * TI LP855x Backlight Driver
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_data/lp855x.h>
+#include <linux/pwm.h>
+
+/* LP8550/1/2/3/6 Registers */
+#define LP855X_BRIGHTNESS_CTRL 0x00
+#define LP855X_DEVICE_CTRL 0x01
+#define LP855X_EEPROM_START 0xA0
+#define LP855X_EEPROM_END 0xA7
+#define LP8556_EPROM_START 0xA0
+#define LP8556_EPROM_END 0xAF
+
+/* LP8555/7 Registers */
+#define LP8557_BL_CMD 0x00
+#define LP8557_BL_MASK 0x01
+#define LP8557_BL_ON 0x01
+#define LP8557_BL_OFF 0x00
+#define LP8557_BRIGHTNESS_CTRL 0x04
+#define LP8557_CONFIG 0x10
+#define LP8555_EPROM_START 0x10
+#define LP8555_EPROM_END 0x7A
+#define LP8557_EPROM_START 0x10
+#define LP8557_EPROM_END 0x1E
+
+#define DEFAULT_BL_NAME "lcd-backlight"
+#define MAX_BRIGHTNESS 255
+
+enum lp855x_brightness_ctrl_mode {
+ PWM_BASED = 1,
+ REGISTER_BASED,
+};
+
+struct lp855x;
+
+/*
+ * struct lp855x_device_config
+ * @pre_init_device: init device function call before updating the brightness
+ * @reg_brightness: register address for brigthenss control
+ * @reg_devicectrl: register address for device control
+ * @post_init_device: late init device function call
+ */
+struct lp855x_device_config {
+ int (*pre_init_device)(struct lp855x *);
+ u8 reg_brightness;
+ u8 reg_devicectrl;
+ int (*post_init_device)(struct lp855x *);
+};
+
+struct lp855x {
+ const char *chipname;
+ enum lp855x_chip_id chip_id;
+ enum lp855x_brightness_ctrl_mode mode;
+ struct lp855x_device_config *cfg;
+ struct i2c_client *client;
+ struct backlight_device *bl;
+ struct device *dev;
+ struct lp855x_platform_data *pdata;
+ struct pwm_device *pwm;
+};
+
+static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
+{
+ return i2c_smbus_write_byte_data(lp->client, reg, data);
+}
+
+static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
+{
+ int ret;
+ u8 tmp;
+
+ ret = i2c_smbus_read_byte_data(lp->client, reg);
+ if (ret < 0) {
+ dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
+ return ret;
+ }
+
+ tmp = (u8)ret;
+ tmp &= ~mask;
+ tmp |= data & mask;
+
+ return lp855x_write_byte(lp, reg, tmp);
+}
+
+static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
+{
+ u8 start, end;
+
+ switch (lp->chip_id) {
+ case LP8550:
+ case LP8551:
+ case LP8552:
+ case LP8553:
+ start = LP855X_EEPROM_START;
+ end = LP855X_EEPROM_END;
+ break;
+ case LP8556:
+ start = LP8556_EPROM_START;
+ end = LP8556_EPROM_END;
+ break;
+ case LP8555:
+ start = LP8555_EPROM_START;
+ end = LP8555_EPROM_END;
+ break;
+ case LP8557:
+ start = LP8557_EPROM_START;
+ end = LP8557_EPROM_END;
+ break;
+ default:
+ return false;
+ }
+
+ return addr >= start && addr <= end;
+}
+
+static int lp8557_bl_off(struct lp855x *lp)
+{
+ /* BL_ON = 0 before updating EPROM settings */
+ return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
+ LP8557_BL_OFF);
+}
+
+static int lp8557_bl_on(struct lp855x *lp)
+{
+ /* BL_ON = 1 after updating EPROM settings */
+ return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
+ LP8557_BL_ON);
+}
+
+static struct lp855x_device_config lp855x_dev_cfg = {
+ .reg_brightness = LP855X_BRIGHTNESS_CTRL,
+ .reg_devicectrl = LP855X_DEVICE_CTRL,
+};
+
+static struct lp855x_device_config lp8557_dev_cfg = {
+ .reg_brightness = LP8557_BRIGHTNESS_CTRL,
+ .reg_devicectrl = LP8557_CONFIG,
+ .pre_init_device = lp8557_bl_off,
+ .post_init_device = lp8557_bl_on,
+};
+
+/*
+ * Device specific configuration flow
+ *
+ * a) pre_init_device(optional)
+ * b) update the brightness register
+ * c) update device control register
+ * d) update ROM area(optional)
+ * e) post_init_device(optional)
+ *
+ */
+static int lp855x_configure(struct lp855x *lp)
+{
+ u8 val, addr;
+ int i, ret;
+ struct lp855x_platform_data *pd = lp->pdata;
+
+ switch (lp->chip_id) {
+ case LP8550:
+ case LP8551:
+ case LP8552:
+ case LP8553:
+ case LP8556:
+ lp->cfg = &lp855x_dev_cfg;
+ break;
+ case LP8555:
+ case LP8557:
+ lp->cfg = &lp8557_dev_cfg;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lp->cfg->pre_init_device) {
+ ret = lp->cfg->pre_init_device(lp);
+ if (ret) {
+ dev_err(lp->dev, "pre init device err: %d\n", ret);
+ goto err;
+ }
+ }
+
+ val = pd->initial_brightness;
+ ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
+ if (ret)
+ goto err;
+
+ val = pd->device_control;
+ ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
+ if (ret)
+ goto err;
+
+ if (pd->size_program > 0) {
+ for (i = 0; i < pd->size_program; i++) {
+ addr = pd->rom_data[i].addr;
+ val = pd->rom_data[i].val;
+ if (!lp855x_is_valid_rom_area(lp, addr))
+ continue;
+
+ ret = lp855x_write_byte(lp, addr, val);
+ if (ret)
+ goto err;
+ }
+ }
+
+ if (lp->cfg->post_init_device) {
+ ret = lp->cfg->post_init_device(lp);
+ if (ret) {
+ dev_err(lp->dev, "post init device err: %d\n", ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
+{
+ unsigned int period = lp->pdata->period_ns;
+ unsigned int duty = br * period / max_br;
+ struct pwm_device *pwm;
+
+ /* request pwm device with the consumer name */
+ if (!lp->pwm) {
+ pwm = devm_pwm_get(lp->dev, lp->chipname);
+ if (IS_ERR(pwm))
+ return;
+
+ lp->pwm = pwm;
+ }
+
+ pwm_config(lp->pwm, duty, period);
+ if (duty)
+ pwm_enable(lp->pwm);
+ else
+ pwm_disable(lp->pwm);
+}
+
+static int lp855x_bl_update_status(struct backlight_device *bl)
+{
+ struct lp855x *lp = bl_get_data(bl);
+
+ if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+ bl->props.brightness = 0;
+
+ if (lp->mode == PWM_BASED) {
+ int br = bl->props.brightness;
+ int max_br = bl->props.max_brightness;
+
+ lp855x_pwm_ctrl(lp, br, max_br);
+
+ } else if (lp->mode == REGISTER_BASED) {
+ u8 val = bl->props.brightness;
+ lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
+ }
+
+ return 0;
+}
+
+static int lp855x_bl_get_brightness(struct backlight_device *bl)
+{
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops lp855x_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lp855x_bl_update_status,
+ .get_brightness = lp855x_bl_get_brightness,
+};
+
+static int lp855x_backlight_register(struct lp855x *lp)
+{
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ struct lp855x_platform_data *pdata = lp->pdata;
+ const char *name = pdata->name ? : DEFAULT_BL_NAME;
+
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = MAX_BRIGHTNESS;
+
+ if (pdata->initial_brightness > props.max_brightness)
+ pdata->initial_brightness = props.max_brightness;
+
+ props.brightness = pdata->initial_brightness;
+
+ bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
+ &lp855x_bl_ops, &props);
+ if (IS_ERR(bl))
+ return PTR_ERR(bl);
+
+ lp->bl = bl;
+
+ return 0;
+}
+
+static ssize_t lp855x_get_chip_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp855x *lp = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
+}
+
+static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp855x *lp = dev_get_drvdata(dev);
+ char *strmode = NULL;
+
+ if (lp->mode == PWM_BASED)
+ strmode = "pwm based";
+ else if (lp->mode == REGISTER_BASED)
+ strmode = "register based";
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
+}
+
+static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
+static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
+
+static struct attribute *lp855x_attributes[] = {
+ &dev_attr_chip_id.attr,
+ &dev_attr_bl_ctl_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group lp855x_attr_group = {
+ .attrs = lp855x_attributes,
+};
+
+#ifdef CONFIG_OF
+static int lp855x_parse_dt(struct device *dev, struct device_node *node)
+{
+ struct lp855x_platform_data *pdata;
+ int rom_length;
+
+ if (!node) {
+ dev_err(dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ of_property_read_string(node, "bl-name", &pdata->name);
+ of_property_read_u8(node, "dev-ctrl", &pdata->device_control);
+ of_property_read_u8(node, "init-brt", &pdata->initial_brightness);
+ of_property_read_u32(node, "pwm-period", &pdata->period_ns);
+
+ /* Fill ROM platform data if defined */
+ rom_length = of_get_child_count(node);
+ if (rom_length > 0) {
+ struct lp855x_rom_data *rom;
+ struct device_node *child;
+ int i = 0;
+
+ rom = devm_kzalloc(dev, sizeof(*rom) * rom_length, GFP_KERNEL);
+ if (!rom)
+ return -ENOMEM;
+
+ for_each_child_of_node(node, child) {
+ of_property_read_u8(child, "rom-addr", &rom[i].addr);
+ of_property_read_u8(child, "rom-val", &rom[i].val);
+ i++;
+ }
+
+ pdata->size_program = rom_length;
+ pdata->rom_data = &rom[0];
+ }
+
+ dev->platform_data = pdata;
+
+ return 0;
+}
+#else
+static int lp855x_parse_dt(struct device *dev, struct device_node *node)
+{
+ return -EINVAL;
+}
+#endif
+
+static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+ struct lp855x *lp;
+ struct lp855x_platform_data *pdata = dev_get_platdata(&cl->dev);
+ struct device_node *node = cl->dev.of_node;
+ int ret;
+
+ if (!pdata) {
+ ret = lp855x_parse_dt(&cl->dev, node);
+ if (ret < 0)
+ return ret;
+
+ pdata = dev_get_platdata(&cl->dev);
+ }
+
+ if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EIO;
+
+ lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
+ if (!lp)
+ return -ENOMEM;
+
+ if (pdata->period_ns > 0)
+ lp->mode = PWM_BASED;
+ else
+ lp->mode = REGISTER_BASED;
+
+ lp->client = cl;
+ lp->dev = &cl->dev;
+ lp->pdata = pdata;
+ lp->chipname = id->name;
+ lp->chip_id = id->driver_data;
+ i2c_set_clientdata(cl, lp);
+
+ ret = lp855x_configure(lp);
+ if (ret) {
+ dev_err(lp->dev, "device config err: %d", ret);
+ return ret;
+ }
+
+ ret = lp855x_backlight_register(lp);
+ if (ret) {
+ dev_err(lp->dev,
+ "failed to register backlight. err: %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
+ if (ret) {
+ dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
+ return ret;
+ }
+
+ backlight_update_status(lp->bl);
+ return 0;
+}
+
+static int lp855x_remove(struct i2c_client *cl)
+{
+ struct lp855x *lp = i2c_get_clientdata(cl);
+
+ lp->bl->props.brightness = 0;
+ backlight_update_status(lp->bl);
+ sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
+
+ return 0;
+}
+
+static const struct of_device_id lp855x_dt_ids[] = {
+ { .compatible = "ti,lp8550", },
+ { .compatible = "ti,lp8551", },
+ { .compatible = "ti,lp8552", },
+ { .compatible = "ti,lp8553", },
+ { .compatible = "ti,lp8555", },
+ { .compatible = "ti,lp8556", },
+ { .compatible = "ti,lp8557", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lp855x_dt_ids);
+
+static const struct i2c_device_id lp855x_ids[] = {
+ {"lp8550", LP8550},
+ {"lp8551", LP8551},
+ {"lp8552", LP8552},
+ {"lp8553", LP8553},
+ {"lp8555", LP8555},
+ {"lp8556", LP8556},
+ {"lp8557", LP8557},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp855x_ids);
+
+static struct i2c_driver lp855x_driver = {
+ .driver = {
+ .name = "lp855x",
+ .of_match_table = of_match_ptr(lp855x_dt_ids),
+ },
+ .probe = lp855x_probe,
+ .remove = lp855x_remove,
+ .id_table = lp855x_ids,
+};
+
+module_i2c_driver(lp855x_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
+MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c
new file mode 100644
index 00000000000..daba34dc46d
--- /dev/null
+++ b/drivers/video/backlight/lp8788_bl.c
@@ -0,0 +1,332 @@
+/*
+ * TI LP8788 MFD - backlight driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+/* Register address */
+#define LP8788_BL_CONFIG 0x96
+#define LP8788_BL_EN BIT(0)
+#define LP8788_BL_PWM_INPUT_EN BIT(5)
+#define LP8788_BL_FULLSCALE_SHIFT 2
+#define LP8788_BL_DIM_MODE_SHIFT 1
+#define LP8788_BL_PWM_POLARITY_SHIFT 6
+
+#define LP8788_BL_BRIGHTNESS 0x97
+
+#define LP8788_BL_RAMP 0x98
+#define LP8788_BL_RAMP_RISE_SHIFT 4
+
+#define MAX_BRIGHTNESS 127
+#define DEFAULT_BL_NAME "lcd-backlight"
+
+struct lp8788_bl_config {
+ enum lp8788_bl_ctrl_mode bl_mode;
+ enum lp8788_bl_dim_mode dim_mode;
+ enum lp8788_bl_full_scale_current full_scale;
+ enum lp8788_bl_ramp_step rise_time;
+ enum lp8788_bl_ramp_step fall_time;
+ enum pwm_polarity pwm_pol;
+};
+
+struct lp8788_bl {
+ struct lp8788 *lp;
+ struct backlight_device *bl_dev;
+ struct lp8788_backlight_platform_data *pdata;
+ enum lp8788_bl_ctrl_mode mode;
+ struct pwm_device *pwm;
+};
+
+static struct lp8788_bl_config default_bl_config = {
+ .bl_mode = LP8788_BL_REGISTER_ONLY,
+ .dim_mode = LP8788_DIM_EXPONENTIAL,
+ .full_scale = LP8788_FULLSCALE_1900uA,
+ .rise_time = LP8788_RAMP_8192us,
+ .fall_time = LP8788_RAMP_8192us,
+ .pwm_pol = PWM_POLARITY_NORMAL,
+};
+
+static inline bool is_brightness_ctrl_by_pwm(enum lp8788_bl_ctrl_mode mode)
+{
+ return mode == LP8788_BL_COMB_PWM_BASED;
+}
+
+static inline bool is_brightness_ctrl_by_register(enum lp8788_bl_ctrl_mode mode)
+{
+ return mode == LP8788_BL_REGISTER_ONLY ||
+ mode == LP8788_BL_COMB_REGISTER_BASED;
+}
+
+static int lp8788_backlight_configure(struct lp8788_bl *bl)
+{
+ struct lp8788_backlight_platform_data *pdata = bl->pdata;
+ struct lp8788_bl_config *cfg = &default_bl_config;
+ int ret;
+ u8 val;
+
+ /*
+ * Update chip configuration if platform data exists,
+ * otherwise use the default settings.
+ */
+ if (pdata) {
+ cfg->bl_mode = pdata->bl_mode;
+ cfg->dim_mode = pdata->dim_mode;
+ cfg->full_scale = pdata->full_scale;
+ cfg->rise_time = pdata->rise_time;
+ cfg->fall_time = pdata->fall_time;
+ cfg->pwm_pol = pdata->pwm_pol;
+ }
+
+ /* Brightness ramp up/down */
+ val = (cfg->rise_time << LP8788_BL_RAMP_RISE_SHIFT) | cfg->fall_time;
+ ret = lp8788_write_byte(bl->lp, LP8788_BL_RAMP, val);
+ if (ret)
+ return ret;
+
+ /* Fullscale current setting */
+ val = (cfg->full_scale << LP8788_BL_FULLSCALE_SHIFT) |
+ (cfg->dim_mode << LP8788_BL_DIM_MODE_SHIFT);
+
+ /* Brightness control mode */
+ switch (cfg->bl_mode) {
+ case LP8788_BL_REGISTER_ONLY:
+ val |= LP8788_BL_EN;
+ break;
+ case LP8788_BL_COMB_PWM_BASED:
+ case LP8788_BL_COMB_REGISTER_BASED:
+ val |= LP8788_BL_EN | LP8788_BL_PWM_INPUT_EN |
+ (cfg->pwm_pol << LP8788_BL_PWM_POLARITY_SHIFT);
+ break;
+ default:
+ dev_err(bl->lp->dev, "invalid mode: %d\n", cfg->bl_mode);
+ return -EINVAL;
+ }
+
+ bl->mode = cfg->bl_mode;
+
+ return lp8788_write_byte(bl->lp, LP8788_BL_CONFIG, val);
+}
+
+static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
+{
+ unsigned int period;
+ unsigned int duty;
+ struct device *dev;
+ struct pwm_device *pwm;
+
+ if (!bl->pdata)
+ return;
+
+ period = bl->pdata->period_ns;
+ duty = br * period / max_br;
+ dev = bl->lp->dev;
+
+ /* request PWM device with the consumer name */
+ if (!bl->pwm) {
+ pwm = devm_pwm_get(dev, LP8788_DEV_BACKLIGHT);
+ if (IS_ERR(pwm)) {
+ dev_err(dev, "can not get PWM device\n");
+ return;
+ }
+
+ bl->pwm = pwm;
+ }
+
+ pwm_config(bl->pwm, duty, period);
+ if (duty)
+ pwm_enable(bl->pwm);
+ else
+ pwm_disable(bl->pwm);
+}
+
+static int lp8788_bl_update_status(struct backlight_device *bl_dev)
+{
+ struct lp8788_bl *bl = bl_get_data(bl_dev);
+ enum lp8788_bl_ctrl_mode mode = bl->mode;
+
+ if (bl_dev->props.state & BL_CORE_SUSPENDED)
+ bl_dev->props.brightness = 0;
+
+ if (is_brightness_ctrl_by_pwm(mode)) {
+ int brt = bl_dev->props.brightness;
+ int max = bl_dev->props.max_brightness;
+
+ lp8788_pwm_ctrl(bl, brt, max);
+ } else if (is_brightness_ctrl_by_register(mode)) {
+ u8 brt = bl_dev->props.brightness;
+
+ lp8788_write_byte(bl->lp, LP8788_BL_BRIGHTNESS, brt);
+ }
+
+ return 0;
+}
+
+static int lp8788_bl_get_brightness(struct backlight_device *bl_dev)
+{
+ return bl_dev->props.brightness;
+}
+
+static const struct backlight_ops lp8788_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lp8788_bl_update_status,
+ .get_brightness = lp8788_bl_get_brightness,
+};
+
+static int lp8788_backlight_register(struct lp8788_bl *bl)
+{
+ struct backlight_device *bl_dev;
+ struct backlight_properties props;
+ struct lp8788_backlight_platform_data *pdata = bl->pdata;
+ int init_brt;
+ char *name;
+
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = MAX_BRIGHTNESS;
+
+ /* Initial brightness */
+ if (pdata)
+ init_brt = min_t(int, pdata->initial_brightness,
+ props.max_brightness);
+ else
+ init_brt = 0;
+
+ props.brightness = init_brt;
+
+ /* Backlight device name */
+ if (!pdata || !pdata->name)
+ name = DEFAULT_BL_NAME;
+ else
+ name = pdata->name;
+
+ bl_dev = backlight_device_register(name, bl->lp->dev, bl,
+ &lp8788_bl_ops, &props);
+ if (IS_ERR(bl_dev))
+ return PTR_ERR(bl_dev);
+
+ bl->bl_dev = bl_dev;
+
+ return 0;
+}
+
+static void lp8788_backlight_unregister(struct lp8788_bl *bl)
+{
+ struct backlight_device *bl_dev = bl->bl_dev;
+
+ if (bl_dev)
+ backlight_device_unregister(bl_dev);
+}
+
+static ssize_t lp8788_get_bl_ctl_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8788_bl *bl = dev_get_drvdata(dev);
+ enum lp8788_bl_ctrl_mode mode = bl->mode;
+ char *strmode;
+
+ if (is_brightness_ctrl_by_pwm(mode))
+ strmode = "PWM based";
+ else if (is_brightness_ctrl_by_register(mode))
+ strmode = "Register based";
+ else
+ strmode = "Invalid mode";
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
+}
+
+static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp8788_get_bl_ctl_mode, NULL);
+
+static struct attribute *lp8788_attributes[] = {
+ &dev_attr_bl_ctl_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group lp8788_attr_group = {
+ .attrs = lp8788_attributes,
+};
+
+static int lp8788_backlight_probe(struct platform_device *pdev)
+{
+ struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+ struct lp8788_bl *bl;
+ int ret;
+
+ bl = devm_kzalloc(lp->dev, sizeof(struct lp8788_bl), GFP_KERNEL);
+ if (!bl)
+ return -ENOMEM;
+
+ bl->lp = lp;
+ if (lp->pdata)
+ bl->pdata = lp->pdata->bl_pdata;
+
+ platform_set_drvdata(pdev, bl);
+
+ ret = lp8788_backlight_configure(bl);
+ if (ret) {
+ dev_err(lp->dev, "backlight config err: %d\n", ret);
+ goto err_dev;
+ }
+
+ ret = lp8788_backlight_register(bl);
+ if (ret) {
+ dev_err(lp->dev, "register backlight err: %d\n", ret);
+ goto err_dev;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group);
+ if (ret) {
+ dev_err(lp->dev, "register sysfs err: %d\n", ret);
+ goto err_sysfs;
+ }
+
+ backlight_update_status(bl->bl_dev);
+
+ return 0;
+
+err_sysfs:
+ lp8788_backlight_unregister(bl);
+err_dev:
+ return ret;
+}
+
+static int lp8788_backlight_remove(struct platform_device *pdev)
+{
+ struct lp8788_bl *bl = platform_get_drvdata(pdev);
+ struct backlight_device *bl_dev = bl->bl_dev;
+
+ bl_dev->props.brightness = 0;
+ backlight_update_status(bl_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group);
+ lp8788_backlight_unregister(bl);
+
+ return 0;
+}
+
+static struct platform_driver lp8788_bl_driver = {
+ .probe = lp8788_backlight_probe,
+ .remove = lp8788_backlight_remove,
+ .driver = {
+ .name = LP8788_DEV_BACKLIGHT,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(lp8788_bl_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-backlight");
diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c
index 2eb206bf73e..383f550e165 100644
--- a/drivers/video/backlight/ltv350qv.c
+++ b/drivers/video/backlight/ltv350qv.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/lcd.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spi/spi.h>
#include "ltv350qv.h"
@@ -74,7 +75,7 @@ static int ltv350qv_power_on(struct ltv350qv *lcd)
/* Power On Reset Display off State */
if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, 0x0000))
goto err;
- msleep(15);
+ usleep_range(15000, 16000);
/* Power Setting Function 1 */
if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE))
@@ -152,7 +153,7 @@ err_settings:
err_power2:
err_power1:
ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000);
- msleep(1);
+ usleep_range(1000, 1100);
err:
ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE);
return -EIO;
@@ -174,7 +175,7 @@ static int ltv350qv_power_off(struct ltv350qv *lcd)
ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000);
/* Wait at least 1 ms */
- msleep(1);
+ usleep_range(1000, 1100);
/* Power down setting 2 */
ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE);
@@ -225,76 +226,68 @@ static struct lcd_ops ltv_ops = {
.set_power = ltv350qv_set_power,
};
-static int __devinit ltv350qv_probe(struct spi_device *spi)
+static int ltv350qv_probe(struct spi_device *spi)
{
struct ltv350qv *lcd;
struct lcd_device *ld;
int ret;
- lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL);
+ lcd = devm_kzalloc(&spi->dev, sizeof(struct ltv350qv), GFP_KERNEL);
if (!lcd)
return -ENOMEM;
lcd->spi = spi;
lcd->power = FB_BLANK_POWERDOWN;
- lcd->buffer = kzalloc(8, GFP_KERNEL);
+ lcd->buffer = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
+ if (!lcd->buffer)
+ return -ENOMEM;
+
+ ld = devm_lcd_device_register(&spi->dev, "ltv350qv", &spi->dev, lcd,
+ &ltv_ops);
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
- ld = lcd_device_register("ltv350qv", &spi->dev, lcd, &ltv_ops);
- if (IS_ERR(ld)) {
- ret = PTR_ERR(ld);
- goto out_free_lcd;
- }
lcd->ld = ld;
ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK);
if (ret)
- goto out_unregister;
+ return ret;
- dev_set_drvdata(&spi->dev, lcd);
+ spi_set_drvdata(spi, lcd);
return 0;
-
-out_unregister:
- lcd_device_unregister(ld);
-out_free_lcd:
- kfree(lcd);
- return ret;
}
-static int __devexit ltv350qv_remove(struct spi_device *spi)
+static int ltv350qv_remove(struct spi_device *spi)
{
- struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
+ struct ltv350qv *lcd = spi_get_drvdata(spi);
ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
- lcd_device_unregister(lcd->ld);
- kfree(lcd);
-
return 0;
}
-#ifdef CONFIG_PM
-static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int ltv350qv_suspend(struct device *dev)
{
- struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
+ struct ltv350qv *lcd = dev_get_drvdata(dev);
return ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
}
-static int ltv350qv_resume(struct spi_device *spi)
+static int ltv350qv_resume(struct device *dev)
{
- struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
+ struct ltv350qv *lcd = dev_get_drvdata(dev);
return ltv350qv_power(lcd, FB_BLANK_UNBLANK);
}
-#else
-#define ltv350qv_suspend NULL
-#define ltv350qv_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(ltv350qv_pm_ops, ltv350qv_suspend, ltv350qv_resume);
+
/* Power down all displays on reboot, poweroff or halt */
static void ltv350qv_shutdown(struct spi_device *spi)
{
- struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
+ struct ltv350qv *lcd = spi_get_drvdata(spi);
ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
}
@@ -302,29 +295,18 @@ static void ltv350qv_shutdown(struct spi_device *spi)
static struct spi_driver ltv350qv_driver = {
.driver = {
.name = "ltv350qv",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .pm = &ltv350qv_pm_ops,
},
.probe = ltv350qv_probe,
- .remove = __devexit_p(ltv350qv_remove),
+ .remove = ltv350qv_remove,
.shutdown = ltv350qv_shutdown,
- .suspend = ltv350qv_suspend,
- .resume = ltv350qv_resume,
};
-static int __init ltv350qv_init(void)
-{
- return spi_register_driver(&ltv350qv_driver);
-}
-
-static void __exit ltv350qv_exit(void)
-{
- spi_unregister_driver(&ltv350qv_driver);
-}
-module_init(ltv350qv_init);
-module_exit(ltv350qv_exit);
+module_spi_driver(ltv350qv_driver);
-MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");
+MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ltv350qv");
diff --git a/drivers/video/backlight/lv5207lp.c b/drivers/video/backlight/lv5207lp.c
new file mode 100644
index 00000000000..1802b2d1357
--- /dev/null
+++ b/drivers/video/backlight/lv5207lp.c
@@ -0,0 +1,170 @@
+/*
+ * Sanyo LV5207LP LED Driver
+ *
+ * Copyright (C) 2013 Ideas on board SPRL
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_data/lv5207lp.h>
+#include <linux/slab.h>
+
+#define LV5207LP_CTRL1 0x00
+#define LV5207LP_CPSW (1 << 7)
+#define LV5207LP_SCTEN (1 << 6)
+#define LV5207LP_C10 (1 << 5)
+#define LV5207LP_CKSW (1 << 4)
+#define LV5207LP_RSW (1 << 3)
+#define LV5207LP_GSW (1 << 2)
+#define LV5207LP_BSW (1 << 1)
+#define LV5207LP_CTRL2 0x01
+#define LV5207LP_MSW (1 << 7)
+#define LV5207LP_MLED4 (1 << 6)
+#define LV5207LP_RED 0x02
+#define LV5207LP_GREEN 0x03
+#define LV5207LP_BLUE 0x04
+
+#define LV5207LP_MAX_BRIGHTNESS 32
+
+struct lv5207lp {
+ struct i2c_client *client;
+ struct backlight_device *backlight;
+ struct lv5207lp_platform_data *pdata;
+};
+
+static int lv5207lp_write(struct lv5207lp *lv, u8 reg, u8 data)
+{
+ return i2c_smbus_write_byte_data(lv->client, reg, data);
+}
+
+static int lv5207lp_backlight_update_status(struct backlight_device *backlight)
+{
+ struct lv5207lp *lv = bl_get_data(backlight);
+ int brightness = backlight->props.brightness;
+
+ if (backlight->props.power != FB_BLANK_UNBLANK ||
+ backlight->props.fb_blank != FB_BLANK_UNBLANK ||
+ backlight->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+ brightness = 0;
+
+ if (brightness) {
+ lv5207lp_write(lv, LV5207LP_CTRL1,
+ LV5207LP_CPSW | LV5207LP_C10 | LV5207LP_CKSW);
+ lv5207lp_write(lv, LV5207LP_CTRL2,
+ LV5207LP_MSW | LV5207LP_MLED4 |
+ (brightness - 1));
+ } else {
+ lv5207lp_write(lv, LV5207LP_CTRL1, 0);
+ lv5207lp_write(lv, LV5207LP_CTRL2, 0);
+ }
+
+ return 0;
+}
+
+static int lv5207lp_backlight_get_brightness(struct backlight_device *backlight)
+{
+ return backlight->props.brightness;
+}
+
+static int lv5207lp_backlight_check_fb(struct backlight_device *backlight,
+ struct fb_info *info)
+{
+ struct lv5207lp *lv = bl_get_data(backlight);
+
+ return lv->pdata->fbdev == NULL || lv->pdata->fbdev == info->dev;
+}
+
+static const struct backlight_ops lv5207lp_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lv5207lp_backlight_update_status,
+ .get_brightness = lv5207lp_backlight_get_brightness,
+ .check_fb = lv5207lp_backlight_check_fb,
+};
+
+static int lv5207lp_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lv5207lp_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct backlight_device *backlight;
+ struct backlight_properties props;
+ struct lv5207lp *lv;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&client->dev,
+ "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ lv = devm_kzalloc(&client->dev, sizeof(*lv), GFP_KERNEL);
+ if (!lv)
+ return -ENOMEM;
+
+ lv->client = client;
+ lv->pdata = pdata;
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = min_t(unsigned int, pdata->max_value,
+ LV5207LP_MAX_BRIGHTNESS);
+ props.brightness = clamp_t(unsigned int, pdata->def_value, 0,
+ props.max_brightness);
+
+ backlight = devm_backlight_device_register(&client->dev,
+ dev_name(&client->dev), &lv->client->dev,
+ lv, &lv5207lp_backlight_ops, &props);
+ if (IS_ERR(backlight)) {
+ dev_err(&client->dev, "failed to register backlight\n");
+ return PTR_ERR(backlight);
+ }
+
+ backlight_update_status(backlight);
+ i2c_set_clientdata(client, backlight);
+
+ return 0;
+}
+
+static int lv5207lp_remove(struct i2c_client *client)
+{
+ struct backlight_device *backlight = i2c_get_clientdata(client);
+
+ backlight->props.brightness = 0;
+ backlight_update_status(backlight);
+
+ return 0;
+}
+
+static const struct i2c_device_id lv5207lp_ids[] = {
+ { "lv5207lp", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lv5207lp_ids);
+
+static struct i2c_driver lv5207lp_driver = {
+ .driver = {
+ .name = "lv5207lp",
+ },
+ .probe = lv5207lp_probe,
+ .remove = lv5207lp_remove,
+ .id_table = lv5207lp_ids,
+};
+
+module_i2c_driver(lv5207lp_driver);
+
+MODULE_DESCRIPTION("Sanyo LV5207LP Backlight Driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c
new file mode 100644
index 00000000000..66fa08c920d
--- /dev/null
+++ b/drivers/video/backlight/max8925_bl.c
@@ -0,0 +1,210 @@
+/*
+ * Backlight driver for Maxim MAX8925
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/backlight.h>
+#include <linux/mfd/max8925.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define MAX_BRIGHTNESS (0xff)
+#define MIN_BRIGHTNESS (0)
+
+#define LWX_FREQ(x) (((x - 601) / 100) & 0x7)
+
+struct max8925_backlight_data {
+ struct max8925_chip *chip;
+
+ int current_brightness;
+ int reg_mode_cntl;
+ int reg_cntl;
+};
+
+static int max8925_backlight_set(struct backlight_device *bl, int brightness)
+{
+ struct max8925_backlight_data *data = bl_get_data(bl);
+ struct max8925_chip *chip = data->chip;
+ unsigned char value;
+ int ret;
+
+ if (brightness > MAX_BRIGHTNESS)
+ value = MAX_BRIGHTNESS;
+ else
+ value = brightness;
+
+ ret = max8925_reg_write(chip->i2c, data->reg_cntl, value);
+ if (ret < 0)
+ goto out;
+
+ if (!data->current_brightness && brightness)
+ /* enable WLED output */
+ ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 1, 1);
+ else if (!brightness)
+ /* disable WLED output */
+ ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 1, 0);
+ if (ret < 0)
+ goto out;
+ dev_dbg(chip->dev, "set brightness %d\n", value);
+ data->current_brightness = value;
+ return 0;
+out:
+ dev_dbg(chip->dev, "set brightness %d failure with return value:%d\n",
+ value, ret);
+ return ret;
+}
+
+static int max8925_backlight_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;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ return max8925_backlight_set(bl, brightness);
+}
+
+static int max8925_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct max8925_backlight_data *data = bl_get_data(bl);
+ struct max8925_chip *chip = data->chip;
+ int ret;
+
+ ret = max8925_reg_read(chip->i2c, data->reg_cntl);
+ if (ret < 0)
+ return -EINVAL;
+ data->current_brightness = ret;
+ dev_dbg(chip->dev, "get brightness %d\n", data->current_brightness);
+ return ret;
+}
+
+static const struct backlight_ops max8925_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = max8925_backlight_update_status,
+ .get_brightness = max8925_backlight_get_brightness,
+};
+
+static void max8925_backlight_dt_init(struct platform_device *pdev)
+{
+ struct device_node *nproot = pdev->dev.parent->of_node, *np;
+ struct max8925_backlight_pdata *pdata;
+ u32 val;
+
+ if (!nproot || !IS_ENABLED(CONFIG_OF))
+ return;
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct max8925_backlight_pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return;
+
+ np = of_find_node_by_name(nproot, "backlight");
+ if (!np) {
+ dev_err(&pdev->dev, "failed to find backlight node\n");
+ return;
+ }
+
+ if (!of_property_read_u32(np, "maxim,max8925-dual-string", &val))
+ pdata->dual_string = val;
+
+ pdev->dev.platform_data = pdata;
+}
+
+static int max8925_backlight_probe(struct platform_device *pdev)
+{
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct max8925_backlight_pdata *pdata;
+ struct max8925_backlight_data *data;
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ struct resource *res;
+ unsigned char value;
+ int ret = 0;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct max8925_backlight_data),
+ GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for mode control!\n");
+ return -ENXIO;
+ }
+ data->reg_mode_cntl = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_REG, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "No REG resource for control!\n");
+ return -ENXIO;
+ }
+ data->reg_cntl = res->start;
+
+ data->chip = chip;
+ data->current_brightness = 0;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = MAX_BRIGHTNESS;
+ bl = devm_backlight_device_register(&pdev->dev, "max8925-backlight",
+ &pdev->dev, data,
+ &max8925_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+ bl->props.brightness = MAX_BRIGHTNESS;
+
+ platform_set_drvdata(pdev, bl);
+
+ value = 0;
+ if (!pdev->dev.platform_data)
+ max8925_backlight_dt_init(pdev);
+
+ pdata = pdev->dev.platform_data;
+ if (pdata) {
+ if (pdata->lxw_scl)
+ value |= (1 << 7);
+ if (pdata->lxw_freq)
+ value |= (LWX_FREQ(pdata->lxw_freq) << 4);
+ if (pdata->dual_string)
+ value |= (1 << 1);
+ }
+ ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value);
+ if (ret < 0)
+ return ret;
+ backlight_update_status(bl);
+ return 0;
+}
+
+static struct platform_driver max8925_backlight_driver = {
+ .driver = {
+ .name = "max8925-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8925_backlight_probe,
+};
+
+module_platform_driver(max8925_backlight_driver);
+
+MODULE_DESCRIPTION("Backlight Driver for Maxim MAX8925");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max8925-backlight");
diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c
deleted file mode 100644
index 385cba40ea8..00000000000
--- a/drivers/video/backlight/mbp_nvidia_bl.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Backlight Driver for Nvidia 8600 in Macbook Pro
- *
- * Copyright (c) Red Hat <mjg@redhat.com>
- * Based on code from Pommed:
- * Copyright (C) 2006 Nicolas Boichat <nicolas @boichat.ch>
- * Copyright (C) 2006 Felipe Alfaro Solana <felipe_alfaro @linuxmail.org>
- * Copyright (C) 2007 Julien BLACHE <jb@jblache.org>
- *
- * 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 triggers SMIs which cause the firmware to change the
- * backlight brightness. This is icky in many ways, but it's impractical to
- * get at the firmware code in order to figure out what it's actually doing.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/backlight.h>
-#include <linux/err.h>
-#include <linux/dmi.h>
-#include <linux/io.h>
-
-static struct backlight_device *mbp_backlight_device;
-
-static struct dmi_system_id __initdata mbp_device_table[] = {
- {
- .ident = "3,1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
- },
- },
- {
- .ident = "3,2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"),
- },
- },
- {
- .ident = "4,1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"),
- },
- },
- { }
-};
-
-static int mbp_send_intensity(struct backlight_device *bd)
-{
- int intensity = bd->props.brightness;
-
- outb(0x04 | (intensity << 4), 0xb3);
- outb(0xbf, 0xb2);
-
- return 0;
-}
-
-static int mbp_get_intensity(struct backlight_device *bd)
-{
- outb(0x03, 0xb3);
- outb(0xbf, 0xb2);
- return inb(0xb3) >> 4;
-}
-
-static struct backlight_ops mbp_ops = {
- .get_brightness = mbp_get_intensity,
- .update_status = mbp_send_intensity,
-};
-
-static int __init mbp_init(void)
-{
- if (!dmi_check_system(mbp_device_table))
- return -ENODEV;
-
- if (!request_region(0xb2, 2, "Macbook Pro backlight"))
- return -ENXIO;
-
- mbp_backlight_device = backlight_device_register("mbp_backlight",
- NULL, NULL,
- &mbp_ops);
- if (IS_ERR(mbp_backlight_device)) {
- release_region(0xb2, 2);
- return PTR_ERR(mbp_backlight_device);
- }
-
- mbp_backlight_device->props.max_brightness = 15;
- mbp_backlight_device->props.brightness =
- mbp_get_intensity(mbp_backlight_device);
- backlight_update_status(mbp_backlight_device);
-
- return 0;
-}
-
-static void __exit mbp_exit(void)
-{
- backlight_device_unregister(mbp_backlight_device);
-
- release_region(0xb2, 2);
-}
-
-module_init(mbp_init);
-module_exit(mbp_exit);
-
-MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
-MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,1");
-MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,2");
-MODULE_ALIAS("svnAppleInc.:pnMacBookPro4,1");
diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c
index 891875d53a4..a0dcd88ac74 100644
--- a/drivers/video/backlight/omap1_bl.c
+++ b/drivers/video/backlight/omap1_bl.c
@@ -24,10 +24,11 @@
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
+#include <linux/slab.h>
+#include <linux/platform_data/omap1_bl.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/board.h>
-#include <asm/arch/mux.h>
+#include <mach/hardware.h>
+#include <mach/mux.h>
#define OMAPBL_MAX_INTENSITY 0xff
@@ -39,12 +40,12 @@ struct omap_backlight {
struct omap_backlight_config *pdata;
};
-static void inline omapbl_send_intensity(int intensity)
+static inline void omapbl_send_intensity(int intensity)
{
omap_writeb(intensity, OMAP_PWL_ENABLE);
}
-static void inline omapbl_send_enable(int enable)
+static inline void omapbl_send_enable(int enable)
{
omap_writeb(enable, OMAP_PWL_CLK_ENABLE);
}
@@ -70,32 +71,29 @@ static void omapbl_blank(struct omap_backlight *bl, int mode)
}
}
-#ifdef CONFIG_PM
-static int omapbl_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int omapbl_suspend(struct device *dev)
{
- struct backlight_device *dev = platform_get_drvdata(pdev);
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+ struct backlight_device *bl_dev = dev_get_drvdata(dev);
+ struct omap_backlight *bl = bl_get_data(bl_dev);
omapbl_blank(bl, FB_BLANK_POWERDOWN);
return 0;
}
-static int omapbl_resume(struct platform_device *pdev)
+static int omapbl_resume(struct device *dev)
{
- struct backlight_device *dev = platform_get_drvdata(pdev);
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+ struct backlight_device *bl_dev = dev_get_drvdata(dev);
+ struct omap_backlight *bl = bl_get_data(bl_dev);
omapbl_blank(bl, bl->powermode);
return 0;
}
-#else
-#define omapbl_suspend NULL
-#define omapbl_resume NULL
#endif
static int omapbl_set_power(struct backlight_device *dev, int state)
{
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+ struct omap_backlight *bl = bl_get_data(dev);
omapbl_blank(bl, state);
bl->powermode = state;
@@ -105,7 +103,7 @@ static int omapbl_set_power(struct backlight_device *dev, int state)
static int omapbl_update_status(struct backlight_device *dev)
{
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+ struct omap_backlight *bl = bl_get_data(dev);
if (bl->current_intensity != dev->props.brightness) {
if (bl->powermode == FB_BLANK_UNBLANK)
@@ -121,35 +119,37 @@ static int omapbl_update_status(struct backlight_device *dev)
static int omapbl_get_intensity(struct backlight_device *dev)
{
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+ struct omap_backlight *bl = bl_get_data(dev);
return bl->current_intensity;
}
-static struct backlight_ops omapbl_ops = {
+static const struct backlight_ops omapbl_ops = {
.get_brightness = omapbl_get_intensity,
.update_status = omapbl_update_status,
};
static int omapbl_probe(struct platform_device *pdev)
{
+ struct backlight_properties props;
struct backlight_device *dev;
struct omap_backlight *bl;
- struct omap_backlight_config *pdata = pdev->dev.platform_data;
+ struct omap_backlight_config *pdata = dev_get_platdata(&pdev->dev);
if (!pdata)
return -ENXIO;
- omapbl_ops.check_fb = pdata->check_fb;
-
- bl = kzalloc(sizeof(struct omap_backlight), GFP_KERNEL);
+ bl = devm_kzalloc(&pdev->dev, sizeof(struct omap_backlight),
+ GFP_KERNEL);
if (unlikely(!bl))
return -ENOMEM;
- dev = backlight_device_register("omap-bl", &pdev->dev, bl, &omapbl_ops);
- if (IS_ERR(dev)) {
- kfree(bl);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = OMAPBL_MAX_INTENSITY;
+ dev = devm_backlight_device_register(&pdev->dev, "omap-bl", &pdev->dev,
+ bl, &omapbl_ops, &props);
+ if (IS_ERR(dev))
return PTR_ERR(dev);
- }
bl->powermode = FB_BLANK_POWERDOWN;
bl->current_intensity = 0;
@@ -162,48 +162,25 @@ static int omapbl_probe(struct platform_device *pdev)
omap_cfg_reg(PWL); /* Conflicts with UART3 */
dev->props.fb_blank = FB_BLANK_UNBLANK;
- dev->props.max_brightness = OMAPBL_MAX_INTENSITY;
dev->props.brightness = pdata->default_intensity;
omapbl_update_status(dev);
- printk(KERN_INFO "OMAP LCD backlight initialised\n");
+ dev_info(&pdev->dev, "OMAP LCD backlight initialised\n");
return 0;
}
-static int omapbl_remove(struct platform_device *pdev)
-{
- struct backlight_device *dev = platform_get_drvdata(pdev);
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
-
- backlight_device_unregister(dev);
- kfree(bl);
-
- return 0;
-}
+static SIMPLE_DEV_PM_OPS(omapbl_pm_ops, omapbl_suspend, omapbl_resume);
static struct platform_driver omapbl_driver = {
.probe = omapbl_probe,
- .remove = omapbl_remove,
- .suspend = omapbl_suspend,
- .resume = omapbl_resume,
.driver = {
.name = "omap-bl",
+ .pm = &omapbl_pm_ops,
},
};
-static int __init omapbl_init(void)
-{
- return platform_driver_register(&omapbl_driver);
-}
-
-static void __exit omapbl_exit(void)
-{
- platform_driver_unregister(&omapbl_driver);
-}
-
-module_init(omapbl_init);
-module_exit(omapbl_exit);
+module_platform_driver(omapbl_driver);
MODULE_AUTHOR("Andrzej Zaborowski <balrog@zabor.org>");
MODULE_DESCRIPTION("OMAP LCD Backlight driver");
diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c
new file mode 100644
index 00000000000..f5a5202dd79
--- /dev/null
+++ b/drivers/video/backlight/ot200_bl.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2012 Bachmann electronic GmbH
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ *
+ * Backlight driver for ot200 visualisation device from
+ * Bachmann electronic GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/cs5535.h>
+
+static struct cs5535_mfgpt_timer *pwm_timer;
+
+/* this array defines the mapping of brightness in % to pwm frequency */
+static const u8 dim_table[101] = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
+ 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9,
+ 10, 10, 11, 11, 12, 12, 13, 14, 15, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28,
+ 30, 31, 33, 35, 37, 39, 41, 43, 45, 47, 50,
+ 53, 55, 58, 61, 65, 68, 72, 75, 79, 84, 88,
+ 93, 97, 103, 108, 114, 120, 126, 133, 140,
+ 147, 155, 163};
+
+struct ot200_backlight_data {
+ int current_brightness;
+};
+
+#define GPIO_DIMM 27
+#define SCALE 1
+#define CMP1MODE 0x2 /* compare on GE; output high on compare
+ * greater than or equal */
+#define PWM_SETUP (SCALE | CMP1MODE << 6 | MFGPT_SETUP_CNTEN)
+#define MAX_COMP2 163
+
+static int ot200_backlight_update_status(struct backlight_device *bl)
+{
+ struct ot200_backlight_data *data = bl_get_data(bl);
+ int brightness = bl->props.brightness;
+
+ if (bl->props.state & BL_CORE_FBBLANK)
+ brightness = 0;
+
+ /* enable or disable PWM timer */
+ if (brightness == 0)
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, 0);
+ else if (data->current_brightness == 0) {
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP,
+ MFGPT_SETUP_CNTEN);
+ }
+
+ /* apply new brightness value */
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1,
+ MAX_COMP2 - dim_table[brightness]);
+ data->current_brightness = brightness;
+
+ return 0;
+}
+
+static int ot200_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct ot200_backlight_data *data = bl_get_data(bl);
+ return data->current_brightness;
+}
+
+static const struct backlight_ops ot200_backlight_ops = {
+ .update_status = ot200_backlight_update_status,
+ .get_brightness = ot200_backlight_get_brightness,
+};
+
+static int ot200_backlight_probe(struct platform_device *pdev)
+{
+ struct backlight_device *bl;
+ struct ot200_backlight_data *data;
+ struct backlight_properties props;
+ int retval = 0;
+
+ /* request gpio */
+ if (devm_gpio_request(&pdev->dev, GPIO_DIMM,
+ "ot200 backlight dimmer") < 0) {
+ dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM);
+ return -ENODEV;
+ }
+
+ /* request timer */
+ pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY);
+ if (!pwm_timer) {
+ dev_err(&pdev->dev, "MFGPT 7 not available\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ retval = -ENOMEM;
+ goto error_devm_kzalloc;
+ }
+
+ /* setup gpio */
+ cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_ENABLE);
+ cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_AUX1);
+
+ /* setup timer */
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, 0);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP2, MAX_COMP2);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, PWM_SETUP);
+
+ data->current_brightness = 100;
+ props.max_brightness = 100;
+ props.brightness = 100;
+ props.type = BACKLIGHT_RAW;
+
+ bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev),
+ &pdev->dev, data, &ot200_backlight_ops,
+ &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ retval = PTR_ERR(bl);
+ goto error_devm_kzalloc;
+ }
+
+ platform_set_drvdata(pdev, bl);
+
+ return 0;
+
+error_devm_kzalloc:
+ cs5535_mfgpt_free_timer(pwm_timer);
+ return retval;
+}
+
+static int ot200_backlight_remove(struct platform_device *pdev)
+{
+ /* on module unload set brightness to 100% */
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1,
+ MAX_COMP2 - dim_table[100]);
+
+ cs5535_mfgpt_free_timer(pwm_timer);
+
+ return 0;
+}
+
+static struct platform_driver ot200_backlight_driver = {
+ .driver = {
+ .name = "ot200-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = ot200_backlight_probe,
+ .remove = ot200_backlight_remove,
+};
+
+module_platform_driver(ot200_backlight_driver);
+
+MODULE_DESCRIPTION("backlight driver for ot200 visualisation device");
+MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ot200-backlight");
diff --git a/drivers/video/backlight/pandora_bl.c b/drivers/video/backlight/pandora_bl.c
new file mode 100644
index 00000000000..2098c5d6efb
--- /dev/null
+++ b/drivers/video/backlight/pandora_bl.c
@@ -0,0 +1,161 @@
+/*
+ * Backlight driver for Pandora handheld.
+ * Pandora uses TWL4030 PWM0 -> TPS61161 combo for control backlight.
+ * Based on pwm_bl.c
+ *
+ * Copyright 2009,2012 Gražvydas Ignotas <notasas@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/i2c/twl.h>
+#include <linux/err.h>
+
+#define TWL_PWM0_ON 0x00
+#define TWL_PWM0_OFF 0x01
+
+#define TWL_INTBR_GPBR1 0x0c
+#define TWL_INTBR_PMBR1 0x0d
+
+#define TWL_PMBR1_PWM0_MUXMASK 0x0c
+#define TWL_PMBR1_PWM0 0x04
+#define PWM0_CLK_ENABLE BIT(0)
+#define PWM0_ENABLE BIT(2)
+
+/* range accepted by hardware */
+#define MIN_VALUE 9
+#define MAX_VALUE 63
+#define MAX_USER_VALUE (MAX_VALUE - MIN_VALUE)
+
+#define PANDORABL_WAS_OFF BL_CORE_DRIVER1
+
+static int pandora_backlight_update_status(struct backlight_device *bl)
+{
+ int brightness = bl->props.brightness;
+ u8 r;
+
+ if (bl->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+ if (bl->props.state & BL_CORE_FBBLANK)
+ brightness = 0;
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ if ((unsigned int)brightness > MAX_USER_VALUE)
+ brightness = MAX_USER_VALUE;
+
+ if (brightness == 0) {
+ if (bl->props.state & PANDORABL_WAS_OFF)
+ goto done;
+
+ /* first disable PWM0 output, then clock */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1);
+ r &= ~PWM0_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+ r &= ~PWM0_CLK_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+
+ goto done;
+ }
+
+ if (bl->props.state & PANDORABL_WAS_OFF) {
+ /*
+ * set PWM duty cycle to max. TPS61161 seems to use this
+ * to calibrate it's PWM sensitivity when it starts.
+ */
+ twl_i2c_write_u8(TWL_MODULE_PWM, MAX_VALUE, TWL_PWM0_OFF);
+
+ /* first enable clock, then PWM0 out */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1);
+ r &= ~PWM0_ENABLE;
+ r |= PWM0_CLK_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+ r |= PWM0_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+
+ /*
+ * TI made it very easy to enable digital control, so easy that
+ * it often triggers unintentionally and disabes PWM control,
+ * so wait until 1 wire mode detection window ends.
+ */
+ usleep_range(2000, 10000);
+ }
+
+ twl_i2c_write_u8(TWL_MODULE_PWM, MIN_VALUE + brightness, TWL_PWM0_OFF);
+
+done:
+ if (brightness != 0)
+ bl->props.state &= ~PANDORABL_WAS_OFF;
+ else
+ bl->props.state |= PANDORABL_WAS_OFF;
+
+ return 0;
+}
+
+static int pandora_backlight_get_brightness(struct backlight_device *bl)
+{
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops pandora_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = pandora_backlight_update_status,
+ .get_brightness = pandora_backlight_get_brightness,
+};
+
+static int pandora_backlight_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ u8 r;
+
+ memset(&props, 0, sizeof(props));
+ props.max_brightness = MAX_USER_VALUE;
+ props.type = BACKLIGHT_RAW;
+ bl = devm_backlight_device_register(&pdev->dev, pdev->name, &pdev->dev,
+ NULL, &pandora_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ platform_set_drvdata(pdev, bl);
+
+ /* 64 cycle period, ON position 0 */
+ twl_i2c_write_u8(TWL_MODULE_PWM, 0x80, TWL_PWM0_ON);
+
+ bl->props.state |= PANDORABL_WAS_OFF;
+ bl->props.brightness = MAX_USER_VALUE;
+ backlight_update_status(bl);
+
+ /* enable PWM function in pin mux */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_PMBR1);
+ r &= ~TWL_PMBR1_PWM0_MUXMASK;
+ r |= TWL_PMBR1_PWM0;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_PMBR1);
+
+ return 0;
+}
+
+static struct platform_driver pandora_backlight_driver = {
+ .driver = {
+ .name = "pandora-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = pandora_backlight_probe,
+};
+
+module_platform_driver(pandora_backlight_driver);
+
+MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("Pandora Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pandora-backlight");
diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c
new file mode 100644
index 00000000000..b95d3b0aaff
--- /dev/null
+++ b/drivers/video/backlight/pcf50633-backlight.c
@@ -0,0 +1,163 @@
+/*
+ * 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 pcf50633_bl_probe(struct platform_device *pdev)
+{
+ struct pcf50633_bl *pcf_bl;
+ struct device *parent = pdev->dev.parent;
+ struct pcf50633_platform_data *pcf50633_data = dev_get_platdata(parent);
+ struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data;
+ struct backlight_properties bl_props;
+
+ pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL);
+ if (!pcf_bl)
+ return -ENOMEM;
+
+ memset(&bl_props, 0, sizeof(bl_props));
+ bl_props.type = BACKLIGHT_RAW;
+ 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 = devm_backlight_device_register(&pdev->dev, pdev->name,
+ &pdev->dev, pcf_bl,
+ &pcf50633_bl_ops, &bl_props);
+
+ if (IS_ERR(pcf_bl->bl))
+ return PTR_ERR(pcf_bl->bl);
+
+ 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;
+}
+
+static struct platform_driver pcf50633_bl_driver = {
+ .probe = pcf50633_bl_probe,
+ .driver = {
+ .name = "pcf50633-backlight",
+ },
+};
+
+module_platform_driver(pcf50633_bl_driver);
+
+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/platform_lcd.c b/drivers/video/backlight/platform_lcd.c
index 738694d2388..c3d2e209fc8 100644
--- a/drivers/video/backlight/platform_lcd.c
+++ b/drivers/video/backlight/platform_lcd.c
@@ -16,6 +16,8 @@
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/lcd.h>
+#include <linux/of.h>
+#include <linux/slab.h>
#include <video/platform_lcd.h>
@@ -25,7 +27,7 @@ struct platform_lcd {
struct plat_lcd_data *pdata;
unsigned int power;
- unsigned int suspended : 1;
+ unsigned int suspended:1;
};
static inline struct platform_lcd *to_our_lcd(struct lcd_device *lcd)
@@ -71,59 +73,49 @@ static struct lcd_ops platform_lcd_ops = {
.check_fb = platform_lcd_match,
};
-static int __devinit platform_lcd_probe(struct platform_device *pdev)
+static int platform_lcd_probe(struct platform_device *pdev)
{
struct plat_lcd_data *pdata;
struct platform_lcd *plcd;
struct device *dev = &pdev->dev;
int err;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(dev, "no platform data supplied\n");
return -EINVAL;
}
- plcd = kzalloc(sizeof(struct platform_lcd), GFP_KERNEL);
- if (!plcd) {
- dev_err(dev, "no memory for state\n");
- return -ENOMEM;
+ if (pdata->probe) {
+ err = pdata->probe(pdata);
+ if (err)
+ return err;
}
+ plcd = devm_kzalloc(&pdev->dev, sizeof(struct platform_lcd),
+ GFP_KERNEL);
+ if (!plcd)
+ return -ENOMEM;
+
plcd->us = dev;
plcd->pdata = pdata;
- plcd->lcd = lcd_device_register(dev_name(dev), dev,
- plcd, &platform_lcd_ops);
+ plcd->lcd = devm_lcd_device_register(&pdev->dev, dev_name(dev), dev,
+ plcd, &platform_lcd_ops);
if (IS_ERR(plcd->lcd)) {
dev_err(dev, "cannot register lcd device\n");
- err = PTR_ERR(plcd->lcd);
- goto err_mem;
+ return PTR_ERR(plcd->lcd);
}
platform_set_drvdata(pdev, plcd);
platform_lcd_set_power(plcd->lcd, FB_BLANK_NORMAL);
return 0;
-
- err_mem:
- kfree(plcd);
- return err;
-}
-
-static int __devexit platform_lcd_remove(struct platform_device *pdev)
-{
- struct platform_lcd *plcd = platform_get_drvdata(pdev);
-
- lcd_device_unregister(plcd->lcd);
- kfree(plcd);
-
- return 0;
}
-#ifdef CONFIG_PM
-static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st)
+#ifdef CONFIG_PM_SLEEP
+static int platform_lcd_suspend(struct device *dev)
{
- struct platform_lcd *plcd = platform_get_drvdata(pdev);
+ struct platform_lcd *plcd = dev_get_drvdata(dev);
plcd->suspended = 1;
platform_lcd_set_power(plcd->lcd, plcd->power);
@@ -131,43 +123,39 @@ static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st)
return 0;
}
-static int platform_lcd_resume(struct platform_device *pdev)
+static int platform_lcd_resume(struct device *dev)
{
- struct platform_lcd *plcd = platform_get_drvdata(pdev);
+ struct platform_lcd *plcd = dev_get_drvdata(dev);
plcd->suspended = 0;
platform_lcd_set_power(plcd->lcd, plcd->power);
return 0;
}
-#else
-#define platform_lcd_suspend NULL
-#define platform_lcd_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(platform_lcd_pm_ops, platform_lcd_suspend,
+ platform_lcd_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id platform_lcd_of_match[] = {
+ { .compatible = "platform-lcd" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, platform_lcd_of_match);
#endif
static struct platform_driver platform_lcd_driver = {
.driver = {
.name = "platform-lcd",
.owner = THIS_MODULE,
+ .pm = &platform_lcd_pm_ops,
+ .of_match_table = of_match_ptr(platform_lcd_of_match),
},
.probe = platform_lcd_probe,
- .remove = __devexit_p(platform_lcd_remove),
- .suspend = platform_lcd_suspend,
- .resume = platform_lcd_resume,
};
-static int __init platform_lcd_init(void)
-{
- return platform_driver_register(&platform_lcd_driver);
-}
-
-static void __exit platform_lcd_cleanup(void)
-{
- platform_driver_unregister(&platform_lcd_driver);
-}
-
-module_init(platform_lcd_init);
-module_exit(platform_lcd_cleanup);
+module_platform_driver(platform_lcd_driver);
MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/progear_bl.c b/drivers/video/backlight/progear_bl.c
deleted file mode 100644
index 15fb4d58b5b..00000000000
--- a/drivers/video/backlight/progear_bl.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Backlight Driver for Frontpath ProGear HX1050+
- *
- * Copyright (c) 2006 Marcin Juszkiewicz
- *
- * Based on Progear LCD driver by M Schacht
- * <mschacht at alumni dot washington dot edu>
- *
- * Based on Sharp's Corgi Backlight Driver
- * Based on Backlight Driver for HP Jornada 680
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/fb.h>
-#include <linux/backlight.h>
-#include <linux/pci.h>
-
-#define PMU_LPCR 0xB0
-#define SB_MPS1 0x61
-#define HW_LEVEL_MAX 0x77
-#define HW_LEVEL_MIN 0x4f
-
-static struct pci_dev *pmu_dev = NULL;
-static struct pci_dev *sb_dev = NULL;
-
-static int progearbl_set_intensity(struct backlight_device *bd)
-{
- int intensity = bd->props.brightness;
-
- if (bd->props.power != FB_BLANK_UNBLANK)
- intensity = 0;
- if (bd->props.fb_blank != FB_BLANK_UNBLANK)
- intensity = 0;
-
- pci_write_config_byte(pmu_dev, PMU_LPCR, intensity + HW_LEVEL_MIN);
-
- return 0;
-}
-
-static int progearbl_get_intensity(struct backlight_device *bd)
-{
- u8 intensity;
- pci_read_config_byte(pmu_dev, PMU_LPCR, &intensity);
-
- return intensity - HW_LEVEL_MIN;
-}
-
-static struct backlight_ops progearbl_ops = {
- .get_brightness = progearbl_get_intensity,
- .update_status = progearbl_set_intensity,
-};
-
-static int progearbl_probe(struct platform_device *pdev)
-{
- u8 temp;
- struct backlight_device *progear_backlight_device;
-
- pmu_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, NULL);
- if (!pmu_dev) {
- printk("ALI M7101 PMU not found.\n");
- return -ENODEV;
- }
-
- sb_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
- if (!sb_dev) {
- printk("ALI 1533 SB not found.\n");
- pci_dev_put(pmu_dev);
- return -ENODEV;
- }
-
- /* Set SB_MPS1 to enable brightness control. */
- pci_read_config_byte(sb_dev, SB_MPS1, &temp);
- pci_write_config_byte(sb_dev, SB_MPS1, temp | 0x20);
-
- progear_backlight_device = backlight_device_register("progear-bl",
- &pdev->dev, NULL,
- &progearbl_ops);
- if (IS_ERR(progear_backlight_device))
- return PTR_ERR(progear_backlight_device);
-
- platform_set_drvdata(pdev, progear_backlight_device);
-
- progear_backlight_device->props.power = FB_BLANK_UNBLANK;
- progear_backlight_device->props.brightness = HW_LEVEL_MAX - HW_LEVEL_MIN;
- progear_backlight_device->props.max_brightness = HW_LEVEL_MAX - HW_LEVEL_MIN;
- progearbl_set_intensity(progear_backlight_device);
-
- return 0;
-}
-
-static int progearbl_remove(struct platform_device *pdev)
-{
- struct backlight_device *bd = platform_get_drvdata(pdev);
- backlight_device_unregister(bd);
-
- return 0;
-}
-
-static struct platform_driver progearbl_driver = {
- .probe = progearbl_probe,
- .remove = progearbl_remove,
- .driver = {
- .name = "progear-bl",
- },
-};
-
-static struct platform_device *progearbl_device;
-
-static int __init progearbl_init(void)
-{
- int ret = platform_driver_register(&progearbl_driver);
-
- if (!ret) {
- progearbl_device = platform_device_alloc("progear-bl", -1);
- if (!progearbl_device)
- return -ENOMEM;
-
- ret = platform_device_add(progearbl_device);
-
- if (ret) {
- platform_device_put(progearbl_device);
- platform_driver_unregister(&progearbl_driver);
- }
- }
-
- return ret;
-}
-
-static void __exit progearbl_exit(void)
-{
- pci_dev_put(pmu_dev);
- pci_dev_put(sb_dev);
-
- platform_device_unregister(progearbl_device);
- platform_driver_unregister(&progearbl_driver);
-}
-
-module_init(progearbl_init);
-module_exit(progearbl_exit);
-
-MODULE_AUTHOR("Marcin Juszkiewicz <linux@hrw.one.pl>");
-MODULE_DESCRIPTION("ProGear Backlight Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 6338d0e2fe0..38ca88bc5c3 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -3,13 +3,15 @@
*
* simple PWM based backlight control, board code has to setup
* 1) pin configuration so PWM waveforms can output
- * 2) platform_data casts to the PWM id (0/1/2/3 on PXA)
+ * 2) platform_data being correctly configured
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -19,35 +21,97 @@
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
struct pwm_bl_data {
struct pwm_device *pwm;
+ struct device *dev;
unsigned int period;
- int (*notify)(int brightness);
+ unsigned int lth_brightness;
+ unsigned int *levels;
+ bool enabled;
+ struct regulator *power_supply;
+ struct gpio_desc *enable_gpio;
+ unsigned int scale;
+ int (*notify)(struct device *,
+ int brightness);
+ void (*notify_after)(struct device *,
+ int brightness);
+ int (*check_fb)(struct device *, struct fb_info *);
+ void (*exit)(struct device *);
};
+static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
+{
+ int err;
+
+ if (pb->enabled)
+ return;
+
+ err = regulator_enable(pb->power_supply);
+ if (err < 0)
+ dev_err(pb->dev, "failed to enable power supply\n");
+
+ if (pb->enable_gpio)
+ gpiod_set_value(pb->enable_gpio, 1);
+
+ pwm_enable(pb->pwm);
+ pb->enabled = true;
+}
+
+static void pwm_backlight_power_off(struct pwm_bl_data *pb)
+{
+ if (!pb->enabled)
+ return;
+
+ pwm_config(pb->pwm, 0, pb->period);
+ pwm_disable(pb->pwm);
+
+ if (pb->enable_gpio)
+ gpiod_set_value(pb->enable_gpio, 0);
+
+ regulator_disable(pb->power_supply);
+ pb->enabled = false;
+}
+
+static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
+{
+ unsigned int lth = pb->lth_brightness;
+ int duty_cycle;
+
+ if (pb->levels)
+ duty_cycle = pb->levels[brightness];
+ else
+ duty_cycle = brightness;
+
+ return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
+}
+
static int pwm_backlight_update_status(struct backlight_device *bl)
{
- struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
+ struct pwm_bl_data *pb = bl_get_data(bl);
int brightness = bl->props.brightness;
- int max = bl->props.max_brightness;
-
- if (bl->props.power != FB_BLANK_UNBLANK)
- brightness = 0;
+ int duty_cycle;
- if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+ if (bl->props.power != FB_BLANK_UNBLANK ||
+ bl->props.fb_blank != FB_BLANK_UNBLANK ||
+ bl->props.state & BL_CORE_FBBLANK)
brightness = 0;
if (pb->notify)
- brightness = pb->notify(brightness);
-
- if (brightness == 0) {
- pwm_config(pb->pwm, 0, pb->period);
- pwm_disable(pb->pwm);
- } else {
- pwm_config(pb->pwm, brightness * pb->period / max, pb->period);
- pwm_enable(pb->pwm);
- }
+ brightness = pb->notify(pb->dev, brightness);
+
+ if (brightness > 0) {
+ duty_cycle = compute_duty_cycle(pb, brightness);
+ pwm_config(pb->pwm, duty_cycle, pb->period);
+ pwm_backlight_power_on(pb, brightness);
+ } else
+ pwm_backlight_power_off(pb);
+
+ if (pb->notify_after)
+ pb->notify_after(pb->dev, brightness);
+
return 0;
}
@@ -56,20 +120,100 @@ static int pwm_backlight_get_brightness(struct backlight_device *bl)
return bl->props.brightness;
}
-static struct backlight_ops pwm_backlight_ops = {
+static int pwm_backlight_check_fb(struct backlight_device *bl,
+ struct fb_info *info)
+{
+ struct pwm_bl_data *pb = bl_get_data(bl);
+
+ return !pb->check_fb || pb->check_fb(pb->dev, info);
+}
+
+static const struct backlight_ops pwm_backlight_ops = {
.update_status = pwm_backlight_update_status,
.get_brightness = pwm_backlight_get_brightness,
+ .check_fb = pwm_backlight_check_fb,
};
+#ifdef CONFIG_OF
+static int pwm_backlight_parse_dt(struct device *dev,
+ struct platform_pwm_backlight_data *data)
+{
+ struct device_node *node = dev->of_node;
+ struct property *prop;
+ int length;
+ u32 value;
+ int ret;
+
+ if (!node)
+ return -ENODEV;
+
+ memset(data, 0, sizeof(*data));
+
+ /* determine the number of brightness levels */
+ prop = of_find_property(node, "brightness-levels", &length);
+ if (!prop)
+ return -EINVAL;
+
+ data->max_brightness = length / sizeof(u32);
+
+ /* read brightness levels from DT property */
+ if (data->max_brightness > 0) {
+ size_t size = sizeof(*data->levels) * data->max_brightness;
+
+ data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!data->levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(node, "brightness-levels",
+ data->levels,
+ data->max_brightness);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_u32(node, "default-brightness-level",
+ &value);
+ if (ret < 0)
+ return ret;
+
+ data->dft_brightness = value;
+ data->max_brightness--;
+ }
+
+ return 0;
+}
+
+static struct of_device_id pwm_backlight_of_match[] = {
+ { .compatible = "pwm-backlight" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
+#else
+static int pwm_backlight_parse_dt(struct device *dev,
+ struct platform_pwm_backlight_data *data)
+{
+ return -ENODEV;
+}
+#endif
+
static int pwm_backlight_probe(struct platform_device *pdev)
{
- struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
+ struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
+ struct platform_pwm_backlight_data defdata;
+ struct backlight_properties props;
struct backlight_device *bl;
struct pwm_bl_data *pb;
int ret;
- if (!data)
- return -EINVAL;
+ if (!data) {
+ ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to find platform data\n");
+ return ret;
+ }
+
+ data = &defdata;
+ }
if (data->init) {
ret = data->init(&pdev->dev);
@@ -77,41 +221,116 @@ static int pwm_backlight_probe(struct platform_device *pdev)
return ret;
}
- pb = kzalloc(sizeof(*pb), GFP_KERNEL);
+ pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
if (!pb) {
ret = -ENOMEM;
goto err_alloc;
}
- pb->period = data->pwm_period_ns;
+ if (data->levels) {
+ unsigned int i;
+
+ for (i = 0; i <= data->max_brightness; i++)
+ if (data->levels[i] > pb->scale)
+ pb->scale = data->levels[i];
+
+ pb->levels = data->levels;
+ } else
+ pb->scale = data->max_brightness;
+
pb->notify = data->notify;
+ pb->notify_after = data->notify_after;
+ pb->check_fb = data->check_fb;
+ pb->exit = data->exit;
+ pb->dev = &pdev->dev;
+ pb->enabled = false;
- pb->pwm = pwm_request(data->pwm_id, "backlight");
+ pb->enable_gpio = devm_gpiod_get(&pdev->dev, "enable");
+ if (IS_ERR(pb->enable_gpio)) {
+ ret = PTR_ERR(pb->enable_gpio);
+ if (ret == -ENOENT)
+ pb->enable_gpio = NULL;
+ else
+ goto err_alloc;
+ }
+
+ /*
+ * Compatibility fallback for drivers still using the integer GPIO
+ * platform data. Must go away soon.
+ */
+ if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio,
+ GPIOF_OUT_INIT_HIGH, "enable");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
+ data->enable_gpio, ret);
+ goto err_alloc;
+ }
+
+ pb->enable_gpio = gpio_to_desc(data->enable_gpio);
+ }
+
+ if (pb->enable_gpio)
+ gpiod_direction_output(pb->enable_gpio, 1);
+
+ pb->power_supply = devm_regulator_get(&pdev->dev, "power");
+ if (IS_ERR(pb->power_supply)) {
+ ret = PTR_ERR(pb->power_supply);
+ goto err_alloc;
+ }
+
+ pb->pwm = devm_pwm_get(&pdev->dev, NULL);
if (IS_ERR(pb->pwm)) {
- dev_err(&pdev->dev, "unable to request PWM for backlight\n");
- ret = PTR_ERR(pb->pwm);
- goto err_pwm;
+ dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
+
+ pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
+ if (IS_ERR(pb->pwm)) {
+ dev_err(&pdev->dev, "unable to request legacy PWM\n");
+ ret = PTR_ERR(pb->pwm);
+ goto err_alloc;
+ }
}
- bl = backlight_device_register(pdev->name, &pdev->dev,
- pb, &pwm_backlight_ops);
+ dev_dbg(&pdev->dev, "got pwm for backlight\n");
+
+ /*
+ * The DT case will set the pwm_period_ns field to 0 and store the
+ * period, parsed from the DT, in the PWM device. For the non-DT case,
+ * set the period from platform data if it has not already been set
+ * via the PWM lookup table.
+ */
+ pb->period = pwm_get_period(pb->pwm);
+ if (!pb->period && (data->pwm_period_ns > 0)) {
+ pb->period = data->pwm_period_ns;
+ pwm_set_period(pb->pwm, data->pwm_period_ns);
+ }
+
+ pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = data->max_brightness;
+ bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
+ &pwm_backlight_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
ret = PTR_ERR(bl);
- goto err_bl;
+ goto err_alloc;
+ }
+
+ if (data->dft_brightness > data->max_brightness) {
+ dev_warn(&pdev->dev,
+ "invalid default brightness level: %u, using %u\n",
+ data->dft_brightness, data->max_brightness);
+ data->dft_brightness = data->max_brightness;
}
- bl->props.max_brightness = data->max_brightness;
bl->props.brightness = data->dft_brightness;
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
return 0;
-err_bl:
- pwm_free(pb->pwm);
-err_pwm:
- kfree(pb);
err_alloc:
if (data->exit)
data->exit(&pdev->dev);
@@ -120,66 +339,76 @@ err_alloc:
static int pwm_backlight_remove(struct platform_device *pdev)
{
- struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
+ struct pwm_bl_data *pb = bl_get_data(bl);
backlight_device_unregister(bl);
- pwm_config(pb->pwm, 0, pb->period);
- pwm_disable(pb->pwm);
- pwm_free(pb->pwm);
- kfree(pb);
- if (data->exit)
- data->exit(&pdev->dev);
+ pwm_backlight_power_off(pb);
+
+ if (pb->exit)
+ pb->exit(&pdev->dev);
+
return 0;
}
-#ifdef CONFIG_PM
-static int pwm_backlight_suspend(struct platform_device *pdev,
- pm_message_t state)
+static void pwm_backlight_shutdown(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
+ struct pwm_bl_data *pb = bl_get_data(bl);
+
+ pwm_backlight_power_off(pb);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pwm_backlight_suspend(struct device *dev)
+{
+ struct backlight_device *bl = dev_get_drvdata(dev);
+ struct pwm_bl_data *pb = bl_get_data(bl);
+
+ if (pb->notify)
+ pb->notify(pb->dev, 0);
+
+ pwm_backlight_power_off(pb);
+
+ if (pb->notify_after)
+ pb->notify_after(pb->dev, 0);
- pwm_config(pb->pwm, 0, pb->period);
- pwm_disable(pb->pwm);
return 0;
}
-static int pwm_backlight_resume(struct platform_device *pdev)
+static int pwm_backlight_resume(struct device *dev)
{
- struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct backlight_device *bl = dev_get_drvdata(dev);
backlight_update_status(bl);
+
return 0;
}
-#else
-#define pwm_backlight_suspend NULL
-#define pwm_backlight_resume NULL
#endif
+static const struct dev_pm_ops pwm_backlight_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = pwm_backlight_suspend,
+ .resume = pwm_backlight_resume,
+ .poweroff = pwm_backlight_suspend,
+ .restore = pwm_backlight_resume,
+#endif
+};
+
static struct platform_driver pwm_backlight_driver = {
.driver = {
- .name = "pwm-backlight",
- .owner = THIS_MODULE,
+ .name = "pwm-backlight",
+ .owner = THIS_MODULE,
+ .pm = &pwm_backlight_pm_ops,
+ .of_match_table = of_match_ptr(pwm_backlight_of_match),
},
.probe = pwm_backlight_probe,
.remove = pwm_backlight_remove,
- .suspend = pwm_backlight_suspend,
- .resume = pwm_backlight_resume,
+ .shutdown = pwm_backlight_shutdown,
};
-static int __init pwm_backlight_init(void)
-{
- return platform_driver_register(&pwm_backlight_driver);
-}
-module_init(pwm_backlight_init);
-
-static void __exit pwm_backlight_exit(void)
-{
- platform_driver_unregister(&pwm_backlight_driver);
-}
-module_exit(pwm_backlight_exit);
+module_platform_driver(pwm_backlight_driver);
MODULE_DESCRIPTION("PWM based Backlight Driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pwm-backlight");
diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c
new file mode 100644
index 00000000000..2d6d48196c6
--- /dev/null
+++ b/drivers/video/backlight/s6e63m0.c
@@ -0,0 +1,864 @@
+/*
+ * 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.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.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
+
+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 {
+ msleep(wbuf[i+1]);
+ }
+ 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_is_on(int power)
+{
+ return power <= FB_BLANK_NORMAL;
+}
+
+static int s6e63m0_power_on(struct s6e63m0 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd;
+ struct backlight_device *bd;
+
+ pd = lcd->lcd_pd;
+ bd = lcd->bd;
+
+ if (!pd->power_on) {
+ dev_err(lcd->dev, "power_on is NULL.\n");
+ return -EINVAL;
+ } else {
+ pd->power_on(lcd->ld, 1);
+ msleep(pd->power_on_delay);
+ }
+
+ if (!pd->reset) {
+ dev_err(lcd->dev, "reset is NULL.\n");
+ return -EINVAL;
+ } else {
+ pd->reset(lcd->ld);
+ msleep(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;
+ struct lcd_platform_data *pd;
+
+ pd = lcd->lcd_pd;
+
+ ret = s6e63m0_ldi_disable(lcd);
+ if (ret) {
+ dev_err(lcd->dev, "lcd setting failed.\n");
+ return -EIO;
+ }
+
+ msleep(pd->power_off_delay);
+
+ pd->power_on(lcd->ld, 0);
+
+ return 0;
+}
+
+static int s6e63m0_power(struct s6e63m0 *lcd, int power)
+{
+ int ret = 0;
+
+ if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
+ ret = s6e63m0_power_on(lcd);
+ else if (!s6e63m0_power_is_on(power) && s6e63m0_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 = kstrtouint(buf, 0, &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, "%u\n", lcd->gamma_table_count);
+ strcpy(buf, temp);
+
+ return strlen(buf);
+}
+static DEVICE_ATTR(gamma_table, 0444,
+ s6e63m0_sysfs_show_gamma_table, NULL);
+
+static int s6e63m0_probe(struct spi_device *spi)
+{
+ int ret = 0;
+ struct s6e63m0 *lcd = NULL;
+ struct lcd_device *ld = NULL;
+ struct backlight_device *bd = NULL;
+ struct backlight_properties props;
+
+ lcd = devm_kzalloc(&spi->dev, 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");
+ return ret;
+ }
+
+ lcd->spi = spi;
+ lcd->dev = &spi->dev;
+
+ lcd->lcd_pd = dev_get_platdata(&spi->dev);
+ if (!lcd->lcd_pd) {
+ dev_err(&spi->dev, "platform data is NULL.\n");
+ return -EINVAL;
+ }
+
+ ld = devm_lcd_device_register(&spi->dev, "s6e63m0", &spi->dev, lcd,
+ &s6e63m0_lcd_ops);
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
+
+ lcd->ld = ld;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = MAX_BRIGHTNESS;
+
+ bd = devm_backlight_device_register(&spi->dev, "s6e63m0bl-bl",
+ &spi->dev, lcd, &s6e63m0_backlight_ops,
+ &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ 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;
+ }
+
+ spi_set_drvdata(spi, lcd);
+
+ dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
+
+ return 0;
+}
+
+static int s6e63m0_remove(struct spi_device *spi)
+{
+ struct s6e63m0 *lcd = spi_get_drvdata(spi);
+
+ s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+ device_remove_file(&spi->dev, &dev_attr_gamma_table);
+ device_remove_file(&spi->dev, &dev_attr_gamma_mode);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int s6e63m0_suspend(struct device *dev)
+{
+ struct s6e63m0 *lcd = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "lcd->power = %d\n", lcd->power);
+
+ /*
+ * when lcd panel is suspend, lcd panel becomes off
+ * regardless of status.
+ */
+ return s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static int s6e63m0_resume(struct device *dev)
+{
+ struct s6e63m0 *lcd = dev_get_drvdata(dev);
+
+ lcd->power = FB_BLANK_POWERDOWN;
+
+ return s6e63m0_power(lcd, FB_BLANK_UNBLANK);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(s6e63m0_pm_ops, s6e63m0_suspend, s6e63m0_resume);
+
+/* Power down all displays on reboot, poweroff or halt. */
+static void s6e63m0_shutdown(struct spi_device *spi)
+{
+ struct s6e63m0 *lcd = spi_get_drvdata(spi);
+
+ s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver s6e63m0_driver = {
+ .driver = {
+ .name = "s6e63m0",
+ .owner = THIS_MODULE,
+ .pm = &s6e63m0_pm_ops,
+ },
+ .probe = s6e63m0_probe,
+ .remove = s6e63m0_remove,
+ .shutdown = s6e63m0_shutdown,
+};
+
+module_spi_driver(s6e63m0_driver);
+
+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/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c
new file mode 100644
index 00000000000..908016fc582
--- /dev/null
+++ b/drivers/video/backlight/tdo24m.c
@@ -0,0 +1,451 @@
+/*
+ * tdo24m - SPI-based drivers for Toppoly TDO24M series LCD panels
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tdo24m.h>
+#include <linux/fb.h>
+#include <linux/lcd.h>
+#include <linux/slab.h>
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+#define TDO24M_SPI_BUFF_SIZE (4)
+#define MODE_QVGA 0
+#define MODE_VGA 1
+
+struct tdo24m {
+ struct spi_device *spi_dev;
+ struct lcd_device *lcd_dev;
+
+ struct spi_message msg;
+ struct spi_transfer xfer;
+ uint8_t *buf;
+
+ int (*adj_mode)(struct tdo24m *lcd, int mode);
+ int color_invert;
+
+ int power;
+ int mode;
+};
+
+/* use bit 30, 31 as the indicator of command parameter number */
+#define CMD0(x) ((0 << 30) | (x))
+#define CMD1(x, x1) ((1 << 30) | ((x) << 9) | 0x100 | (x1))
+#define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\
+ ((x1) << 9) | 0x100 | (x2))
+#define CMD_NULL (-1)
+
+static const uint32_t lcd_panel_reset[] = {
+ CMD0(0x1), /* reset */
+ CMD0(0x0), /* nop */
+ CMD0(0x0), /* nop */
+ CMD0(0x0), /* nop */
+ CMD_NULL,
+};
+
+static const uint32_t lcd_panel_on[] = {
+ CMD0(0x29), /* Display ON */
+ CMD2(0xB8, 0xFF, 0xF9), /* Output Control */
+ CMD0(0x11), /* Sleep out */
+ CMD1(0xB0, 0x16), /* Wake */
+ CMD_NULL,
+};
+
+static const uint32_t lcd_panel_off[] = {
+ CMD0(0x28), /* Display OFF */
+ CMD2(0xB8, 0x80, 0x02), /* Output Control */
+ CMD0(0x10), /* Sleep in */
+ CMD1(0xB0, 0x00), /* Deep stand by in */
+ CMD_NULL,
+};
+
+static const uint32_t lcd_vga_pass_through_tdo24m[] = {
+ CMD1(0xB0, 0x16),
+ CMD1(0xBC, 0x80),
+ CMD1(0xE1, 0x00),
+ CMD1(0x36, 0x50),
+ CMD1(0x3B, 0x00),
+ CMD_NULL,
+};
+
+static const uint32_t lcd_qvga_pass_through_tdo24m[] = {
+ CMD1(0xB0, 0x16),
+ CMD1(0xBC, 0x81),
+ CMD1(0xE1, 0x00),
+ CMD1(0x36, 0x50),
+ CMD1(0x3B, 0x22),
+ CMD_NULL,
+};
+
+static const uint32_t lcd_vga_transfer_tdo24m[] = {
+ CMD1(0xcf, 0x02), /* Blanking period control (1) */
+ CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */
+ CMD1(0xd1, 0x01), /* CKV timing control on/off */
+ CMD2(0xd2, 0x14, 0x00), /* CKV 1,2 timing control */
+ CMD2(0xd3, 0x1a, 0x0f), /* OEV timing control */
+ CMD2(0xd4, 0x1f, 0xaf), /* ASW timing control (1) */
+ CMD1(0xd5, 0x14), /* ASW timing control (2) */
+ CMD0(0x21), /* Invert for normally black display */
+ CMD0(0x29), /* Display on */
+ CMD_NULL,
+};
+
+static const uint32_t lcd_qvga_transfer[] = {
+ CMD1(0xd6, 0x02), /* Blanking period control (1) */
+ CMD2(0xd7, 0x08, 0x04), /* Blanking period control (2) */
+ CMD1(0xd8, 0x01), /* CKV timing control on/off */
+ CMD2(0xd9, 0x00, 0x08), /* CKV 1,2 timing control */
+ CMD2(0xde, 0x05, 0x0a), /* OEV timing control */
+ CMD2(0xdf, 0x0a, 0x19), /* ASW timing control (1) */
+ CMD1(0xe0, 0x0a), /* ASW timing control (2) */
+ CMD0(0x21), /* Invert for normally black display */
+ CMD0(0x29), /* Display on */
+ CMD_NULL,
+};
+
+static const uint32_t lcd_vga_pass_through_tdo35s[] = {
+ CMD1(0xB0, 0x16),
+ CMD1(0xBC, 0x80),
+ CMD1(0xE1, 0x00),
+ CMD1(0x3B, 0x00),
+ CMD_NULL,
+};
+
+static const uint32_t lcd_qvga_pass_through_tdo35s[] = {
+ CMD1(0xB0, 0x16),
+ CMD1(0xBC, 0x81),
+ CMD1(0xE1, 0x00),
+ CMD1(0x3B, 0x22),
+ CMD_NULL,
+};
+
+static const uint32_t lcd_vga_transfer_tdo35s[] = {
+ CMD1(0xcf, 0x02), /* Blanking period control (1) */
+ CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */
+ CMD1(0xd1, 0x01), /* CKV timing control on/off */
+ CMD2(0xd2, 0x00, 0x1e), /* CKV 1,2 timing control */
+ CMD2(0xd3, 0x14, 0x28), /* OEV timing control */
+ CMD2(0xd4, 0x28, 0x64), /* ASW timing control (1) */
+ CMD1(0xd5, 0x28), /* ASW timing control (2) */
+ CMD0(0x21), /* Invert for normally black display */
+ CMD0(0x29), /* Display on */
+ CMD_NULL,
+};
+
+static const uint32_t lcd_panel_config[] = {
+ CMD2(0xb8, 0xff, 0xf9), /* Output control */
+ CMD0(0x11), /* sleep out */
+ CMD1(0xba, 0x01), /* Display mode (1) */
+ CMD1(0xbb, 0x00), /* Display mode (2) */
+ CMD1(0x3a, 0x60), /* Display mode 18-bit RGB */
+ CMD1(0xbf, 0x10), /* Drive system change control */
+ CMD1(0xb1, 0x56), /* Booster operation setup */
+ CMD1(0xb2, 0x33), /* Booster mode setup */
+ CMD1(0xb3, 0x11), /* Booster frequency setup */
+ CMD1(0xb4, 0x02), /* Op amp/system clock */
+ CMD1(0xb5, 0x35), /* VCS voltage */
+ CMD1(0xb6, 0x40), /* VCOM voltage */
+ CMD1(0xb7, 0x03), /* External display signal */
+ CMD1(0xbd, 0x00), /* ASW slew rate */
+ CMD1(0xbe, 0x00), /* Dummy data for QuadData operation */
+ CMD1(0xc0, 0x11), /* Sleep out FR count (A) */
+ CMD1(0xc1, 0x11), /* Sleep out FR count (B) */
+ CMD1(0xc2, 0x11), /* Sleep out FR count (C) */
+ CMD2(0xc3, 0x20, 0x40), /* Sleep out FR count (D) */
+ CMD2(0xc4, 0x60, 0xc0), /* Sleep out FR count (E) */
+ CMD2(0xc5, 0x10, 0x20), /* Sleep out FR count (F) */
+ CMD1(0xc6, 0xc0), /* Sleep out FR count (G) */
+ CMD2(0xc7, 0x33, 0x43), /* Gamma 1 fine tuning (1) */
+ CMD1(0xc8, 0x44), /* Gamma 1 fine tuning (2) */
+ CMD1(0xc9, 0x33), /* Gamma 1 inclination adjustment */
+ CMD1(0xca, 0x00), /* Gamma 1 blue offset adjustment */
+ CMD2(0xec, 0x01, 0xf0), /* Horizontal clock cycles */
+ CMD_NULL,
+};
+
+static int tdo24m_writes(struct tdo24m *lcd, const uint32_t *array)
+{
+ struct spi_transfer *x = &lcd->xfer;
+ const uint32_t *p = array;
+ uint32_t data;
+ int nparams, err = 0;
+
+ for (; *p != CMD_NULL; p++) {
+ if (!lcd->color_invert && *p == CMD0(0x21))
+ continue;
+
+ nparams = (*p >> 30) & 0x3;
+
+ data = *p << (7 - nparams);
+ switch (nparams) {
+ case 0:
+ lcd->buf[0] = (data >> 8) & 0xff;
+ lcd->buf[1] = data & 0xff;
+ break;
+ case 1:
+ lcd->buf[0] = (data >> 16) & 0xff;
+ lcd->buf[1] = (data >> 8) & 0xff;
+ lcd->buf[2] = data & 0xff;
+ break;
+ case 2:
+ lcd->buf[0] = (data >> 24) & 0xff;
+ lcd->buf[1] = (data >> 16) & 0xff;
+ lcd->buf[2] = (data >> 8) & 0xff;
+ lcd->buf[3] = data & 0xff;
+ break;
+ default:
+ continue;
+ }
+ x->len = nparams + 2;
+ err = spi_sync(lcd->spi_dev, &lcd->msg);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int tdo24m_adj_mode(struct tdo24m *lcd, int mode)
+{
+ switch (mode) {
+ case MODE_VGA:
+ tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m);
+ tdo24m_writes(lcd, lcd_panel_config);
+ tdo24m_writes(lcd, lcd_vga_transfer_tdo24m);
+ break;
+ case MODE_QVGA:
+ tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m);
+ tdo24m_writes(lcd, lcd_panel_config);
+ tdo24m_writes(lcd, lcd_qvga_transfer);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ lcd->mode = mode;
+ return 0;
+}
+
+static int tdo35s_adj_mode(struct tdo24m *lcd, int mode)
+{
+ switch (mode) {
+ case MODE_VGA:
+ tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s);
+ tdo24m_writes(lcd, lcd_panel_config);
+ tdo24m_writes(lcd, lcd_vga_transfer_tdo35s);
+ break;
+ case MODE_QVGA:
+ tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s);
+ tdo24m_writes(lcd, lcd_panel_config);
+ tdo24m_writes(lcd, lcd_qvga_transfer);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ lcd->mode = mode;
+ return 0;
+}
+
+static int tdo24m_power_on(struct tdo24m *lcd)
+{
+ int err;
+
+ err = tdo24m_writes(lcd, lcd_panel_on);
+ if (err)
+ goto out;
+
+ err = tdo24m_writes(lcd, lcd_panel_reset);
+ if (err)
+ goto out;
+
+ err = lcd->adj_mode(lcd, lcd->mode);
+out:
+ return err;
+}
+
+static int tdo24m_power_off(struct tdo24m *lcd)
+{
+ return tdo24m_writes(lcd, lcd_panel_off);
+}
+
+static int tdo24m_power(struct tdo24m *lcd, int power)
+{
+ int ret = 0;
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
+ ret = tdo24m_power_on(lcd);
+ else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
+ ret = tdo24m_power_off(lcd);
+
+ if (!ret)
+ lcd->power = power;
+
+ return ret;
+}
+
+
+static int tdo24m_set_power(struct lcd_device *ld, int power)
+{
+ struct tdo24m *lcd = lcd_get_data(ld);
+ return tdo24m_power(lcd, power);
+}
+
+static int tdo24m_get_power(struct lcd_device *ld)
+{
+ struct tdo24m *lcd = lcd_get_data(ld);
+ return lcd->power;
+}
+
+static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m)
+{
+ struct tdo24m *lcd = lcd_get_data(ld);
+ int mode = MODE_QVGA;
+
+ if (m->xres == 640 || m->xres == 480)
+ mode = MODE_VGA;
+
+ if (lcd->mode == mode)
+ return 0;
+
+ return lcd->adj_mode(lcd, mode);
+}
+
+static struct lcd_ops tdo24m_ops = {
+ .get_power = tdo24m_get_power,
+ .set_power = tdo24m_set_power,
+ .set_mode = tdo24m_set_mode,
+};
+
+static int tdo24m_probe(struct spi_device *spi)
+{
+ struct tdo24m *lcd;
+ struct spi_message *m;
+ struct spi_transfer *x;
+ struct tdo24m_platform_data *pdata;
+ enum tdo24m_model model;
+ int err;
+
+ pdata = dev_get_platdata(&spi->dev);
+ if (pdata)
+ model = pdata->model;
+ else
+ model = TDO24M;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3;
+ err = spi_setup(spi);
+ if (err)
+ return err;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ lcd->spi_dev = spi;
+ lcd->power = FB_BLANK_POWERDOWN;
+ lcd->mode = MODE_VGA; /* default to VGA */
+
+ lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
+ if (lcd->buf == NULL)
+ return -ENOMEM;
+
+ m = &lcd->msg;
+ x = &lcd->xfer;
+
+ spi_message_init(m);
+
+ x->cs_change = 1;
+ x->tx_buf = &lcd->buf[0];
+ spi_message_add_tail(x, m);
+
+ switch (model) {
+ case TDO24M:
+ lcd->color_invert = 1;
+ lcd->adj_mode = tdo24m_adj_mode;
+ break;
+ case TDO35S:
+ lcd->adj_mode = tdo35s_adj_mode;
+ lcd->color_invert = 0;
+ break;
+ default:
+ dev_err(&spi->dev, "Unsupported model");
+ return -EINVAL;
+ }
+
+ lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "tdo24m", &spi->dev,
+ lcd, &tdo24m_ops);
+ if (IS_ERR(lcd->lcd_dev))
+ return PTR_ERR(lcd->lcd_dev);
+
+ spi_set_drvdata(spi, lcd);
+ err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int tdo24m_remove(struct spi_device *spi)
+{
+ struct tdo24m *lcd = spi_get_drvdata(spi);
+
+ tdo24m_power(lcd, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tdo24m_suspend(struct device *dev)
+{
+ struct tdo24m *lcd = dev_get_drvdata(dev);
+
+ return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static int tdo24m_resume(struct device *dev)
+{
+ struct tdo24m *lcd = dev_get_drvdata(dev);
+
+ return tdo24m_power(lcd, FB_BLANK_UNBLANK);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tdo24m_pm_ops, tdo24m_suspend, tdo24m_resume);
+
+/* Power down all displays on reboot, poweroff or halt */
+static void tdo24m_shutdown(struct spi_device *spi)
+{
+ struct tdo24m *lcd = spi_get_drvdata(spi);
+
+ tdo24m_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver tdo24m_driver = {
+ .driver = {
+ .name = "tdo24m",
+ .owner = THIS_MODULE,
+ .pm = &tdo24m_pm_ops,
+ },
+ .probe = tdo24m_probe,
+ .remove = tdo24m_remove,
+ .shutdown = tdo24m_shutdown,
+};
+
+module_spi_driver(tdo24m_driver);
+
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tdo24m");
diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
new file mode 100644
index 00000000000..3ad676558c8
--- /dev/null
+++ b/drivers/video/backlight/tosa_bl.c
@@ -0,0 +1,178 @@
+/*
+ * LCD / Backlight control code for Sharp SL-6000x (tosa)
+ *
+ * Copyright (c) 2005 Dirk Opfer
+ * Copyright (c) 2007,2008 Dmitry Baryshkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/slab.h>
+
+#include <asm/mach/sharpsl_param.h>
+
+#include <mach/tosa.h>
+
+#define COMADJ_DEFAULT 97
+
+#define DAC_CH1 0
+#define DAC_CH2 1
+
+struct tosa_bl_data {
+ struct i2c_client *i2c;
+ struct backlight_device *bl;
+
+ int comadj;
+};
+
+static void tosa_bl_set_backlight(struct tosa_bl_data *data, int brightness)
+{
+ struct spi_device *spi = dev_get_platdata(&data->i2c->dev);
+
+ i2c_smbus_write_byte_data(data->i2c, DAC_CH1, data->comadj);
+
+ /* SetBacklightDuty */
+ i2c_smbus_write_byte_data(data->i2c, DAC_CH2, (u8)(brightness & 0xff));
+
+ /* SetBacklightVR */
+ gpio_set_value(TOSA_GPIO_BL_C20MA, brightness & 0x100);
+
+ tosa_bl_enable(spi, brightness);
+}
+
+static int tosa_bl_update_status(struct backlight_device *dev)
+{
+ struct backlight_properties *props = &dev->props;
+ struct tosa_bl_data *data = bl_get_data(dev);
+ int power = max(props->power, props->fb_blank);
+ int brightness = props->brightness;
+
+ if (power)
+ brightness = 0;
+
+ tosa_bl_set_backlight(data, brightness);
+
+ return 0;
+}
+
+static int tosa_bl_get_brightness(struct backlight_device *dev)
+{
+ struct backlight_properties *props = &dev->props;
+
+ return props->brightness;
+}
+
+static const struct backlight_ops bl_ops = {
+ .get_brightness = tosa_bl_get_brightness,
+ .update_status = tosa_bl_update_status,
+};
+
+static int tosa_bl_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct backlight_properties props;
+ struct tosa_bl_data *data;
+ int ret = 0;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct tosa_bl_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
+
+ ret = devm_gpio_request_one(&client->dev, TOSA_GPIO_BL_C20MA,
+ GPIOF_OUT_INIT_LOW, "backlight");
+ if (ret) {
+ dev_dbg(&data->bl->dev, "Unable to request gpio!\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->i2c = client;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 512 - 1;
+ data->bl = devm_backlight_device_register(&client->dev, "tosa-bl",
+ &client->dev, data, &bl_ops,
+ &props);
+ if (IS_ERR(data->bl)) {
+ ret = PTR_ERR(data->bl);
+ goto err_reg;
+ }
+
+ data->bl->props.brightness = 69;
+ data->bl->props.power = FB_BLANK_UNBLANK;
+
+ backlight_update_status(data->bl);
+
+ return 0;
+
+err_reg:
+ data->bl = NULL;
+ return ret;
+}
+
+static int tosa_bl_remove(struct i2c_client *client)
+{
+ struct tosa_bl_data *data = i2c_get_clientdata(client);
+
+ data->bl = NULL;
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tosa_bl_suspend(struct device *dev)
+{
+ struct tosa_bl_data *data = dev_get_drvdata(dev);
+
+ tosa_bl_set_backlight(data, 0);
+
+ return 0;
+}
+
+static int tosa_bl_resume(struct device *dev)
+{
+ struct tosa_bl_data *data = dev_get_drvdata(dev);
+
+ backlight_update_status(data->bl);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tosa_bl_pm_ops, tosa_bl_suspend, tosa_bl_resume);
+
+static const struct i2c_device_id tosa_bl_id[] = {
+ { "tosa-bl", 0 },
+ { },
+};
+
+static struct i2c_driver tosa_bl_driver = {
+ .driver = {
+ .name = "tosa-bl",
+ .owner = THIS_MODULE,
+ .pm = &tosa_bl_pm_ops,
+ },
+ .probe = tosa_bl_probe,
+ .remove = tosa_bl_remove,
+ .id_table = tosa_bl_id,
+};
+
+module_i2c_driver(tosa_bl_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("LCD/Backlight control for Sharp SL-6000 PDA");
+
diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c
new file mode 100644
index 00000000000..f08d641ccd0
--- /dev/null
+++ b/drivers/video/backlight/tosa_lcd.c
@@ -0,0 +1,278 @@
+/*
+ * LCD / Backlight control code for Sharp SL-6000x (tosa)
+ *
+ * Copyright (c) 2005 Dirk Opfer
+ * Copyright (c) 2007,2008 Dmitry Baryshkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/lcd.h>
+#include <linux/fb.h>
+
+#include <asm/mach/sharpsl_param.h>
+
+#include <mach/tosa.h>
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+#define TG_REG0_VQV 0x0001
+#define TG_REG0_COLOR 0x0002
+#define TG_REG0_UD 0x0004
+#define TG_REG0_LR 0x0008
+
+#define DAC_BASE 0x4e
+
+struct tosa_lcd_data {
+ struct spi_device *spi;
+ struct lcd_device *lcd;
+ struct i2c_client *i2c;
+
+ int lcd_power;
+ bool is_vga;
+};
+
+static int tosa_tg_send(struct spi_device *spi, int adrs, uint8_t data)
+{
+ u8 buf[1];
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 1,
+ .cs_change = 1,
+ .tx_buf = buf,
+ };
+
+ buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ return spi_sync(spi, &msg);
+}
+
+int tosa_bl_enable(struct spi_device *spi, int enable)
+{
+ /* bl_enable GP04=1 otherwise GP04=0*/
+ return tosa_tg_send(spi, TG_GPODR2, enable ? 0x01 : 0x00);
+}
+EXPORT_SYMBOL(tosa_bl_enable);
+
+static void tosa_lcd_tg_init(struct tosa_lcd_data *data)
+{
+ /* TG on */
+ gpio_set_value(TOSA_GPIO_TG_ON, 0);
+
+ mdelay(60);
+
+ /* delayed 0clk TCTL signal for VGA */
+ tosa_tg_send(data->spi, TG_TPOSCTL, 0x00);
+ /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
+ tosa_tg_send(data->spi, TG_GPOSR, 0x02);
+}
+
+static void tosa_lcd_tg_on(struct tosa_lcd_data *data)
+{
+ struct spi_device *spi = data->spi;
+ int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
+
+ if (data->is_vga)
+ value |= TG_REG0_VQV;
+
+ tosa_tg_send(spi, TG_PNLCTL, value);
+
+ /* TG LCD pannel power up */
+ tosa_tg_send(spi, TG_PINICTL, 0x4);
+ mdelay(50);
+
+ /* TG LCD GVSS */
+ tosa_tg_send(spi, TG_PINICTL, 0x0);
+
+ if (!data->i2c) {
+ /*
+ * after the pannel is powered up the first time,
+ * we can access the i2c bus so probe for the DAC
+ */
+ struct i2c_adapter *adap = i2c_get_adapter(0);
+ struct i2c_board_info info = {
+ .type = "tosa-bl",
+ .addr = DAC_BASE,
+ .platform_data = data->spi,
+ };
+ data->i2c = i2c_new_device(adap, &info);
+ }
+}
+
+static void tosa_lcd_tg_off(struct tosa_lcd_data *data)
+{
+ struct spi_device *spi = data->spi;
+
+ /* TG LCD VHSA off */
+ tosa_tg_send(spi, TG_PINICTL, 0x4);
+ mdelay(50);
+
+ /* TG LCD signal off */
+ tosa_tg_send(spi, TG_PINICTL, 0x6);
+ mdelay(50);
+
+ /* TG Off */
+ gpio_set_value(TOSA_GPIO_TG_ON, 1);
+ mdelay(100);
+}
+
+int tosa_lcd_set_power(struct lcd_device *lcd, int power)
+{
+ struct tosa_lcd_data *data = lcd_get_data(lcd);
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(data->lcd_power))
+ tosa_lcd_tg_on(data);
+
+ if (!POWER_IS_ON(power) && POWER_IS_ON(data->lcd_power))
+ tosa_lcd_tg_off(data);
+
+ data->lcd_power = power;
+ return 0;
+}
+
+static int tosa_lcd_get_power(struct lcd_device *lcd)
+{
+ struct tosa_lcd_data *data = lcd_get_data(lcd);
+
+ return data->lcd_power;
+}
+
+static int tosa_lcd_set_mode(struct lcd_device *lcd, struct fb_videomode *mode)
+{
+ struct tosa_lcd_data *data = lcd_get_data(lcd);
+
+ if (mode->xres == 320 || mode->yres == 320)
+ data->is_vga = false;
+ else
+ data->is_vga = true;
+
+ if (POWER_IS_ON(data->lcd_power))
+ tosa_lcd_tg_on(data);
+
+ return 0;
+}
+
+static struct lcd_ops tosa_lcd_ops = {
+ .set_power = tosa_lcd_set_power,
+ .get_power = tosa_lcd_get_power,
+ .set_mode = tosa_lcd_set_mode,
+};
+
+static int tosa_lcd_probe(struct spi_device *spi)
+{
+ int ret;
+ struct tosa_lcd_data *data;
+
+ data = devm_kzalloc(&spi->dev, sizeof(struct tosa_lcd_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->is_vga = true; /* default to VGA mode */
+
+ /*
+ * bits_per_word cannot be configured in platform data
+ */
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ data->spi = spi;
+ spi_set_drvdata(spi, data);
+
+ ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON,
+ GPIOF_OUT_INIT_LOW, "tg #pwr");
+ if (ret < 0)
+ return ret;
+
+ mdelay(60);
+
+ tosa_lcd_tg_init(data);
+
+ tosa_lcd_tg_on(data);
+
+ data->lcd = devm_lcd_device_register(&spi->dev, "tosa-lcd", &spi->dev,
+ data, &tosa_lcd_ops);
+
+ if (IS_ERR(data->lcd)) {
+ ret = PTR_ERR(data->lcd);
+ data->lcd = NULL;
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+ tosa_lcd_tg_off(data);
+ return ret;
+}
+
+static int tosa_lcd_remove(struct spi_device *spi)
+{
+ struct tosa_lcd_data *data = spi_get_drvdata(spi);
+
+ if (data->i2c)
+ i2c_unregister_device(data->i2c);
+
+ tosa_lcd_tg_off(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tosa_lcd_suspend(struct device *dev)
+{
+ struct tosa_lcd_data *data = dev_get_drvdata(dev);
+
+ tosa_lcd_tg_off(data);
+
+ return 0;
+}
+
+static int tosa_lcd_resume(struct device *dev)
+{
+ struct tosa_lcd_data *data = dev_get_drvdata(dev);
+
+ tosa_lcd_tg_init(data);
+ if (POWER_IS_ON(data->lcd_power))
+ tosa_lcd_tg_on(data);
+ else
+ tosa_lcd_tg_off(data);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tosa_lcd_pm_ops, tosa_lcd_suspend, tosa_lcd_resume);
+
+static struct spi_driver tosa_lcd_driver = {
+ .driver = {
+ .name = "tosa-lcd",
+ .owner = THIS_MODULE,
+ .pm = &tosa_lcd_pm_ops,
+ },
+ .probe = tosa_lcd_probe,
+ .remove = tosa_lcd_remove,
+};
+
+module_spi_driver(tosa_lcd_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("LCD/Backlight control for Sharp SL-6000 PDA");
+MODULE_ALIAS("spi:tosa-lcd");
diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c
new file mode 100644
index 00000000000..595dcf56102
--- /dev/null
+++ b/drivers/video/backlight/tps65217_bl.c
@@ -0,0 +1,341 @@
+/*
+ * tps65217_bl.c
+ *
+ * TPS65217 backlight driver
+ *
+ * Copyright (C) 2012 Matthias Kaehlcke
+ * Author: Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/mfd/tps65217.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct tps65217_bl {
+ struct tps65217 *tps;
+ struct device *dev;
+ struct backlight_device *bl;
+ bool is_enabled;
+};
+
+static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl)
+{
+ int rc;
+
+ rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISINK_ENABLE,
+ TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to enable backlight: %d\n", rc);
+ return rc;
+ }
+
+ tps65217_bl->is_enabled = true;
+
+ dev_dbg(tps65217_bl->dev, "backlight enabled\n");
+
+ return 0;
+}
+
+static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl)
+{
+ int rc;
+
+ rc = tps65217_clear_bits(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISINK_ENABLE,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to disable backlight: %d\n", rc);
+ return rc;
+ }
+
+ tps65217_bl->is_enabled = false;
+
+ dev_dbg(tps65217_bl->dev, "backlight disabled\n");
+
+ return 0;
+}
+
+static int tps65217_bl_update_status(struct backlight_device *bl)
+{
+ struct tps65217_bl *tps65217_bl = bl_get_data(bl);
+ int rc;
+ int brightness = bl->props.brightness;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ if ((bl->props.power != FB_BLANK_UNBLANK) ||
+ (bl->props.fb_blank != FB_BLANK_UNBLANK))
+ /* framebuffer in low power mode or blanking active */
+ brightness = 0;
+
+ if (brightness > 0) {
+ rc = tps65217_reg_write(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL2,
+ brightness - 1,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to set brightness level: %d\n", rc);
+ return rc;
+ }
+
+ dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness);
+
+ if (!tps65217_bl->is_enabled)
+ rc = tps65217_bl_enable(tps65217_bl);
+ } else {
+ rc = tps65217_bl_disable(tps65217_bl);
+ }
+
+ return rc;
+}
+
+static int tps65217_bl_get_brightness(struct backlight_device *bl)
+{
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops tps65217_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = tps65217_bl_update_status,
+ .get_brightness = tps65217_bl_get_brightness
+};
+
+static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
+ struct tps65217_bl_pdata *pdata)
+{
+ int rc;
+
+ rc = tps65217_bl_disable(tps65217_bl);
+ if (rc)
+ return rc;
+
+ switch (pdata->isel) {
+ case TPS65217_BL_ISET1:
+ /* select ISET_1 current level */
+ rc = tps65217_clear_bits(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISEL,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to select ISET1 current level: %d)\n",
+ rc);
+ return rc;
+ }
+
+ dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
+
+ break;
+
+ case TPS65217_BL_ISET2:
+ /* select ISET2 current level */
+ rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_ISEL,
+ TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to select ISET2 current level: %d\n",
+ rc);
+ return rc;
+ }
+
+ dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
+
+ break;
+
+ default:
+ dev_err(tps65217_bl->dev,
+ "invalid value for current level: %d\n", pdata->isel);
+ return -EINVAL;
+ }
+
+ /* set PWM frequency */
+ rc = tps65217_set_bits(tps65217_bl->tps,
+ TPS65217_REG_WLEDCTRL1,
+ TPS65217_WLEDCTRL1_FDIM_MASK,
+ pdata->fdim,
+ TPS65217_PROTECT_NONE);
+ if (rc) {
+ dev_err(tps65217_bl->dev,
+ "failed to select PWM dimming frequency: %d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct tps65217_bl_pdata *
+tps65217_bl_parse_dt(struct platform_device *pdev)
+{
+ struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *node = of_node_get(tps->dev->of_node);
+ struct tps65217_bl_pdata *pdata, *err;
+ u32 val;
+
+ node = of_find_node_by_name(node, "backlight");
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ err = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ pdata->isel = TPS65217_BL_ISET1;
+ if (!of_property_read_u32(node, "isel", &val)) {
+ if (val < TPS65217_BL_ISET1 ||
+ val > TPS65217_BL_ISET2) {
+ dev_err(&pdev->dev,
+ "invalid 'isel' value in the device tree\n");
+ err = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ pdata->isel = val;
+ }
+
+ pdata->fdim = TPS65217_BL_FDIM_200HZ;
+ if (!of_property_read_u32(node, "fdim", &val)) {
+ switch (val) {
+ case 100:
+ pdata->fdim = TPS65217_BL_FDIM_100HZ;
+ break;
+
+ case 200:
+ pdata->fdim = TPS65217_BL_FDIM_200HZ;
+ break;
+
+ case 500:
+ pdata->fdim = TPS65217_BL_FDIM_500HZ;
+ break;
+
+ case 1000:
+ pdata->fdim = TPS65217_BL_FDIM_1000HZ;
+ break;
+
+ default:
+ dev_err(&pdev->dev,
+ "invalid 'fdim' value in the device tree\n");
+ err = ERR_PTR(-EINVAL);
+ goto err;
+ }
+ }
+
+ if (!of_property_read_u32(node, "default-brightness", &val)) {
+ if (val < 0 ||
+ val > 100) {
+ dev_err(&pdev->dev,
+ "invalid 'default-brightness' value in the device tree\n");
+ err = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ pdata->dft_brightness = val;
+ }
+
+ of_node_put(node);
+
+ return pdata;
+
+err:
+ of_node_put(node);
+
+ return err;
+}
+#else
+static struct tps65217_bl_pdata *
+tps65217_bl_parse_dt(struct platform_device *pdev)
+{
+ return NULL;
+}
+#endif
+
+static int tps65217_bl_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct tps65217_bl *tps65217_bl;
+ struct tps65217_bl_pdata *pdata;
+ struct backlight_properties bl_props;
+
+ if (tps->dev->of_node) {
+ pdata = tps65217_bl_parse_dt(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ } else {
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data provided\n");
+ return -EINVAL;
+ }
+ }
+
+ tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
+ GFP_KERNEL);
+ if (tps65217_bl == NULL)
+ return -ENOMEM;
+
+ tps65217_bl->tps = tps;
+ tps65217_bl->dev = &pdev->dev;
+ tps65217_bl->is_enabled = false;
+
+ rc = tps65217_bl_hw_init(tps65217_bl, pdata);
+ if (rc)
+ return rc;
+
+ memset(&bl_props, 0, sizeof(struct backlight_properties));
+ bl_props.type = BACKLIGHT_RAW;
+ bl_props.max_brightness = 100;
+
+ tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name,
+ tps65217_bl->dev, tps65217_bl,
+ &tps65217_bl_ops, &bl_props);
+ if (IS_ERR(tps65217_bl->bl)) {
+ dev_err(tps65217_bl->dev,
+ "registration of backlight device failed: %d\n", rc);
+ return PTR_ERR(tps65217_bl->bl);
+ }
+
+ tps65217_bl->bl->props.brightness = pdata->dft_brightness;
+ backlight_update_status(tps65217_bl->bl);
+ platform_set_drvdata(pdev, tps65217_bl);
+
+ return 0;
+}
+
+static struct platform_driver tps65217_bl_driver = {
+ .probe = tps65217_bl_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tps65217-bl",
+ },
+};
+
+module_platform_driver(tps65217_bl_driver);
+
+MODULE_DESCRIPTION("TPS65217 Backlight driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");
diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c
index 593c7687d54..d538947a67d 100644
--- a/drivers/video/backlight/vgg2432a4.c
+++ b/drivers/video/backlight/vgg2432a4.c
@@ -26,7 +26,7 @@
/* Device initialisation sequences */
-static struct ili9320_reg vgg_init1[] = {
+static const struct ili9320_reg vgg_init1[] = {
{
.address = ILI9320_POWER1,
.value = ILI9320_POWER1_AP(0) | ILI9320_POWER1_BT(0),
@@ -43,7 +43,7 @@ static struct ili9320_reg vgg_init1[] = {
},
};
-static struct ili9320_reg vgg_init2[] = {
+static const struct ili9320_reg vgg_init2[] = {
{
.address = ILI9320_POWER1,
.value = (ILI9320_POWER1_AP(3) | ILI9320_POWER1_APE |
@@ -54,7 +54,7 @@ static struct ili9320_reg vgg_init2[] = {
}
};
-static struct ili9320_reg vgg_gamma[] = {
+static const struct ili9320_reg vgg_gamma[] = {
{
.address = ILI9320_GAMMA1,
.value = 0x0000,
@@ -89,7 +89,7 @@ static struct ili9320_reg vgg_gamma[] = {
};
-static struct ili9320_reg vgg_init0[] = {
+static const struct ili9320_reg vgg_init0[] = {
[0] = {
/* set direction and scan mode gate */
.address = ILI9320_DRIVER,
@@ -137,7 +137,7 @@ static int vgg2432a4_lcd_init(struct ili9320 *lcd,
ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1);
ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0);
- ili9320_write(lcd, ILI9320_RGB_IF2, ILI9320_RGBIF2_DPL);
+ ili9320_write(lcd, ILI9320_RGB_IF2, cfg->rgb_if2);
ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1));
if (ret != 0)
@@ -205,19 +205,15 @@ static int vgg2432a4_lcd_init(struct ili9320 *lcd,
return ret;
}
-#ifdef CONFIG_PM
-static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int vgg2432a4_suspend(struct device *dev)
{
- return ili9320_suspend(dev_get_drvdata(&spi->dev), state);
+ return ili9320_suspend(dev_get_drvdata(dev));
}
-
-static int vgg2432a4_resume(struct spi_device *spi)
+static int vgg2432a4_resume(struct device *dev)
{
- return ili9320_resume(dev_get_drvdata(&spi->dev));
+ return ili9320_resume(dev_get_drvdata(dev));
}
-#else
-#define vgg2432a4_suspend NULL
-#define vgg2432a4_resume NULL
#endif
static struct ili9320_client vgg2432a4_client = {
@@ -227,7 +223,7 @@ static struct ili9320_client vgg2432a4_client = {
/* Device probe */
-static int __devinit vgg2432a4_probe(struct spi_device *spi)
+static int vgg2432a4_probe(struct spi_device *spi)
{
int ret;
@@ -240,45 +236,32 @@ static int __devinit vgg2432a4_probe(struct spi_device *spi)
return 0;
}
-static int __devexit vgg2432a4_remove(struct spi_device *spi)
+static int vgg2432a4_remove(struct spi_device *spi)
{
- return ili9320_remove(dev_get_drvdata(&spi->dev));
+ return ili9320_remove(spi_get_drvdata(spi));
}
static void vgg2432a4_shutdown(struct spi_device *spi)
{
- ili9320_shutdown(dev_get_drvdata(&spi->dev));
+ ili9320_shutdown(spi_get_drvdata(spi));
}
+static SIMPLE_DEV_PM_OPS(vgg2432a4_pm_ops, vgg2432a4_suspend, vgg2432a4_resume);
+
static struct spi_driver vgg2432a4_driver = {
.driver = {
.name = "VGG2432A4",
.owner = THIS_MODULE,
+ .pm = &vgg2432a4_pm_ops,
},
.probe = vgg2432a4_probe,
- .remove = __devexit_p(vgg2432a4_remove),
+ .remove = vgg2432a4_remove,
.shutdown = vgg2432a4_shutdown,
- .suspend = vgg2432a4_suspend,
- .resume = vgg2432a4_resume,
};
-/* Device driver initialisation */
-
-static int __init vgg2432a4_init(void)
-{
- return spi_register_driver(&vgg2432a4_driver);
-}
-
-static void __exit vgg2432a4_exit(void)
-{
- spi_unregister_driver(&vgg2432a4_driver);
-}
-
-module_init(vgg2432a4_init);
-module_exit(vgg2432a4_exit);
+module_spi_driver(vgg2432a4_driver);
MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
MODULE_DESCRIPTION("VGG2432A4 LCD Driver");
MODULE_LICENSE("GPL v2");
-
-
+MODULE_ALIAS("spi:VGG2432A4");
diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c
new file mode 100644
index 00000000000..8b9455e9306
--- /dev/null
+++ b/drivers/video/backlight/wm831x_bl.c
@@ -0,0 +1,230 @@
+/*
+ * Backlight driver for Wolfson Microelectronics WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectonics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+struct wm831x_backlight_data {
+ struct wm831x *wm831x;
+ int isink_reg;
+ int current_brightness;
+};
+
+static int wm831x_backlight_set(struct backlight_device *bl, int brightness)
+{
+ struct wm831x_backlight_data *data = bl_get_data(bl);
+ struct wm831x *wm831x = data->wm831x;
+ int power_up = !data->current_brightness && brightness;
+ int power_down = data->current_brightness && !brightness;
+ int ret;
+
+ if (power_up) {
+ /* Enable the ISINK */
+ ret = wm831x_set_bits(wm831x, data->isink_reg,
+ WM831X_CS1_ENA, WM831X_CS1_ENA);
+ if (ret < 0)
+ goto err;
+
+ /* Enable the DC-DC */
+ ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE,
+ WM831X_DC4_ENA, WM831X_DC4_ENA);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (power_down) {
+ /* DCDC first */
+ ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE,
+ WM831X_DC4_ENA, 0);
+ if (ret < 0)
+ goto err;
+
+ /* ISINK */
+ ret = wm831x_set_bits(wm831x, data->isink_reg,
+ WM831X_CS1_DRIVE | WM831X_CS1_ENA, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* Set the new brightness */
+ ret = wm831x_set_bits(wm831x, data->isink_reg,
+ WM831X_CS1_ISEL_MASK, brightness);
+ if (ret < 0)
+ goto err;
+
+ if (power_up) {
+ /* Drive current through the ISINK */
+ ret = wm831x_set_bits(wm831x, data->isink_reg,
+ WM831X_CS1_DRIVE, WM831X_CS1_DRIVE);
+ if (ret < 0)
+ return ret;
+ }
+
+ data->current_brightness = brightness;
+
+ return 0;
+
+err:
+ /* If we were in the middle of a power transition always shut down
+ * for safety.
+ */
+ if (power_up || power_down) {
+ wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
+ wm831x_set_bits(wm831x, data->isink_reg, WM831X_CS1_ENA, 0);
+ }
+
+ return ret;
+}
+
+static int wm831x_backlight_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;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ return wm831x_backlight_set(bl, brightness);
+}
+
+static int wm831x_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct wm831x_backlight_data *data = bl_get_data(bl);
+ return data->current_brightness;
+}
+
+static const struct backlight_ops wm831x_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = wm831x_backlight_update_status,
+ .get_brightness = wm831x_backlight_get_brightness,
+};
+
+static int wm831x_backlight_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *wm831x_pdata = dev_get_platdata(pdev->dev.parent);
+ struct wm831x_backlight_pdata *pdata;
+ struct wm831x_backlight_data *data;
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ int ret, i, max_isel, isink_reg, dcdc_cfg;
+
+ /* We need platform data */
+ if (wm831x_pdata)
+ pdata = wm831x_pdata->backlight;
+ else
+ pdata = NULL;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ /* Figure out the maximum current we can use */
+ for (i = 0; i < WM831X_ISINK_MAX_ISEL; i++) {
+ if (wm831x_isinkv_values[i] > pdata->max_uA)
+ break;
+ }
+
+ if (i == 0) {
+ dev_err(&pdev->dev, "Invalid max_uA: %duA\n", pdata->max_uA);
+ return -EINVAL;
+ }
+ max_isel = i - 1;
+
+ if (pdata->max_uA != wm831x_isinkv_values[max_isel])
+ dev_warn(&pdev->dev,
+ "Maximum current is %duA not %duA as requested\n",
+ wm831x_isinkv_values[max_isel], pdata->max_uA);
+
+ switch (pdata->isink) {
+ case 1:
+ isink_reg = WM831X_CURRENT_SINK_1;
+ dcdc_cfg = 0;
+ break;
+ case 2:
+ isink_reg = WM831X_CURRENT_SINK_2;
+ dcdc_cfg = WM831X_DC4_FBSRC;
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid ISINK %d\n", pdata->isink);
+ return -EINVAL;
+ }
+
+ /* Configure the ISINK to use for feedback */
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret < 0)
+ return ret;
+
+ ret = wm831x_set_bits(wm831x, WM831X_DC4_CONTROL, WM831X_DC4_FBSRC,
+ dcdc_cfg);
+
+ wm831x_reg_lock(wm831x);
+ if (ret < 0)
+ return ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->wm831x = wm831x;
+ data->current_brightness = 0;
+ data->isink_reg = isink_reg;
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = max_isel;
+ bl = devm_backlight_device_register(&pdev->dev, "wm831x", &pdev->dev,
+ data, &wm831x_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ bl->props.brightness = max_isel;
+
+ platform_set_drvdata(pdev, bl);
+
+ /* Disable the DCDC if it was started so we can bootstrap */
+ wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
+
+ backlight_update_status(bl);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_backlight_driver = {
+ .driver = {
+ .name = "wm831x-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_backlight_probe,
+};
+
+module_platform_driver(wm831x_backlight_driver);
+
+MODULE_DESCRIPTION("Backlight Driver for WM831x PMICs");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-backlight");