Add TPS611xx(58/61/63/65) driver based on EasyScale protocol v0.5.TPS611XX.V0
authorDaniel jeong <daniel.jeong@ti.com>
Mon, 2 Jun 2014 06:27:52 +0000 (15:27 +0900)
committerDaniel jeong <daniel.jeong@ti.com>
Mon, 2 Jun 2014 06:27:52 +0000 (15:27 +0900)
Signed-off-by: Daniel jeong <daniel.jeong@ti.com>
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/tps611xx_bl.c [new file with mode: 0644]
include/linux/platform_data/tps611xx_bl.h [new file with mode: 0644]

index 6963bcf34fda25f7dc76d91ab957b4b93cc42732..cc95545810efa0a51e06f63d65dd83e6260e6597 100644 (file)
@@ -435,10 +435,19 @@ config BACKLIGHT_TPS65217
 config BACKLIGHT_TPS61163A
        tristate "TPS61163A Backlight"
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on GPIOLIB
        help
          This supports TPS61163A Texas Instruments backlight driver
          based on EasyScale Interface.
 
+config BACKLIGHT_TPS611XX
+       tristate "TPS611XX Backlight"
+       depends on BACKLIGHT_CLASS_DEVICE
+       depends on GPIOLIB
+       help
+         This supports TPS61158, TPS61161, TPS61163 and TPS61165
+         Texas Instruments backlight drivers based on EasyScale Interface.
+
 config BACKLIGHT_AS3711
        tristate "AS3711 Backlight"
        depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711
index c6abc3d7d13e2f2a96f3c6a33fb12667b17ce177..f04eceb2a02391a88fbdd534eae554f16f1f03e4 100644 (file)
@@ -55,4 +55,5 @@ obj-$(CONFIG_BACKLIGHT_SAHARA)                += kb3886_bl.o
 obj-$(CONFIG_BACKLIGHT_TOSA)           += tosa_bl.o
 obj-$(CONFIG_BACKLIGHT_TPS65217)       += tps65217_bl.o
 obj-$(CONFIG_BACKLIGHT_TPS61163A)      += tps61163a_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 (file)
index 0000000..8a49d60
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * 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;
+
+       printk(KERN_INFO "\n\n");
+       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");
+               goto err_out;
+       }
+       platform_set_drvdata(pdev, pchip);
+
+       /* EasyScale init */
+       ret = gpio_request(pchip->en_gpio, "tps611xx");
+       if (ret) {
+               device_remove_file(&(pchip->bled->dev), &dev_attr_enable);
+               dev_err(pchip->dev, "failed : get gpio %d\n", pchip->en_gpio);
+               goto err_out;
+       }
+
+       /*
+        * 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 based on EasyScale is initialized\n", pchip->esdata->name);
+       return 0;
+
+err_out:
+       backlight_device_unregister(pchip->bled);
+       return ret;
+}
+
+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 (file)
index 0000000..082c5c5
--- /dev/null
@@ -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
+ * @use_rfa : request for acknowledge
+ * @en_gpio_num : en pin gpio number
+ */
+struct tps611xx_platform_data {
+
+       int rfa_en;
+       unsigned int en_gpio_num;
+};
+
+#endif /* __TPS61163A_H */