diff mbox

[RFC,1/2] backlight: add new tps611xx backlight driver

Message ID 1402885043-3626-2-git-send-email-gshark.jeong@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Jeong June 16, 2014, 2:17 a.m. UTC
This driver a general version for tps611xx backlgiht chips of TI.
It supports tps61158, tps61161, tps61163 and tps61165 backlight driver
based on EasyScale protocol.

Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com>
---
 drivers/video/backlight/Kconfig           |    7 +
 drivers/video/backlight/Makefile          |    1 +
 drivers/video/backlight/tps611xx_bl.c     |  486 +++++++++++++++++++++++++++++
 include/linux/platform_data/tps611xx_bl.h |   30 ++
 4 files changed, 524 insertions(+)
 create mode 100644 drivers/video/backlight/tps611xx_bl.c
 create mode 100644 include/linux/platform_data/tps611xx_bl.h

Comments

Jingoo Han June 16, 2014, 12:19 p.m. UTC | #1
On Monday, June 16, 2014 11:17 AM, Daniel Jeong wrote:
> 
> This driver a general version for tps611xx backlgiht chips of TI.
> It supports tps61158, tps61161, tps61163 and tps61165 backlight driver
> based on EasyScale protocol.

"EasyScale" protocol is a TI-specific protocol. How about adding more
detailed description as below? I referred to the datasheet of tps61158.

based on EasyScale protocol (1-Wire Control Interface). The protocol
consists of a device specific address byte and a data byte. The device
specific address byte is fixed to 58 hex. The data byte consists of
five bits for information, two address bits ("00"), and the RFA bit.

> 
> Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com>
> ---
>  drivers/video/backlight/Kconfig           |    7 +
>  drivers/video/backlight/Makefile          |    1 +
>  drivers/video/backlight/tps611xx_bl.c     |  486 +++++++++++++++++++++++++++++
>  include/linux/platform_data/tps611xx_bl.h |   30 ++
>  4 files changed, 524 insertions(+)
>  create mode 100644 drivers/video/backlight/tps611xx_bl.c
>  create mode 100644 include/linux/platform_data/tps611xx_bl.h
> 
> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> index 5a3eb2e..c779a85 100644
> --- a/drivers/video/backlight/Kconfig
> +++ b/drivers/video/backlight/Kconfig
> @@ -418,6 +418,13 @@ config BACKLIGHT_TPS65217
>  	  If you have a Texas Instruments TPS65217 say Y to enable the
>  	  backlight driver.
> 
> +config BACKLIGHT_TPS611xx
> +	tristate "TPS611xx Backlight"
> +	depends on BACKLIGHT_CLASS_DEVICE && GPIOLIB
> +	help
> +	  This supports TI TPS61158, TPS61161, TPS61163 and TPS61165
> +	  backlight driver based on EasyScale Protocol.
> +
>  config BACKLIGHT_AS3711
>  	tristate "AS3711 Backlight"
>  	depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711
> diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
> index bb82002..44f1641 100644
> --- a/drivers/video/backlight/Makefile
> +++ b/drivers/video/backlight/Makefile
> @@ -52,4 +52,5 @@ 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_TPS611xx)	+= tps611xx_bl.o
>  obj-$(CONFIG_BACKLIGHT_WM831X)		+= wm831x_bl.o
> diff --git a/drivers/video/backlight/tps611xx_bl.c b/drivers/video/backlight/tps611xx_bl.c
> new file mode 100644
> index 0000000..4b76cdd
> --- /dev/null
> +++ b/drivers/video/backlight/tps611xx_bl.c
> @@ -0,0 +1,486 @@
> +/*
> + * Simple driver for Texas Instruments TPS611XX Backlight driver chip
> + *        using EasyScale Interface. It supports TPS61158, TPS61161,
> + *        TPS61163 and TPS61165.
> + *
> + * Copyright (C) 2014 Texas Instruments
> + * Author: Daniel Jeong  <gshark.jeong@gmail.com>
> + *	       Ldd Mlp <ldd-mlp@list.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/delay.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_device.h>
> +#include <linux/platform_data/tps611xx_bl.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#define CMD_FORWARD 0
> +#define CMD_BACKWARD 1
> +
> +enum tps611xx_id {
> +	TPS61158_ID = 0,
> +	TPS61161_ID,
> +	TPS61163_ID,
> +	TPS61165_ID,
> +};
> +
> +/*
> + * easyscale time spec
> + * @es_delay : es delay time(ns)
> + * @es_det   : es detection time(ns)
> + * @start    : start time of data stream(ns)
> + * @eos      : end time of data stream(ns)
> + * @reset    : ic shutdown time(ms)
> + * @logic_1_low : low time high bit(ns)
> + * @logic_0_low : low time low bit(ns)
> + * @ackn        : duation of ack condistion(ns)
> + * @ack_poll    : ack polling duration(ns)
> + */
> +struct tps611xx_time {
> +	unsigned int es_delay;
> +	unsigned int es_det;
> +	unsigned int start;
> +	unsigned int eos;
> +	unsigned int reset;
> +	unsigned int logic_1_low;
> +	unsigned int logic_0_low;
> +	unsigned int ackn;
> +	unsigned int ack_poll;
> +};
> +
> +/*
> + * @seq : sequence of data transfer
> + * @size: size of data
> + * @brt_max : max brightness
> + * @brt_bmask : bit mask of dimming bits
> + * @rfa_bmask : bit mask of request of ack

s/request of ack/request for ack ?

> + */
> +struct tps611xx_command {
> +	int seq;
> +	int size;
> +	int brt_max;
> +	int brt_bmask;
> +	int rfa_bmask;
> +};
> +
> +/*
> + * @id : product id
> + * @name : product name
> + * @addr : device address
> + * @cmd  : es command info
> + * @time : es time info
> + */
> +struct tps611xx_esdata {
> +	enum tps611xx_id id;
> +	char *name;
> +	int addr;
> +	struct tps611xx_command cmd;
> +	struct tps611xx_time time;
> +};
> +
> +struct tps611xx_bl_data {
> +	struct device *dev;
> +	struct backlight_device *bled;
> +	struct tps611xx_platform_data *pdata;
> +
> +	/*
> +	 * @rfa_en : acknowlege request enable

s/acknowledge/acknowledge

How about the following?

+	 * @rfa_en : request for acknowledge enable

> +	 * @en_gpio: enable pin gpio no.
> +	 * @esdata : easyscale data
> +	 */
> +	int rfa_en;
> +	unsigned int en_gpio;
> +	const struct tps611xx_esdata *esdata;
> +};
> +
> +static struct tps611xx_esdata tps611xx_info[] = {
> +	[TPS61158_ID] = {
> +			 .id = TPS61158_ID,
> +			 .name = "tps61158",
> +			 .addr = 0x5800,
> +			 .cmd = {
> +				 .seq = CMD_FORWARD,
> +				 .size = 16,
> +				 .brt_max = 31,
> +				 .brt_bmask = 0x1f,
> +				 .rfa_bmask = 0x80},

Would you keep the coding style? The following looks better.

+				 .rfa_bmask = 0x80
+			},


> +			.time = {
> +				  .es_delay = 100000,
> +				  .es_det = 450000,
> +				  .start = 3500,
> +				  .eos = 3500,
> +				  .reset = 4,
> +				  .logic_1_low = 5000,
> +				  .logic_0_low = 15000,
> +				  .ackn = 900000,
> +				  .ack_poll = 2000},
> +			 },
> +
> +	[TPS61161_ID] = {
> +			 .id = TPS61161_ID,
> +			 .name = "tps61161",
> +			 .addr = 0x7200,
> +			 .cmd = {
> +				 .seq = CMD_FORWARD,
> +				 .size = 16,
> +				 .brt_max = 31,
> +				 .brt_bmask = 0x1f,
> +				 .rfa_bmask = 0x80},
> +			 .time = {
> +				  .es_delay = 120000,
> +				  .es_det = 280000,
> +				  .start = 2000,
> +				  .eos = 2000,
> +				  .reset = 3,
> +				  .logic_1_low = 3000,
> +				  .logic_0_low = 7000,
> +				  .ackn = 512000,
> +				  .ack_poll = 2000},
> +			 },
> +
> +	[TPS61163_ID] = {
> +			 .id = TPS61163_ID,
> +			 .name = "tps61163",
> +			 .addr = 0x8F0000,
> +			 .cmd = {
> +				 .seq = CMD_BACKWARD,
> +				 .size = 24,
> +				 .brt_max = 511,
> +				 .brt_bmask = 0x1ff,
> +				 .rfa_bmask = 0x400},
> +			 .time = {
> +				  .es_delay = 100000,
> +				  .es_det = 260000,
> +				  .start = 2000,
> +				  .eos = 2000,
> +				  .reset = 3,
> +				  .logic_1_low = 3000,
> +				  .logic_0_low = 7000,
> +				  .ackn = 512000,
> +				  .ack_poll = 2000},
> +			 },
> +
> +	[TPS61165_ID] = {
> +			 .id = TPS61165_ID,
> +			 .name = "tps61165",
> +			 .addr = 0x7200,
> +			 .cmd = {
> +				 .seq = CMD_FORWARD,
> +				 .size = 16,
> +				 .brt_max = 31,
> +				 .brt_bmask = 0x1f,
> +				 .rfa_bmask = 0x80},
> +			 .time = {
> +				  .es_delay = 120000,
> +				  .es_det = 280000,
> +				  .start = 4000,
> +				  .eos = 4000,
> +				  .reset = 3,
> +				  .logic_1_low = 3000,
> +				  .logic_0_low = 7000,
> +				  .ackn = 512000,
> +				  .ack_poll = 2000},
> +			 },
> +};
> +
> +static int tps611xx_bl_update_status(struct backlight_device *bl)
> +{
> +	struct tps611xx_bl_data *pchip = bl_get_data(bl);
> +	const struct tps611xx_esdata *esdata = pchip->esdata;
> +	int data_in, t_low, t_logic, max_bmask;
> +	unsigned long flags;
> +
> +	data_in = esdata->addr | (bl->props.brightness & esdata->cmd.brt_bmask);
> +	if (pchip->rfa_en)
> +		data_in |= esdata->cmd.rfa_bmask;
> +
> +	max_bmask = 0x1 << esdata->cmd.size;
> +	t_logic = esdata->time.logic_1_low + esdata->time.logic_0_low;
> +
> +	local_irq_save(flags);
> +	/* t_start : 2us high before data byte */
> +	gpio_direction_output(pchip->en_gpio, 1);
> +	ndelay(esdata->time.start);
> +
> +	/* forward command transfer */
> +	if (esdata->cmd.seq == CMD_FORWARD) {
> +		int addr_bmask = max_bmask >> 8;
> +
> +		for (max_bmask >>= 1; max_bmask > 0x0; max_bmask >>= 1) {
> +			if (data_in & max_bmask)
> +				t_low = esdata->time.logic_1_low;
> +			else
> +				t_low = esdata->time.logic_0_low;
> +
> +			gpio_direction_output(pchip->en_gpio, 0);
> +			ndelay(t_low);
> +			gpio_direction_output(pchip->en_gpio, 1);
> +			ndelay(t_logic - t_low);
> +
> +			if (max_bmask == addr_bmask) {
> +				gpio_direction_output(pchip->en_gpio, 0);
> +				/* t_eos : low after address byte */
> +				ndelay(esdata->time.eos);
> +				gpio_direction_output(pchip->en_gpio, 1);
> +				/* t_start : high before data byte */
> +				ndelay(esdata->time.start);
> +			}
> +		}
> +	} else {
> +		/* backward command tansfer */
> +		int bmask;
> +
> +		for (bmask = 0x01; bmask < max_bmask; bmask <<= 1) {
> +			if (data_in & bmask)
> +				t_low = esdata->time.logic_1_low;
> +			else
> +				t_low = esdata->time.logic_0_low;
> +
> +			gpio_direction_output(pchip->en_gpio, 0);
> +			ndelay(t_low);
> +			gpio_direction_output(pchip->en_gpio, 1);
> +			ndelay(t_logic - t_low);
> +		}
> +	}
> +
> +	/*
> +	 * t_eos : low after address byte
> +	 * t_ackVal is also t_eos
> +	 */
> +	gpio_direction_output(pchip->en_gpio, 0);
> +	ndelay(esdata->time.eos);
> +
> +	/* RFA management  */
> +	if (pchip->rfa_en) {
> +		int max_ack_time = esdata->time.ackn;
> +		/* set input */
> +		gpio_direction_input(pchip->en_gpio);
> +		/* read acknowledge from chip */
> +		while (max_ack_time > 0) {
> +			if (gpio_get_value(pchip->en_gpio) == 0)
> +				break;
> +			max_ack_time -= esdata->time.ack_poll;
> +		}
> +		if (max_ack_time <= 0)
> +			dev_err(pchip->dev,
> +				"easyscale : no ack from %s\n", esdata->name);
> +		else
> +			ndelay(max_ack_time);
> +	}
> +	gpio_direction_output(pchip->en_gpio, 1);
> +	local_irq_restore(flags);
> +
> +	return bl->props.brightness;
> +}
> +
> +static int tps611xx_bl_get_brightness(struct backlight_device *bl)
> +{
> +	return bl->props.brightness;
> +}
> +
> +static const struct backlight_ops tps611xx_bl_ops = {
> +	.update_status = tps611xx_bl_update_status,
> +	.get_brightness = tps611xx_bl_get_brightness,
> +};
> +
> +static ssize_t tps611xx_enable_store(struct device *dev,
> +				     struct device_attribute *devAttr,
> +				     const char *buf, size_t size)
> +{
> +	struct tps611xx_bl_data *pchip = dev_get_drvdata(dev);
> +	const struct tps611xx_esdata *esdata = pchip->esdata;
> +	unsigned long flags;
> +	unsigned int input;
> +	int ret;
> +
> +	ret = kstrtouint(buf, 10, &input);
> +	if (ret)
> +		return -EINVAL;
> +
> +	local_irq_save(flags);
> +	if (input == 0) {
> +		/* chip disable */
> +		gpio_direction_output(pchip->en_gpio, 0);
> +		/* low more than reset ms to reset */
> +		mdelay(esdata->time.reset);
> +	} else {
> +		/* easyscale detection window */
> +		gpio_direction_output(pchip->en_gpio, 1);
> +		ndelay(esdata->time.es_delay);
> +		gpio_direction_output(pchip->en_gpio, 0);
> +		ndelay(esdata->time.es_det);
> +		gpio_direction_output(pchip->en_gpio, 1);
> +	}
> +	local_irq_restore(flags);
> +
> +	return size;
> +}
> +
> +static DEVICE_ATTR(enable, S_IWUSR, NULL, tps611xx_enable_store);
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id tps611xx_backlight_of_match[] = {

Please add 'const' as below.

+static const struct of_device_id tps611xx_backlight_of_match[] = {

> +	{.compatible = "ti,tps61158_bl", .data = &tps611xx_info[TPS61158_ID]},
> +	{.compatible = "ti,tps61161_bl", .data = &tps611xx_info[TPS61161_ID]},
> +	{.compatible = "ti,tps61163_bl", .data = &tps611xx_info[TPS61163_ID]},
> +	{.compatible = "ti,tps61165_bl", .data = &tps611xx_info[TPS61165_ID]},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(of, tps611xx_backlight_of_match);
> +
> +static int tps611xx_backlight_parse_dt(struct tps611xx_bl_data *pchip)
> +{
> +	struct device *dev = pchip->dev;
> +	struct device_node *node = dev->of_node;
> +	const struct of_device_id *of_id =
> +	    of_match_device(tps611xx_backlight_of_match, dev);
> +	u32 value;
> +	int ret;
> +
> +	if (!node)
> +		return -ENODEV;
> +
> +	if (!of_id || !of_id->data) {
> +		dev_err(dev, "Failed to find tps611xx chip id\n");
> +		return -EFAULT;
> +	}
> +	pchip->esdata = of_id->data;
> +
> +	ret = of_property_read_u32(node, "en_gpio_num", &value);
> +	if (ret < 0)
> +		return ret;
> +	pchip->en_gpio = value;
> +	ret = of_property_read_u32(node, "rfa_en", &value);
> +	if (ret < 0)
> +		return ret;
> +	pchip->rfa_en = value;
> +
> +	return 0;
> +}
> +#else
> +static int tps611xx_backlight_parse_dt(struct tps611xx_bl_data *pchip)
> +{
> +	return -ENODEV;
> +}
> +#endif
> +
> +static int tps611xx_backlight_probe(struct platform_device *pdev)
> +{
> +	struct tps611xx_bl_data *pchip;
> +	struct backlight_properties props;
> +	const struct tps611xx_esdata *esdata;
> +	struct tps611xx_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	unsigned long flags;
> +	int ret;
> +
> +	pchip = devm_kzalloc(&pdev->dev,
> +			     sizeof(struct tps611xx_bl_data), GFP_KERNEL);
> +	if (pchip == NULL)
> +		return -ENOMEM;
> +	pchip->dev = &pdev->dev;
> +
> +	if (pdata == NULL) {
> +		ret = tps611xx_backlight_parse_dt(pchip);
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		pchip->rfa_en = pdata->rfa_en;
> +		pchip->en_gpio = pdata->en_gpio_num;
> +		pchip->esdata = (const struct tps611xx_esdata *)
> +		    platform_get_device_id(pdev)->driver_data;
> +	}
> +	esdata = pchip->esdata;
> +
> +	memset(&props, 0, sizeof(struct backlight_properties));
> +	props.brightness = esdata->cmd.brt_max;
> +	props.max_brightness = esdata->cmd.brt_max;
> +	props.type = BACKLIGHT_RAW;
> +	pchip->bled =
> +	    devm_backlight_device_register(pchip->dev, TPS611XX_NAME,
> +					   pchip->dev, pchip,
> +					   &tps611xx_bl_ops, &props);
> +	if (IS_ERR(pchip->bled))
> +		return PTR_ERR(pchip->bled);
> +
> +	/* for enable/disable */
> +	ret = device_create_file(&(pchip->bled->dev), &dev_attr_enable);
> +	if (ret < 0) {
> +		dev_err(pchip->dev, "failed : add sysfs entries\n");
> +		return ret;
> +	}
> +	platform_set_drvdata(pdev, pchip);
> +
> +	/* EasyScale init */
> +	ret = gpio_request_one(pchip->en_gpio, GPIOF_OUT_INIT_HIGH, "tps611xx");
> +	if (ret) {
> +		device_remove_file(&(pchip->bled->dev), &dev_attr_enable);
> +		dev_err(pchip->dev, "failed : get gpio %d\n", pchip->en_gpio);
> +		return ret;
> +	}
> +
> +	/*
> +	 * ES Detection Window
> +	 *   - ES detect delay
> +	 *   - ES detect time
> +	 */
> +	local_irq_save(flags);
> +	gpio_direction_output(pchip->en_gpio, 1);
> +	ndelay(esdata->time.es_delay);
> +	gpio_direction_output(pchip->en_gpio, 0);
> +	ndelay(esdata->time.es_det);
> +	gpio_direction_output(pchip->en_gpio, 1);
> +	local_irq_restore(flags);
> +	dev_info(pchip->dev,
> +		 "%s EasyScale is initialized\n", pchip->esdata->name);
> +	return 0;
> +}
> +
> +static int tps611xx_backlight_remove(struct platform_device *pdev)
> +{
> +	struct tps611xx_bl_data *pchip = platform_get_drvdata(pdev);
> +	const struct tps611xx_esdata *esdata = pchip->esdata;
> +
> +	device_remove_file(&(pchip->bled->dev), &dev_attr_enable);
> +	gpio_direction_output(pchip->en_gpio, 0);
> +	mdelay(esdata->time.reset);
> +	return 0;
> +}
> +
> +static const struct platform_device_id tps611xx_id_table[] = {
> +	{TPS61158_NAME, (unsigned long)&tps611xx_info[TPS61158_ID]},
> +	{TPS61161_NAME, (unsigned long)&tps611xx_info[TPS61161_ID]},
> +	{TPS61163_NAME, (unsigned long)&tps611xx_info[TPS61163_ID]},
> +	{TPS61165_NAME, (unsigned long)&tps611xx_info[TPS61165_ID]},
> +	{}
> +};
> +
> +static struct platform_driver tps611xx_backlight_driver = {
> +	.driver = {
> +		   .name = TPS611XX_NAME,
> +		   .owner = THIS_MODULE,
> +		   .of_match_table = of_match_ptr(tps611xx_backlight_of_match),
> +		   },
> +	.probe = tps611xx_backlight_probe,
> +	.remove = tps611xx_backlight_remove,
> +	.id_table = tps611xx_id_table,
> +};
> +
> +module_platform_driver(tps611xx_backlight_driver);
> +
> +MODULE_DESCRIPTION("EasyScale based tps611xx Backlight Driver");
> +MODULE_LICENSE("GPL");

How about adding 'GPL v2' instead of 'GPL'?

+ MODULE_LICENSE("GPL v2");

> +MODULE_ALIAS("platform:tps611xx_bl");
> diff --git a/include/linux/platform_data/tps611xx_bl.h b/include/linux/platform_data/tps611xx_bl.h
> new file mode 100644
> index 0000000..b7c4504
> --- /dev/null
> +++ b/include/linux/platform_data/tps611xx_bl.h
> @@ -0,0 +1,30 @@
> +/*
> + * Simple driver for Texas Instruments TPS61163a Backlight driver chip
> + * Copyright (C) 2014 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.
> + *
> + */
> +
> +#ifndef __TPS611XX_H
> +#define __TPS611XX_H
> +
> +#define TPS611XX_NAME "tps611xx_bl"
> +#define TPS61158_NAME "tps61158_bl"
> +#define TPS61161_NAME "tps61161_bl"
> +#define TPS61163_NAME "tps61163_bl"
> +#define TPS61165_NAME "tps61165_bl"
> +
> +/* struct tps61163a platform data

Please use the proper coding style as below.

+/*
+ * struct tps61163a platform data

> + * @rfa_en : request for acknowledge
> + * @en_gpio_num : gpio number for en_pin
> + */
> +struct tps611xx_platform_data {
> +
> +	int rfa_en;
> +	unsigned int en_gpio_num;
> +};
> +
> +#endif /* __TPS61163A_H */

s/__TPS61163A_H/__TPS611XX_H


Best regards,
Jingoo Han

> --
> 1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 5a3eb2e..c779a85 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -418,6 +418,13 @@  config BACKLIGHT_TPS65217
 	  If you have a Texas Instruments TPS65217 say Y to enable the
 	  backlight driver.
 
+config BACKLIGHT_TPS611xx
+	tristate "TPS611xx Backlight"
+	depends on BACKLIGHT_CLASS_DEVICE && GPIOLIB
+	help
+	  This supports TI TPS61158, TPS61161, TPS61163 and TPS61165
+	  backlight driver based on EasyScale Protocol.
+
 config BACKLIGHT_AS3711
 	tristate "AS3711 Backlight"
 	depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index bb82002..44f1641 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -52,4 +52,5 @@  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_TPS611xx)	+= tps611xx_bl.o
 obj-$(CONFIG_BACKLIGHT_WM831X)		+= wm831x_bl.o
diff --git a/drivers/video/backlight/tps611xx_bl.c b/drivers/video/backlight/tps611xx_bl.c
new file mode 100644
index 0000000..4b76cdd
--- /dev/null
+++ b/drivers/video/backlight/tps611xx_bl.c
@@ -0,0 +1,486 @@ 
+/*
+ * Simple driver for Texas Instruments TPS611XX Backlight driver chip
+ *        using EasyScale Interface. It supports TPS61158, TPS61161,
+ *        TPS61163 and TPS61165.
+ *
+ * Copyright (C) 2014 Texas Instruments
+ * Author: Daniel Jeong  <gshark.jeong@gmail.com>
+ *	       Ldd Mlp <ldd-mlp@list.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/delay.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_device.h>
+#include <linux/platform_data/tps611xx_bl.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define CMD_FORWARD 0
+#define CMD_BACKWARD 1
+
+enum tps611xx_id {
+	TPS61158_ID = 0,
+	TPS61161_ID,
+	TPS61163_ID,
+	TPS61165_ID,
+};
+
+/*
+ * easyscale time spec
+ * @es_delay : es delay time(ns)
+ * @es_det   : es detection time(ns)
+ * @start    : start time of data stream(ns)
+ * @eos      : end time of data stream(ns)
+ * @reset    : ic shutdown time(ms)
+ * @logic_1_low : low time high bit(ns)
+ * @logic_0_low : low time low bit(ns)
+ * @ackn        : duation of ack condistion(ns)
+ * @ack_poll    : ack polling duration(ns)
+ */
+struct tps611xx_time {
+	unsigned int es_delay;
+	unsigned int es_det;
+	unsigned int start;
+	unsigned int eos;
+	unsigned int reset;
+	unsigned int logic_1_low;
+	unsigned int logic_0_low;
+	unsigned int ackn;
+	unsigned int ack_poll;
+};
+
+/*
+ * @seq : sequence of data transfer
+ * @size: size of data
+ * @brt_max : max brightness
+ * @brt_bmask : bit mask of dimming bits
+ * @rfa_bmask : bit mask of request of ack
+ */
+struct tps611xx_command {
+	int seq;
+	int size;
+	int brt_max;
+	int brt_bmask;
+	int rfa_bmask;
+};
+
+/*
+ * @id : product id
+ * @name : product name
+ * @addr : device address
+ * @cmd  : es command info
+ * @time : es time info
+ */
+struct tps611xx_esdata {
+	enum tps611xx_id id;
+	char *name;
+	int addr;
+	struct tps611xx_command cmd;
+	struct tps611xx_time time;
+};
+
+struct tps611xx_bl_data {
+	struct device *dev;
+	struct backlight_device *bled;
+	struct tps611xx_platform_data *pdata;
+
+	/*
+	 * @rfa_en : acknowlege request enable
+	 * @en_gpio: enable pin gpio no.
+	 * @esdata : easyscale data
+	 */
+	int rfa_en;
+	unsigned int en_gpio;
+	const struct tps611xx_esdata *esdata;
+};
+
+static struct tps611xx_esdata tps611xx_info[] = {
+	[TPS61158_ID] = {
+			 .id = TPS61158_ID,
+			 .name = "tps61158",
+			 .addr = 0x5800,
+			 .cmd = {
+				 .seq = CMD_FORWARD,
+				 .size = 16,
+				 .brt_max = 31,
+				 .brt_bmask = 0x1f,
+				 .rfa_bmask = 0x80},
+			 .time = {
+				  .es_delay = 100000,
+				  .es_det = 450000,
+				  .start = 3500,
+				  .eos = 3500,
+				  .reset = 4,
+				  .logic_1_low = 5000,
+				  .logic_0_low = 15000,
+				  .ackn = 900000,
+				  .ack_poll = 2000},
+			 },
+
+	[TPS61161_ID] = {
+			 .id = TPS61161_ID,
+			 .name = "tps61161",
+			 .addr = 0x7200,
+			 .cmd = {
+				 .seq = CMD_FORWARD,
+				 .size = 16,
+				 .brt_max = 31,
+				 .brt_bmask = 0x1f,
+				 .rfa_bmask = 0x80},
+			 .time = {
+				  .es_delay = 120000,
+				  .es_det = 280000,
+				  .start = 2000,
+				  .eos = 2000,
+				  .reset = 3,
+				  .logic_1_low = 3000,
+				  .logic_0_low = 7000,
+				  .ackn = 512000,
+				  .ack_poll = 2000},
+			 },
+
+	[TPS61163_ID] = {
+			 .id = TPS61163_ID,
+			 .name = "tps61163",
+			 .addr = 0x8F0000,
+			 .cmd = {
+				 .seq = CMD_BACKWARD,
+				 .size = 24,
+				 .brt_max = 511,
+				 .brt_bmask = 0x1ff,
+				 .rfa_bmask = 0x400},
+			 .time = {
+				  .es_delay = 100000,
+				  .es_det = 260000,
+				  .start = 2000,
+				  .eos = 2000,
+				  .reset = 3,
+				  .logic_1_low = 3000,
+				  .logic_0_low = 7000,
+				  .ackn = 512000,
+				  .ack_poll = 2000},
+			 },
+
+	[TPS61165_ID] = {
+			 .id = TPS61165_ID,
+			 .name = "tps61165",
+			 .addr = 0x7200,
+			 .cmd = {
+				 .seq = CMD_FORWARD,
+				 .size = 16,
+				 .brt_max = 31,
+				 .brt_bmask = 0x1f,
+				 .rfa_bmask = 0x80},
+			 .time = {
+				  .es_delay = 120000,
+				  .es_det = 280000,
+				  .start = 4000,
+				  .eos = 4000,
+				  .reset = 3,
+				  .logic_1_low = 3000,
+				  .logic_0_low = 7000,
+				  .ackn = 512000,
+				  .ack_poll = 2000},
+			 },
+};
+
+static int tps611xx_bl_update_status(struct backlight_device *bl)
+{
+	struct tps611xx_bl_data *pchip = bl_get_data(bl);
+	const struct tps611xx_esdata *esdata = pchip->esdata;
+	int data_in, t_low, t_logic, max_bmask;
+	unsigned long flags;
+
+	data_in = esdata->addr | (bl->props.brightness & esdata->cmd.brt_bmask);
+	if (pchip->rfa_en)
+		data_in |= esdata->cmd.rfa_bmask;
+
+	max_bmask = 0x1 << esdata->cmd.size;
+	t_logic = esdata->time.logic_1_low + esdata->time.logic_0_low;
+
+	local_irq_save(flags);
+	/* t_start : 2us high before data byte */
+	gpio_direction_output(pchip->en_gpio, 1);
+	ndelay(esdata->time.start);
+
+	/* forward command transfer */
+	if (esdata->cmd.seq == CMD_FORWARD) {
+		int addr_bmask = max_bmask >> 8;
+
+		for (max_bmask >>= 1; max_bmask > 0x0; max_bmask >>= 1) {
+			if (data_in & max_bmask)
+				t_low = esdata->time.logic_1_low;
+			else
+				t_low = esdata->time.logic_0_low;
+
+			gpio_direction_output(pchip->en_gpio, 0);
+			ndelay(t_low);
+			gpio_direction_output(pchip->en_gpio, 1);
+			ndelay(t_logic - t_low);
+
+			if (max_bmask == addr_bmask) {
+				gpio_direction_output(pchip->en_gpio, 0);
+				/* t_eos : low after address byte */
+				ndelay(esdata->time.eos);
+				gpio_direction_output(pchip->en_gpio, 1);
+				/* t_start : high before data byte */
+				ndelay(esdata->time.start);
+			}
+		}
+	} else {
+		/* backward command tansfer */
+		int bmask;
+
+		for (bmask = 0x01; bmask < max_bmask; bmask <<= 1) {
+			if (data_in & bmask)
+				t_low = esdata->time.logic_1_low;
+			else
+				t_low = esdata->time.logic_0_low;
+
+			gpio_direction_output(pchip->en_gpio, 0);
+			ndelay(t_low);
+			gpio_direction_output(pchip->en_gpio, 1);
+			ndelay(t_logic - t_low);
+		}
+	}
+
+	/*
+	 * t_eos : low after address byte
+	 * t_ackVal is also t_eos
+	 */
+	gpio_direction_output(pchip->en_gpio, 0);
+	ndelay(esdata->time.eos);
+
+	/* RFA management  */
+	if (pchip->rfa_en) {
+		int max_ack_time = esdata->time.ackn;
+		/* set input */
+		gpio_direction_input(pchip->en_gpio);
+		/* read acknowledge from chip */
+		while (max_ack_time > 0) {
+			if (gpio_get_value(pchip->en_gpio) == 0)
+				break;
+			max_ack_time -= esdata->time.ack_poll;
+		}
+		if (max_ack_time <= 0)
+			dev_err(pchip->dev,
+				"easyscale : no ack from %s\n", esdata->name);
+		else
+			ndelay(max_ack_time);
+	}
+	gpio_direction_output(pchip->en_gpio, 1);
+	local_irq_restore(flags);
+
+	return bl->props.brightness;
+}
+
+static int tps611xx_bl_get_brightness(struct backlight_device *bl)
+{
+	return bl->props.brightness;
+}
+
+static const struct backlight_ops tps611xx_bl_ops = {
+	.update_status = tps611xx_bl_update_status,
+	.get_brightness = tps611xx_bl_get_brightness,
+};
+
+static ssize_t tps611xx_enable_store(struct device *dev,
+				     struct device_attribute *devAttr,
+				     const char *buf, size_t size)
+{
+	struct tps611xx_bl_data *pchip = dev_get_drvdata(dev);
+	const struct tps611xx_esdata *esdata = pchip->esdata;
+	unsigned long flags;
+	unsigned int input;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &input);
+	if (ret)
+		return -EINVAL;
+
+	local_irq_save(flags);
+	if (input == 0) {
+		/* chip disable */
+		gpio_direction_output(pchip->en_gpio, 0);
+		/* low more than reset ms to reset */
+		mdelay(esdata->time.reset);
+	} else {
+		/* easyscale detection window */
+		gpio_direction_output(pchip->en_gpio, 1);
+		ndelay(esdata->time.es_delay);
+		gpio_direction_output(pchip->en_gpio, 0);
+		ndelay(esdata->time.es_det);
+		gpio_direction_output(pchip->en_gpio, 1);
+	}
+	local_irq_restore(flags);
+
+	return size;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR, NULL, tps611xx_enable_store);
+
+#ifdef CONFIG_OF
+static struct of_device_id tps611xx_backlight_of_match[] = {
+	{.compatible = "ti,tps61158_bl", .data = &tps611xx_info[TPS61158_ID]},
+	{.compatible = "ti,tps61161_bl", .data = &tps611xx_info[TPS61161_ID]},
+	{.compatible = "ti,tps61163_bl", .data = &tps611xx_info[TPS61163_ID]},
+	{.compatible = "ti,tps61165_bl", .data = &tps611xx_info[TPS61165_ID]},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, tps611xx_backlight_of_match);
+
+static int tps611xx_backlight_parse_dt(struct tps611xx_bl_data *pchip)
+{
+	struct device *dev = pchip->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *of_id =
+	    of_match_device(tps611xx_backlight_of_match, dev);
+	u32 value;
+	int ret;
+
+	if (!node)
+		return -ENODEV;
+
+	if (!of_id || !of_id->data) {
+		dev_err(dev, "Failed to find tps611xx chip id\n");
+		return -EFAULT;
+	}
+	pchip->esdata = of_id->data;
+
+	ret = of_property_read_u32(node, "en_gpio_num", &value);
+	if (ret < 0)
+		return ret;
+	pchip->en_gpio = value;
+	ret = of_property_read_u32(node, "rfa_en", &value);
+	if (ret < 0)
+		return ret;
+	pchip->rfa_en = value;
+
+	return 0;
+}
+#else
+static int tps611xx_backlight_parse_dt(struct tps611xx_bl_data *pchip)
+{
+	return -ENODEV;
+}
+#endif
+
+static int tps611xx_backlight_probe(struct platform_device *pdev)
+{
+	struct tps611xx_bl_data *pchip;
+	struct backlight_properties props;
+	const struct tps611xx_esdata *esdata;
+	struct tps611xx_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	unsigned long flags;
+	int ret;
+
+	pchip = devm_kzalloc(&pdev->dev,
+			     sizeof(struct tps611xx_bl_data), GFP_KERNEL);
+	if (pchip == NULL)
+		return -ENOMEM;
+	pchip->dev = &pdev->dev;
+
+	if (pdata == NULL) {
+		ret = tps611xx_backlight_parse_dt(pchip);
+		if (ret < 0)
+			return ret;
+	} else {
+		pchip->rfa_en = pdata->rfa_en;
+		pchip->en_gpio = pdata->en_gpio_num;
+		pchip->esdata = (const struct tps611xx_esdata *)
+		    platform_get_device_id(pdev)->driver_data;
+	}
+	esdata = pchip->esdata;
+
+	memset(&props, 0, sizeof(struct backlight_properties));
+	props.brightness = esdata->cmd.brt_max;
+	props.max_brightness = esdata->cmd.brt_max;
+	props.type = BACKLIGHT_RAW;
+	pchip->bled =
+	    devm_backlight_device_register(pchip->dev, TPS611XX_NAME,
+					   pchip->dev, pchip,
+					   &tps611xx_bl_ops, &props);
+	if (IS_ERR(pchip->bled))
+		return PTR_ERR(pchip->bled);
+
+	/* for enable/disable */
+	ret = device_create_file(&(pchip->bled->dev), &dev_attr_enable);
+	if (ret < 0) {
+		dev_err(pchip->dev, "failed : add sysfs entries\n");
+		return ret;
+	}
+	platform_set_drvdata(pdev, pchip);
+
+	/* EasyScale init */
+	ret = gpio_request_one(pchip->en_gpio, GPIOF_OUT_INIT_HIGH, "tps611xx");
+	if (ret) {
+		device_remove_file(&(pchip->bled->dev), &dev_attr_enable);
+		dev_err(pchip->dev, "failed : get gpio %d\n", pchip->en_gpio);
+		return ret;
+	}
+
+	/*
+	 * ES Detection Window
+	 *   - ES detect delay
+	 *   - ES detect time
+	 */
+	local_irq_save(flags);
+	gpio_direction_output(pchip->en_gpio, 1);
+	ndelay(esdata->time.es_delay);
+	gpio_direction_output(pchip->en_gpio, 0);
+	ndelay(esdata->time.es_det);
+	gpio_direction_output(pchip->en_gpio, 1);
+	local_irq_restore(flags);
+	dev_info(pchip->dev,
+		 "%s EasyScale is initialized\n", pchip->esdata->name);
+	return 0;
+}
+
+static int tps611xx_backlight_remove(struct platform_device *pdev)
+{
+	struct tps611xx_bl_data *pchip = platform_get_drvdata(pdev);
+	const struct tps611xx_esdata *esdata = pchip->esdata;
+
+	device_remove_file(&(pchip->bled->dev), &dev_attr_enable);
+	gpio_direction_output(pchip->en_gpio, 0);
+	mdelay(esdata->time.reset);
+	return 0;
+}
+
+static const struct platform_device_id tps611xx_id_table[] = {
+	{TPS61158_NAME, (unsigned long)&tps611xx_info[TPS61158_ID]},
+	{TPS61161_NAME, (unsigned long)&tps611xx_info[TPS61161_ID]},
+	{TPS61163_NAME, (unsigned long)&tps611xx_info[TPS61163_ID]},
+	{TPS61165_NAME, (unsigned long)&tps611xx_info[TPS61165_ID]},
+	{}
+};
+
+static struct platform_driver tps611xx_backlight_driver = {
+	.driver = {
+		   .name = TPS611XX_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(tps611xx_backlight_of_match),
+		   },
+	.probe = tps611xx_backlight_probe,
+	.remove = tps611xx_backlight_remove,
+	.id_table = tps611xx_id_table,
+};
+
+module_platform_driver(tps611xx_backlight_driver);
+
+MODULE_DESCRIPTION("EasyScale based tps611xx Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tps611xx_bl");
diff --git a/include/linux/platform_data/tps611xx_bl.h b/include/linux/platform_data/tps611xx_bl.h
new file mode 100644
index 0000000..b7c4504
--- /dev/null
+++ b/include/linux/platform_data/tps611xx_bl.h
@@ -0,0 +1,30 @@ 
+/*
+ * Simple driver for Texas Instruments TPS61163a Backlight driver chip
+ * Copyright (C) 2014 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.
+ *
+ */
+
+#ifndef __TPS611XX_H
+#define __TPS611XX_H
+
+#define TPS611XX_NAME "tps611xx_bl"
+#define TPS61158_NAME "tps61158_bl"
+#define TPS61161_NAME "tps61161_bl"
+#define TPS61163_NAME "tps61163_bl"
+#define TPS61165_NAME "tps61165_bl"
+
+/* struct tps61163a platform data
+ * @rfa_en : request for acknowledge
+ * @en_gpio_num : gpio number for en_pin
+ */
+struct tps611xx_platform_data {
+
+	int rfa_en;
+	unsigned int en_gpio_num;
+};
+
+#endif /* __TPS61163A_H */