TPS6116x is an EasyScale backlight controller device. This driver supports TPS6116x devices connected on a single GPIO.
Signed-off-by: Cyril Chemparathy <cy...@ti.com> --- drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/tps6116x.c | 340 ++++++++++++++++++++++++++++++++++++ 3 files changed, 348 insertions(+), 1 deletions(-) create mode 100644 drivers/video/backlight/tps6116x.c diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index e54a337..06e868e 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -307,6 +307,13 @@ config BACKLIGHT_PCF50633 If you have a backlight driven by a NXP PCF50633 MFD, say Y here to enable its driver. +config BACKLIGHT_TPS6116X + tristate "TPS6116X LCD Backlight" + depends on GENERIC_GPIO + help + This driver controls the LCD backlight level for EasyScale capable + SSP connected backlight controllers. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 44c0f81..5d407c8 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -35,4 +35,4 @@ obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o - +obj-$(CONFIG_BACKLIGHT_TPS6116X)+= tps6116x.o diff --git a/drivers/video/backlight/tps6116x.c b/drivers/video/backlight/tps6116x.c new file mode 100644 index 0000000..7a9f8ca --- /dev/null +++ b/drivers/video/backlight/tps6116x.c @@ -0,0 +1,340 @@ +/* + * TPS6116X LCD Backlight Controller Driver + * + * Copyright (C) 2010 Texas Instruments + * + * 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/fb.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/backlight.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#define TPS6116X_MAX_INTENSITY 31 +#define TPS6116X_DEFAULT_INTENSITY 10 + +/* Easyscale timing w/ margin (usecs) */ +#define T_POWER_SETTLE 2000 +#define T_ES_DELAY 120 +#define T_ES_DETECT 280 +#define T_ES_WINDOW (1000 - T_ES_DELAY - T_ES_DETECT) +#define T_START 3 +#define T_EOS 3 +#define T_INACTIVE 3 +#define T_ACTIVE (3 * T_INACTIVE) + +#define CMD_SET 0x72 + +struct tps6116x { + struct ti_ssp_device *handle; + struct device *dev; + int gpio, gpio_initialized; + struct mutex lock; + int intensity; + int power; + struct backlight_properties props; + struct backlight_device *bl; + int suspended:1; + struct regulator *regulator; +}; + +static int __set_power(struct tps6116x *hw, int power) +{ + unsigned long flags; + int error; + + power = !!power; + if (power == hw->power) + return 0; /* nothing to do */ + + /* disabling is simple... choke power */ + if (!power) { + error = regulator_disable(hw->regulator); + goto done; + } + + /* set ctrl pin init state for easyscale detection */ + gpio_set_value(hw->gpio, 0); + + error = regulator_enable(hw->regulator); + if (error < 0) + goto done; + + udelay(T_POWER_SETTLE); + + /* + * Now that the controller is powered up, we need to put it into 1-wire + * mode. This is a timing sensitive operation, hence the irq disable. + * Ideally, this should happen rarely, and mostly at init, so disabling + * interrupts for the duration should not be a problem. + */ + local_irq_save(flags); + + gpio_set_value(hw->gpio, 1); + udelay(T_ES_DELAY); + gpio_set_value(hw->gpio, 0); + udelay(T_ES_DETECT); + gpio_set_value(hw->gpio, 1); + + local_irq_restore(flags); + +done: + if (error >= 0) + hw->power = power; + + return error; +} + +static void __write_byte(struct tps6116x *hw, u8 data) +{ + int bit; + + gpio_set_value(hw->gpio, 1); + udelay(T_START); + + for (bit = 0; bit < 8; bit++, data <<= 1) { + int val = data & 0x80; + int t_lo = val ? T_INACTIVE : T_ACTIVE; + int t_hi = val ? T_ACTIVE : T_INACTIVE; + + gpio_set_value(hw->gpio, 0); + udelay(t_lo); + gpio_set_value(hw->gpio, 1); + udelay(t_hi); + } + + gpio_set_value(hw->gpio, 0); + udelay(T_EOS); + gpio_set_value(hw->gpio, 1); +} + +static void __set_intensity(struct tps6116x *hw, int intensity) +{ + unsigned long flags; + + intensity = clamp(intensity, 0, TPS6116X_MAX_INTENSITY); + + local_irq_save(flags); + __write_byte(hw, CMD_SET); + __write_byte(hw, intensity); + local_irq_restore(flags); +} + +static int set_intensity(struct tps6116x *hw, int intensity) +{ + int error = 0; + + if (intensity == hw->intensity) + return 0; + + mutex_lock(&hw->lock); + + if (!hw->gpio_initialized) { + error = gpio_request_one(hw->gpio, GPIOF_DIR_OUT, + dev_name(hw->dev)); + if (error < 0) + goto error; + hw->gpio_initialized = 1; + } + + error = __set_power(hw, intensity ? 1 : 0); + if (error < 0) + goto error; + + if (intensity > 0) + __set_intensity(hw, intensity); + + hw->intensity = intensity; +error: + mutex_unlock(&hw->lock); + + return error; +} + +static ssize_t intensity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tps6116x *hw = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", hw->intensity); + + return len; +} + +static ssize_t intensity_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct tps6116x *hw = dev_get_drvdata(dev); + unsigned long intensity; + int error; + + error = strict_strtoul(buf, 10, &intensity); + if (error < 0) + return error; + + error = set_intensity(hw, intensity); + if (error < 0) + return error; + + return count; +} + +DEVICE_ATTR(intensity, S_IWUSR | S_IRUGO, intensity_show, intensity_store); + +static int get_brightness(struct backlight_device *bl) +{ + struct tps6116x *hw = bl_get_data(bl); + + return hw->intensity; +} + +static int update_status(struct backlight_device *bl) +{ + struct tps6116x *hw = bl_get_data(bl); + int intensity = bl->props.brightness; + + if (hw->suspended) + intensity = 0; + if (bl->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + return set_intensity(hw, intensity); +} + +static const struct backlight_ops tps6116x_backlight_ops = { + .get_brightness = get_brightness, + .update_status = update_status, +}; + +static int __devinit tps6116x_probe(struct platform_device *pdev) +{ + struct tps6116x *hw; + struct device *dev = &pdev->dev; + struct backlight_properties props; + int error; + + hw = kzalloc(sizeof(struct tps6116x), GFP_KERNEL); + if (!hw) { + error = -ENOMEM; + dev_err(dev, "cannot allocate driver data\n"); + goto fail0; + } + platform_set_drvdata(pdev, hw); + + memset(hw, 0, sizeof(struct tps6116x)); + hw->gpio = (int)dev->platform_data; + hw->dev = dev; + + mutex_init(&hw->lock); + + error = device_create_file(dev, &dev_attr_intensity); + if (error < 0) { + dev_err(dev, "cannot create device attributes\n"); + goto fail1; + } + + hw->regulator = regulator_get(dev, "vlcd"); + if (IS_ERR(hw->regulator)) { + error = PTR_ERR(hw->regulator); + dev_err(dev, "cannot claim regulator\n"); + goto fail2; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = TPS6116X_MAX_INTENSITY; + props.brightness = TPS6116X_DEFAULT_INTENSITY; + + hw->bl = backlight_device_register("tps6116x", hw->dev, hw, + &tps6116x_backlight_ops, &props); + if (IS_ERR(hw->bl)) { + error = PTR_ERR(hw->bl); + dev_err(dev, "backlight registration failed\n"); + goto fail3; + } + + dev_info(dev, "registered backlight controller\n"); + return 0; + +fail3: + regulator_put(hw->regulator); +fail2: + device_remove_file(dev, &dev_attr_intensity); +fail1: + kfree(hw); + platform_set_drvdata(pdev, NULL); +fail0: + return error; +} + +static int __devexit tps6116x_remove(struct platform_device *pdev) +{ + struct tps6116x *hw = platform_get_drvdata(pdev); + + backlight_device_unregister(hw->bl); + regulator_disable(hw->regulator); + regulator_put(hw->regulator); + device_remove_file(hw->dev, &dev_attr_intensity); + kfree(hw); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static int tps6116x_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tps6116x *hw = platform_get_drvdata(pdev); + hw->suspended = 1; + update_status(hw->bl); + return 0; +} + +static int tps6116x_resume(struct platform_device *pdev) +{ + struct tps6116x *hw = platform_get_drvdata(pdev); + hw->suspended = 0; + update_status(hw->bl); + return 0; +} + +static struct platform_driver tps6116x_driver = { + .probe = tps6116x_probe, + .remove = __devexit_p(tps6116x_remove), + .suspend = tps6116x_suspend, + .resume = tps6116x_resume, + .driver = { + .name = "tps6116x", + .owner = THIS_MODULE, + }, +}; + +static int __init tps6116x_init(void) +{ + return platform_driver_register(&tps6116x_driver); +} +module_init(tps6116x_init); + +static void __exit tps6116x_exit(void) +{ + platform_driver_unregister(&tps6116x_driver); +} +module_exit(tps6116x_exit); + +MODULE_DESCRIPTION("SSP TPS6116X Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tps6116x"); -- 1.7.0.4 _______________________________________________ Davinci-linux-open-source mailing list Davinci-linux-open-source@linux.davincidsp.com http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source