Add LM3585 Device driver -v0 based on LK3.8 v0.2.LM3585.V0
authorDaniel jeong <daniel.jeong@ti.com>
Wed, 7 May 2014 01:20:53 +0000 (10:20 +0900)
committerDaniel jeong <daniel.jeong@ti.com>
Wed, 7 May 2014 01:20:53 +0000 (10:20 +0900)
Signed-off-by: Daniel jeong <daniel.jeong@ti.com>
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/lm3585_bl.c [new file with mode: 0644]
include/linux/platform_data/lm3585_bl.h [new file with mode: 0644]

index 908048f2e012b5bac0c500837528ef94d0b37dc3..5ba2edf735760999c5490ec231c4fb6e2b9fb9d5 100644 (file)
@@ -368,6 +368,13 @@ config BACKLIGHT_AAT2870
          If you have a AnalogicTech AAT2870 say Y to enable the
          backlight driver.
 
+config BACKLIGHT_LM3585
+       tristate "Backlight Driver for LM3585"
+       depends on BACKLIGHT_CLASS_DEVICE && I2C
+       select REGMAP_I2C
+       help
+         This supports TI LM3585 Backlight Driver
+
 config BACKLIGHT_LM3630A
        tristate "Backlight Driver for LM3630A"
        depends on BACKLIGHT_CLASS_DEVICE && I2C
index cbc5ac3a40546b2f27b4f9507c85c371f4bd0870..6ecab6863b994e839fec7d560e7609e52153c22e 100644 (file)
@@ -37,6 +37,7 @@ 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_LM3585)         += lm3585_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3630A)                += lm3630a_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3639)         += lm3639_bl.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO)         += locomolcd.o
diff --git a/drivers/video/backlight/lm3585_bl.c b/drivers/video/backlight/lm3585_bl.c
new file mode 100644 (file)
index 0000000..6f75aa5
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Simple driver for Texas Instruments lm3585 Backlight driver chip
+ *
+ * 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/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_data/lm3585_bl.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#define REG_CTRL                               0x00
+#define REG_ENABLE                             0x01
+#define REG_LED_EN_LO                  0x02
+#define REG_LED_EN_HI                  0x03
+#define REG_FAULT                              0x04
+#define REG_BBOOST_MODE                        0x06
+#define REG_BBOOST_OUTV                        0x07
+#define REG_BBOOST_CTRL                        0x08
+#define REG_BOOSTV                             0x0a
+#define REG_RAMP_DURATION              0x0f
+#define REG_RAMP_TAR_LO                        0x10
+#define REG_RAMP_TAR_HI                        0x11
+#define REG_RAMP_TAR_CTRL              0x12
+#define REG_READ_RAMP_LO               0x14
+#define REG_READ_RAMP_HI               0x15
+#define REG_READ_CABC_PWM_LO   0x16
+#define REG_READ_CABC_PWM_HI   0x17
+#define REG_READ_LED_DC_LO             0x18
+#define REG_READ_LED_DC_HI             0x19
+#define REG_REVISION                   0x1f
+#define REG_BBOOST_TRIM                        0x25
+#define REG_MAX        0xff
+
+#define LM3585_ENABLED 1
+#define LM3585_DISABLED 0
+
+struct lm3585_chip {
+       struct device *dev;
+       struct lm3585_platform_data *pdata;
+       struct backlight_device *bled;
+       struct regmap *regmap;
+       int is_enabled;
+};
+
+static int lm3585_startup(struct lm3585_chip *pchip)
+{
+       unsigned int rev1, rev2;
+       int ret;
+
+       /* POR wait */
+       msleep(1);
+       /* sw reset */
+       ret = regmap_write(pchip->regmap, REG_CTRL, 0x01);
+       msleep(1);
+       /* LED enable */
+       ret |= regmap_write(pchip->regmap,
+                           REG_LED_EN_LO, pchip->pdata->led_enable);
+       ret |= regmap_update_bits(pchip->regmap,
+                                 REG_LED_EN_HI, 0x03,
+                                 pchip->pdata->led_enable >> 8);
+
+       /* Vpos 5.1V */
+       ret |= regmap_write(pchip->regmap, REG_BOOSTV, 0x07);
+
+       /*
+        * revision info and set buck/boost current limit
+        * A0 - 0x00
+        * A1 - 0x01
+        * B0 - 0x04 & R25[1:0] : 11b
+        * B1 - 0x04 & R25[1:0] : 10b
+        * B2 - 0x05
+        */
+       ret |= regmap_read(pchip->regmap, REG_REVISION, &rev1);
+       switch (rev1) {
+       case 0x00:
+       case 0x01:
+               ret |= regmap_write(pchip->regmap, REG_BBOOST_CTRL, 0x4A);
+               break;
+       case 0x04:
+               ret |= regmap_read(pchip->regmap, REG_BBOOST_TRIM, &rev2);
+               switch (rev2 & 0x03) {
+               case 0x2:
+                       ret |=
+                           regmap_write(pchip->regmap, REG_BBOOST_CTRL, 0x4A);
+                       break;
+               case 0x3:
+                       ret |=
+                           regmap_write(pchip->regmap, REG_BBOOST_CTRL, 0x0A);
+                       break;
+               }
+               break;
+       case 0x05:
+               ret |= regmap_write(pchip->regmap, REG_BBOOST_CTRL, 0x0A);
+               break;
+       }
+       /* enable buck boost & wait soft start time */
+       ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x01);
+       msleep(10);
+       /* enable charge pump discharge */
+       ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x09);
+       msleep(100);
+       /* enable boost converter */
+       ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x0b);
+       msleep(1);
+       /* enable charge pump Vneg */
+       ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x0f);
+       /* enable global led */
+       ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x2f);
+       /* enable cabc pwm input control */
+       if (pchip->pdata->cabc_input != 0)
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x3f);
+
+       /* set LED current to 0 initially */
+       ret |= regmap_write(pchip->regmap, REG_RAMP_TAR_LO, 0x00);
+       ret |= regmap_update_bits(pchip->regmap, REG_RAMP_TAR_HI,
+                                 0xcf, (pchip->pdata->ctype << 6) | 0x00);
+       /* ramp duration */
+       ret |= regmap_write(pchip->regmap,
+                           REG_RAMP_DURATION, pchip->pdata->ramp_time);
+       /* adgo */
+       ret |= regmap_write(pchip->regmap, REG_RAMP_TAR_CTRL, 0x01);
+
+       if (ret == 0)
+               pchip->is_enabled = LM3585_ENABLED;
+       return ret;
+}
+
+static ssize_t lm3585_reset_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t size)
+{
+       struct lm3585_chip *pchip = dev_get_drvdata(dev);
+       u8 input;
+       int ret;
+
+       if (kstrtou8(buf, 0, &input))
+               return -EINVAL;
+
+       if (input == 1) {
+               ret = lm3585_startup(pchip);
+               if (ret < 0)
+                       return ret;
+       }
+       return size;
+}
+
+static ssize_t lm3585_fault_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct lm3585_chip *pchip = dev_get_drvdata(dev);
+       unsigned int fault;
+       int ret;
+
+       ret = regmap_read(pchip->regmap, REG_FAULT, &fault);
+       if (ret < 0) {
+               dev_err(pchip->dev, "fail : i2c access to register.\n");
+               return sprintf(buf, "%d\n", ret);
+       }
+
+       if (fault & 0x1)
+               dev_err(pchip->dev, "LM3585 Temperature Fault.\n");
+       if (fault & 0x2)
+               dev_err(pchip->dev, "LM3585 Boost Over Voltage Fault.\n");
+       if (fault & 0x4)
+               dev_err(pchip->dev, "LM3585 LED Short Fault.\n");
+       if (fault & 0x8)
+               dev_err(pchip->dev, "LM3585 LED Open Fault.\n");
+
+       return sprintf(buf, "%d\n", fault);
+}
+
+static ssize_t lm3585_enable_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct lm3585_chip *pchip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", pchip->is_enabled);
+}
+
+static ssize_t lm3585_enable_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t size)
+{
+       struct lm3585_chip *pchip = dev_get_drvdata(dev);
+       u8 input;
+       int ret;
+
+       if (kstrtou8(buf, 0, &input))
+               return -EINVAL;
+
+       if (input == LM3585_DISABLED) {
+               /* turn off backlight */
+               ret = regmap_write(pchip->regmap, REG_ENABLE, 0x0f);
+               /* turn off charge pump */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x0b);
+               /* turn off boost */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x09);
+               msleep(100);
+               /* turn off charge pump discharge */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x01);
+               /* turn off buck boost */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x00);
+       } else {
+               /* turn on Vout */
+               ret = regmap_write(pchip->regmap, REG_ENABLE, 0x01);
+               /* turn on the Vpos */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x03);
+               /* enable charger pump */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x0F);
+               /* turn on backlight */
+               ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x2F);
+               /* enable cabc pwm control */
+               if (pchip->pdata->cabc_input != 0)
+                       ret |= regmap_write(pchip->regmap, REG_ENABLE, 0x3F);
+       }
+       if (ret < 0)
+               return ret;
+       pchip->is_enabled = input;
+
+       return size;
+}
+
+#define LM3585_DVNODE_MAX 3
+#define lm3585_attr(_name, _show, _store)\
+{\
+       .attr = {\
+               .name = _name,\
+               .mode = S_IWUSR | S_IRUSR,\
+       },\
+       .show = _show,\
+       .store = _store,\
+}
+
+static struct device_attribute lm3585_dev_attr[] = {
+       lm3585_attr("fault", lm3585_fault_show, NULL),
+       lm3585_attr("enable", lm3585_enable_show, lm3585_enable_store),
+       lm3585_attr("reset", NULL, lm3585_reset_store),
+};
+
+/* brightness control */
+static int lm3585_update_status(struct backlight_device *bl)
+{
+       int ret = -EINVAL;
+       struct lm3585_chip *pchip = bl_get_data(bl);
+
+       if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+               bl->props.brightness = 0;
+
+       /* adstop */
+       ret = regmap_write(pchip->regmap, REG_RAMP_TAR_CTRL, 0x02);
+       /* set target iled low */
+       ret |= regmap_write(pchip->regmap,
+                           REG_RAMP_TAR_LO, bl->props.brightness & 0xff);
+       /* set target iled high */
+       ret |= regmap_update_bits(pchip->regmap,
+                                 REG_RAMP_TAR_HI, 0x0f,
+                                 bl->props.brightness >> 8);
+       /* ramp duration */
+       ret |= regmap_write(pchip->regmap,
+                           REG_RAMP_DURATION, pchip->pdata->ramp_time);
+       /* adgo */
+       ret |= regmap_write(pchip->regmap, REG_RAMP_TAR_CTRL, 0x01);
+
+       if (ret < 0)
+               dev_err(pchip->dev, "fail : i2c access to register.\n");
+       else
+               ret = bl->props.brightness;
+
+       return ret;
+}
+
+static int lm3585_get_brightness(struct backlight_device *bl)
+{
+       return bl->props.brightness;
+}
+
+static const struct backlight_ops lm3585_bled_ops = {
+       .options = BL_CORE_SUSPENDRESUME,
+       .update_status = lm3585_update_status,
+       .get_brightness = lm3585_get_brightness,
+};
+
+static const struct regmap_config lm3585_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = REG_MAX,
+};
+
+#ifdef CONFIG_OF
+static struct lm3585_platform_data *lm3585_parse_dt(struct i2c_client *client)
+{
+       struct device_node *node = client->dev.of_node;
+       struct lm3585_platform_data *pdata = NULL;
+       u32 rdata;
+
+       if (!node) {
+               dev_err(&client->dev, "can't find data in device tree\n");
+               return pdata;
+       }
+
+       pdata = devm_kzalloc(&client->dev,
+                            sizeof(struct lm3585_platform_data), GFP_KERNEL);
+       if (pdata == NULL)
+               return pdata;
+
+       of_property_read_u32(node, "led_enable", &rdata);
+       pdata->led_enable = (enum lm3585_leds_enable)rdata;
+       of_property_read_u32(node, "ctype", &rdata);
+       pdata->ctype = (enum lm3585_curve_type)rdata;
+       of_property_read_u32(node, "cabc_input", &rdata);
+       pdata->cabc_input = (enum lm3585_cabc_input)rdata;
+       of_property_read_u32(node, "ramp_time", &rdata);
+       pdata->ramp_time = rdata;
+
+       return pdata;
+}
+
+static const struct of_device_id of_lm3585_bl_match[] = {
+       {.compatible = "ti,lm3585",},
+       {},
+};
+
+#else
+static struct lm3585_platform_data *lm3585_parse_dt(struct i2c_client *client)
+{
+       return NULL;
+}
+#endif
+
+static int lm3585_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct lm3585_chip *pchip;
+       struct lm3585_platform_data *pdata = dev_get_platdata(&client->dev);
+       struct backlight_properties props;
+       int ret, icnt;
+
+       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 lm3585_chip), GFP_KERNEL);
+       if (!pchip)
+               return -ENOMEM;
+       pchip->dev = &client->dev;
+
+       pchip->regmap = devm_regmap_init_i2c(client, &lm3585_regmap);
+       if (IS_ERR(pchip->regmap)) {
+               ret = PTR_ERR(pchip->regmap);
+               dev_err(pchip->dev, "fail : allocate i2c register map.\n");
+               return ret;
+       }
+
+       if (pdata == NULL) {
+               pdata = lm3585_parse_dt(client);
+               if (pdata == NULL) {
+                       pdata = devm_kzalloc(pchip->dev,
+                                            sizeof(struct
+                                                   lm3585_platform_data),
+                                            GFP_KERNEL);
+                       if (pdata == NULL) {
+                               dev_err(pchip->dev, "fail to get memory\n");
+                               return -ENOMEM;
+                       }
+                       pdata->ramp_time = 0;
+                       pdata->ctype = LM3585_CURVE_LINEAR;
+                       pdata->led_enable = LM3585_LED_ALL_ON;
+                       pdata->cabc_input = LM3585_CABC_INPUT_DISABLE;
+               }
+       }
+       pchip->pdata = pdata;
+
+       i2c_set_clientdata(client, pchip);
+
+       ret = lm3585_startup(pchip);
+       if (ret < 0) {
+               dev_err(pchip->dev, "fail : start up.\n");
+               return ret;
+       }
+
+       props.brightness = 0;
+       props.type = BACKLIGHT_RAW;
+       props.max_brightness = LM3585_BR_MAX;
+       pchip->bled = devm_backlight_device_register(pchip->dev,
+                                                    LM3585_NAME, pchip->dev,
+                                                    pchip, &lm3585_bled_ops,
+                                                    &props);
+       if (IS_ERR(pchip->bled)) {
+               dev_err(pchip->dev, "fail : backlight register.\n");
+               ret = PTR_ERR(pchip->bled);
+               return ret;
+       }
+
+       for (icnt = 0; icnt < LM3585_DVNODE_MAX; icnt++) {
+               ret =
+                   device_create_file(&(pchip->bled->dev),
+                                      &lm3585_dev_attr[icnt]);
+               if (ret < 0) {
+                       dev_err(pchip->dev, "fail : node create\n");
+                       goto err_dn_out;
+               }
+       }
+
+       dev_info(pchip->dev, "LM3585 backlight Initialized.\n");
+       return 0;
+
+err_dn_out:
+       while (--icnt >= 0)
+               device_remove_file(&(pchip->bled->dev), &lm3585_dev_attr[icnt]);
+
+       return ret;
+}
+
+static int lm3585_remove(struct i2c_client *client)
+{
+       struct lm3585_chip *pchip = i2c_get_clientdata(client);
+       int icnt;
+
+       for (icnt = 0; icnt < LM3585_DVNODE_MAX; icnt++)
+               device_remove_file(&(pchip->bled->dev), &lm3585_dev_attr[icnt]);
+       return 0;
+}
+
+static const struct i2c_device_id lm3585_id[] = {
+       {LM3585_NAME, 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3585_id);
+static struct i2c_driver lm3585_i2c_driver = {
+       .driver = {
+                  .name = LM3585_NAME,
+                  },
+       .probe = lm3585_probe,
+       .remove = lm3585_remove,
+       .id_table = lm3585_id,
+};
+
+module_i2c_driver(lm3585_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LM3585 Backlight Driver");
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/lm3585_bl.h b/include/linux/platform_data/lm3585_bl.h
new file mode 100644 (file)
index 0000000..6027f5e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Simple driver for Texas Instruments LM3585 Backlight driver chip
+ * 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.
+ *
+ */
+
+#ifndef __LM3585_H
+#define __LM3585_H
+
+#define LM3585_NAME "lm3585"
+#define LM3585_ADDR 0x31
+
+#define LM3585_BR_MAX 4095
+
+enum lm3585_leds_enable {
+       LM3585_LED_ALL_OFF = 0x00,
+       LM3585_LED_0_ON = 0x01,
+       LM3585_LED_1_ON = 0x02,
+       LM3585_LED_2_ON = 0x04,
+       LM3585_LED_3_ON = 0x08,
+       LM3585_LED_4_ON = 0x10,
+       LM3585_LED_5_ON = 0x20,
+       LM3585_LED_6_ON = 0x40,
+       LM3585_LED_7_ON = 0x80,
+       LM3585_LED_8_ON = 0x100,
+       LM3585_LED_9_ON = 0x200,
+       LM3585_LED_ALL_ON = 0x3ff,
+};
+
+enum lm3585_cabc_input {
+       LM3585_CABC_INPUT_DISABLE = 0,
+       LM3585_CABC_INPUT_ENABLE,
+};
+
+enum lm3585_curve_type {
+       LM3585_CURVE_LINEAR = 0,
+       LM3585_CURVE_QUADRATIC,
+       LM3585_CURVE_CUBIC,
+       LM3585_CURVE_QUARTIC,
+};
+
+/* 
+ * struct lm3585 platform data
+ * @led_enable : each bit disables the chosen LED output or enable all.
+ * @ctype : curve type
+ * @cabc_input : the CABC PWM input control 
+ * @ramp_time : the duration of the ILED transitioin. 0~3fh
+ */
+struct lm3585_platform_data {
+
+       enum lm3585_leds_enable led_enable;
+       enum lm3585_curve_type  ctype;
+       enum lm3585_cabc_input  cabc_input;     
+       int  ramp_time;
+};
+
+#endif /* __LM3585_H */