mfd : add new mfd device LM3632-backlight, regulator and flash
authorDaniel Jeong <gshark.jeong@gmail.com>
Thu, 12 Feb 2015 11:51:30 +0000 (20:51 +0900)
committerDaniel Jeong <gshark.jeong@gmail.com>
Thu, 12 Feb 2015 11:51:30 +0000 (20:51 +0900)
Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com>
13 files changed:
Documentation/devicetree/bindings/mfd/lm3632.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/lm3632-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/backlight/lm3632_bl.txt [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/lm3632.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/lm3632-regulator.c [new file with mode: 0644]
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/lm3632_bl.c [new file with mode: 0644]
include/linux/mfd/lm3632.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/mfd/lm3632.txt b/Documentation/devicetree/bindings/mfd/lm3632.txt
new file mode 100644 (file)
index 0000000..f670058
--- /dev/null
@@ -0,0 +1,59 @@
+TI LM3632 MFD Driver
+
+Required properties:
+  - compatible: "ti,lm3632"
+  - reg: I2C slave address. 0x29.
+  - ti,en-gpio: GPIO number of LM3632 nRST pin.
+
+LM3632 consists of two sub-devices, lm3632-regulator and lm3632-bl.
+
+For the LM3632 regulator properties please refer to:
+Documentation/devicetree/bindings/regulator/lm3632-regulator.txt
+
+For the LM3632 backlight properties please refer to:
+Documentation/devicetree/bindings/video/backlight/lm3632_bl.txt
+
+Example:
+
+lm3632@11 {
+       compatible = "ti,lm3632";
+       reg = <0x11>;
+
+       /* GPIO134 for HWEN pin */
+       ti,en-gpio = <&gpio5 6 0>;
+
+       /* Only Vpos and Vneg are used with LCD boost */
+       regulators {
+               compatible = "ti,lm3632-regulator";
+
+               vboost {
+                       regulator-name = "lcd_boost";
+                       regulator-min-microvolt = <4500000>;
+                       regulator-max-microvolt = <6350000>;
+                       regulator-always-on;
+               };
+
+               vpos {
+                       regulator-name = "lcd_vpos";
+                       regulator-min-microvolt = <4000000>;
+                       regulator-max-microvolt = <6000000>;
+                       regulator-boot-on;
+               };
+
+               vneg {
+                       regulator-name = "lcd_vneg";
+                       regulator-min-microvolt = <4000000>;
+                       regulator-max-microvolt = <6000000>;
+                       regulator-boot-on;
+               };
+       };
+
+       /* Backlight mode is I2C + PWM, two strings used */
+       backlight {
+               compatible = "ti,lm3632-backlight";
+
+               bl-name = "lcd";
+               full-strings-used;
+               mode-comb1;
+       };
+};
diff --git a/Documentation/devicetree/bindings/regulator/lm3632-regulator.txt b/Documentation/devicetree/bindings/regulator/lm3632-regulator.txt
new file mode 100644 (file)
index 0000000..7edb7ba
--- /dev/null
@@ -0,0 +1,57 @@
+TI LM3632 Regulator Driver
+
+Required properties:
+  - compatible: "ti,lm3632-regulator"
+  - Regulator init data from of-regulator structure.
+    Please refer to regulator.txt in this directory.
+
+Example:
+
+&i2c4 {
+       clock-frequency = <400000>;
+
+       lm3632@11 {
+               compatible = "ti,lm3632";
+               reg = <0x29>;
+
+               /* GPIO134 for HWEN pin */
+               ti,en-gpio = <&gpio5 6 0>;
+
+               regulators {
+                       compatible = "ti,lm3632-regulator";
+
+                       vboost {
+                               regulator-name = "lcd_boost";
+                               regulator-min-microvolt = <4500000>;
+                               regulator-max-microvolt = <6350000>;
+                               regulator-always-on;
+                       };
+
+                       vcont {
+                               regulator-name = "lcd_cont";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <3300000>;
+                       };
+
+                       voref {
+                               regulator-compatible = "voref";
+                               regulator-name = "lcd_oref";
+                               regulator-min-microvolt = <4000000>;
+                               regulator-max-microvolt = <6000000>;
+                       };
+
+                       vpos {
+                               regulator-name = "lcd_vpos";
+                               regulator-min-microvolt = <4000000>;
+                               regulator-max-microvolt = <6000000>;
+                               regulator-boot-on;
+                       };
+
+                       vneg {
+                               regulator-name = "lcd_vneg";
+                               regulator-min-microvolt = <4000000>;
+                               regulator-max-microvolt = <6000000>;
+                               regulator-boot-on;
+                       };
+               };
+};
diff --git a/Documentation/devicetree/bindings/video/backlight/lm3632_bl.txt b/Documentation/devicetree/bindings/video/backlight/lm3632_bl.txt
new file mode 100644 (file)
index 0000000..256b053
--- /dev/null
@@ -0,0 +1,65 @@
+TI LM3632 Backlight Driver
+
+Required properties:
+  - compatible: "ti,lm3632-backlight"
+
+Optional properties:
+  - bl-name: Backlight device name
+  - full-strings-used: Define it in case of two LED strings used.
+  - mode-pwm-only: PWM input mode
+    or mode-comb1: I2C x PWM befoer sloping
+    or mode-comb2: Sloped I2C x PWM
+    The default mode is the I2C only.
+  - initial-brightness: Initial brightness value
+
+PWM specific optional properties:
+  - pwm-period: PWM period value. Define it in case of PWM based control mode.
+  - pwms and pwm-names: Please refer to Documentation/devicetree/bindings/pwm/pwm.txt.
+
+
+Example 1:
+Brightness is I2C only mode. Backlight device name is 'lcd'.
+
+&i2c4 {
+       clock-frequency = <400000>;
+
+       lm3632@11 {
+               compatible = "ti,lm3632";
+               reg = <0x11>;
+
+               /* GPIO134 for HWEN pin */
+               ti,en-gpio = <&gpio5 6 0>;
+
+               backlight {
+                       compatible = "ti,lm3632-backlight";
+                       bl-name = "lcd";
+                       full-strings-used;
+               };
+};
+
+Example 2:
+LM3632 brightness is controlled by PWM3943 controller.
+PWM3943 is a PWM controller. PWM#1 is port number of PMW3943.
+
+&i2c4 {
+       clock-frequency = <400000>;
+
+       lm3632@11 {
+               compatible = "ti,lm3632";
+               reg = <0x11>;
+
+               /* GPIO134 for HWEN pin */
+               ti,en-gpio = <&gpio5 6 0>;
+
+               backlight {
+                       compatible = "ti,lm3632-backlight";
+                       bl-name = "lcd";
+                       full-strings-used;
+                       mode-pwm-only;
+
+                       pwm-period = <10000>;
+
+                       pwms = <&pwm3943 1 10000>;
+                       pwm-names = "lm3632-backlight";
+               };
+};
index 28e29fcb7e49b031ba0fa8c85135d71b27674e06..3d250c5c291fcf1be5699124fa4e2f0409364ba4 100644 (file)
@@ -58,6 +58,15 @@ config MFD_LM3631
          Say yes here to enable support for TI LM3631 chip.
          LM3631 has 2 strings for backlight with 5 regulators for LCD bias.
 
+config MFD_LM3632
+       tristate "TI LM3632 Backlight and Bias Power Driver"
+       depends on I2C
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+         Say yes here to enable support for TI LM3632 chip.
+         LM3632 has 2 strings for backlight with 5 regulators for LCD bias.
+
 config MFD_AAT2870_CORE
        bool "AnalogicTech AAT2870"
        select MFD_CORE
index 109247099be48de1dde321f27d25fbd1605e9bad..9a35a740bfafd67774870fe881db6e5e5cbc6ab3 100644 (file)
@@ -104,6 +104,7 @@ obj-$(CONFIG_MFD_DA9052_I2C)        += da9052-i2c.o
 
 obj-$(CONFIG_MFD_LP3943)       += lp3943.o
 obj-$(CONFIG_MFD_LM3631)       += lm3631.o
+obj-$(CONFIG_MFD_LM3632)       += lm3632.o
 obj-$(CONFIG_MFD_LP8788)       += lp8788.o lp8788-irq.o
 
 da9055-objs                    := da9055-core.o da9055-i2c.o
diff --git a/drivers/mfd/lm3632.c b/drivers/mfd/lm3632.c
new file mode 100644 (file)
index 0000000..8b2401f
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * TI LM3632 MFD Driver
+ *
+ * Copyright 2015 Texas Instruments
+ *
+ * Author: Milo 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/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/lm3632.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#define LM3632_DEV_LCD_BIAS(_id)               \
+{                                              \
+       .name = "lm3632-regulator",             \
+       .id   = _id,                            \
+       .of_compatible = "ti,lm3632-regulator", \
+}
+
+#define LM3632_DEV_BL                          \
+{                                              \
+       .name = "lm3632-backlight",             \
+       .of_compatible = "ti,lm3632-backlight", \
+}
+
+static struct mfd_cell lm3632_devs[] = {
+       /* 5 Regulators */
+       LM3632_DEV_LCD_BIAS(1),
+       LM3632_DEV_LCD_BIAS(2),
+       LM3632_DEV_LCD_BIAS(3),
+       LM3632_DEV_LCD_BIAS(4),
+       LM3632_DEV_LCD_BIAS(5),
+
+       /* Backlight */
+       LM3632_DEV_BL,
+};
+
+int lm3632_read_byte(struct lm3632 *lm3632, u8 reg, u8 *read)
+{
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(lm3632->regmap, reg, &val);
+       if (ret < 0)
+               return ret;
+
+       *read = (u8)val;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(lm3632_read_byte);
+
+int lm3632_write_byte(struct lm3632 *lm3632, u8 reg, u8 data)
+{
+       return regmap_write(lm3632->regmap, reg, data);
+}
+EXPORT_SYMBOL_GPL(lm3632_write_byte);
+
+int lm3632_update_bits(struct lm3632 *lm3632, u8 reg, u8 mask, u8 data)
+{
+       return regmap_update_bits(lm3632->regmap, reg, mask, data);
+}
+EXPORT_SYMBOL_GPL(lm3632_update_bits);
+
+static int lm3632_init_device(struct lm3632 *lm3632)
+{
+       int ret;
+
+       /*
+        * Sequence
+        *
+        * 1) Enable nRST pin
+        * 2) Delay about 1ms (bias delay 200us + EPROM read time 700us)
+        * 3) Set LCD_EN bit to 1
+        */
+
+       ret = devm_gpio_request_one(lm3632->dev, lm3632->pdata->en_gpio,
+                                   GPIOF_OUT_INIT_HIGH, "lm3632_hwen");
+       if (ret)
+               return ret;
+
+       usleep_range(1000, 1500);
+
+       return lm3632_update_bits(lm3632, LM3632_REG_DEVCTRL,
+                                 LM3632_LCD_EN_MASK,
+                                 1 << LM3632_LCD_EN_SHIFT);
+}
+
+static void lm3632_deinit_device(struct lm3632 *lm3632)
+{
+       gpio_set_value(lm3632->pdata->en_gpio, 0);
+}
+
+static int lm3632_parse_dt(struct device *dev, struct lm3632 *lm3632)
+{
+       struct device_node *node = dev->of_node;
+       struct lm3632_platform_data *pdata;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       pdata->en_gpio = of_get_named_gpio(node, "ti,en-gpio", 0);
+       if (pdata->en_gpio == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
+       lm3632->pdata = pdata;
+
+       return 0;
+}
+
+static struct regmap_config lm3632_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = LM3632_MAX_REGISTERS,
+};
+
+static int lm3632_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+       struct lm3632 *lm3632;
+       struct device *dev = &cl->dev;
+       struct lm3632_platform_data *pdata = dev_get_platdata(dev);
+       int ret;
+
+       lm3632 = devm_kzalloc(dev, sizeof(*lm3632), GFP_KERNEL);
+       if (!lm3632)
+               return -ENOMEM;
+
+       lm3632->pdata = pdata;
+       if (!pdata) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = lm3632_parse_dt(dev, lm3632);
+               else
+                       ret = -ENODEV;
+
+               if (ret)
+                       return ret;
+       }
+
+       lm3632->regmap = devm_regmap_init_i2c(cl, &lm3632_regmap_config);
+       if (IS_ERR(lm3632->regmap))
+               return PTR_ERR(lm3632->regmap);
+
+       lm3632->dev = &cl->dev;
+       i2c_set_clientdata(cl, lm3632);
+
+       ret = lm3632_init_device(lm3632);
+       if (ret)
+               return ret;
+
+       return mfd_add_devices(dev, -1, lm3632_devs, ARRAY_SIZE(lm3632_devs),
+                              NULL, 0, NULL);
+}
+
+static int lm3632_remove(struct i2c_client *cl)
+{
+       struct lm3632 *lm3632 = i2c_get_clientdata(cl);
+
+       lm3632_deinit_device(lm3632);
+       mfd_remove_devices(lm3632->dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id lm3632_ids[] = {
+       { "lm3632", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lm3632_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3632_of_match[] = {
+       { .compatible = "ti,lm3632", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lm3632_of_match);
+#endif
+
+static struct i2c_driver lm3632_driver = {
+       .probe = lm3632_probe,
+       .remove = lm3632_remove,
+       .driver = {
+               .name = "lm3632",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(lm3632_of_match),
+       },
+       .id_table = lm3632_ids,
+};
+module_i2c_driver(lm3632_driver);
+
+MODULE_DESCRIPTION("TI LM3632 MFD Core");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
index df9c16fc0171b37b91716eb1269c957b078b026d..897b80f91c9663e8a28d41e4e783ca074cc1d0b9 100644 (file)
@@ -397,6 +397,12 @@ config REGULATOR_LM3631
        help
          This driver supports LM3631 voltage regulators for the LCD bias.
 
+config REGULATOR_LM3632
+       tristate "TI LM3632 voltage regulators"
+       depends on MFD_LM3632
+       help
+         This driver supports LM3632 voltage regulators for the LCD bias.
+
 config REGULATOR_PCF50633
        tristate "NXP PCF50633 regulator driver"
        depends on MFD_PCF50633
index b20070d9799267a228569d23b23247c4ff78322a..d555c6f8d1dbcc561d6d04e37829955a27a544d3 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
 obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
 obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
 obj-$(CONFIG_REGULATOR_LM3631) += lm3631-regulator.o
+obj-$(CONFIG_REGULATOR_LM3632) += lm3632-regulator.o
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
 obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
diff --git a/drivers/regulator/lm3632-regulator.c b/drivers/regulator/lm3632-regulator.c
new file mode 100644 (file)
index 0000000..36b821a
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * TI LM3632 LDO Regulator Driver
+ *
+ * Copyright 2015 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/err.h>
+#include <linux/mfd/lm3632.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+#define ENABLE_TIME_USEC               1000
+
+enum lm3632_regulator_id {
+       LM3632_REGULATOR_BOOST,
+       LM3632_LDO_CONT,
+       LM3632_LDO_OREF,
+       LM3632_LDO_POS,
+       LM3632_LDO_NEG,
+};
+
+struct lm3632_regulator {
+       struct lm3632 *lm3632;
+       struct regulator_desc *desc;
+       struct regulator_dev *regulator;
+       struct regulator_init_data *init_data;
+};
+
+static const int lm3632_boost_vtbl[] = {
+       4500000, 4550000, 4600000, 4650000, 4700000, 4750000, 4800000, 4850000,
+       4900000, 4950000, 5000000, 5050000, 5100000, 5150000, 5200000, 5250000,
+       5300000, 5350000, 5400000, 5450000, 5500000, 5550000, 5600000, 5650000,
+       5700000, 5750000, 5800000, 5850000, 5900000, 5950000, 6000000, 6050000,
+       6100000, 6150000, 6200000, 6250000, 6300000, 6350000,
+};
+
+static const int lm3632_ldo_cont_vtbl[] = {
+       1800000, 2300000, 2800000, 3300000,
+};
+
+static const int lm3632_ldo_target_vtbl[] = {
+       4000000, 4050000, 4100000, 4150000, 4200000, 4250000, 4300000, 4350000,
+       4400000, 4450000, 4500000, 4550000, 4600000, 4650000, 4700000, 4750000,
+       4800000, 4850000, 4900000, 4950000, 5000000, 5050000, 5100000, 5150000,
+       5200000, 5250000, 5300000, 5350000, 5400000, 5450000, 5500000, 5550000,
+       5600000, 5650000, 5700000, 5750000, 5800000, 5850000, 5900000, 5950000,
+       6000000,
+};
+
+const int ldo_cont_enable_time[] = {
+       0, 2000, 5000, 10000, 20000, 50000, 100000, 200000,
+};
+
+static int lm3632_regulator_enable_time(struct regulator_dev *rdev)
+{
+       struct lm3632_regulator *lm3632_regulator = rdev_get_drvdata(rdev);
+       enum lm3632_regulator_id id = rdev_get_id(rdev);
+       u8 val, addr, mask;
+
+       switch (id) {
+       case LM3632_LDO_CONT:
+               addr = LM3632_REG_ENTIME_VCONT;
+               mask = LM3632_ENTIME_CONT_MASK;
+               break;
+       case LM3632_LDO_OREF:
+               addr = LM3632_REG_ENTIME_VOREF;
+               mask = LM3632_ENTIME_MASK;
+               break;
+       case LM3632_LDO_POS:
+               addr = LM3632_REG_ENTIME_VPOS;
+               mask = LM3632_ENTIME_MASK;
+               break;
+       case LM3632_LDO_NEG:
+               addr = LM3632_REG_ENTIME_VNEG;
+               mask = LM3632_ENTIME_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (lm3632_read_byte(lm3632_regulator->lm3632, addr, &val))
+               return -EINVAL;
+
+       val = (val & mask) >> LM3632_ENTIME_SHIFT;
+
+       if (id == LM3632_LDO_CONT)
+               return ldo_cont_enable_time[val];
+       else
+               return ENABLE_TIME_USEC * val;
+}
+
+static struct regulator_ops lm3632_boost_voltage_table_ops = {
+       .list_voltage     = regulator_list_voltage_table,
+       .set_voltage_sel  = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel  = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops lm3632_regulator_voltage_table_ops = {
+       .list_voltage     = regulator_list_voltage_table,
+       .set_voltage_sel  = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel  = regulator_get_voltage_sel_regmap,
+       .enable           = regulator_enable_regmap,
+       .disable          = regulator_disable_regmap,
+       .is_enabled       = regulator_is_enabled_regmap,
+       .enable_time      = lm3632_regulator_enable_time,
+};
+
+static struct regulator_desc lm3632_regulator_desc[] = {
+       {
+               .name           = "vboost",
+               .id             = LM3632_REGULATOR_BOOST,
+               .ops            = &lm3632_boost_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3632_boost_vtbl),
+               .volt_table     = lm3632_boost_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3632_REG_VOUT_BOOST,
+               .vsel_mask      = LM3632_VOUT_MASK,
+       },
+       {
+               .name           = "ldo_cont",
+               .id             = LM3632_LDO_CONT,
+               .ops            = &lm3632_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3632_ldo_cont_vtbl),
+               .volt_table     = lm3632_ldo_cont_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3632_REG_VOUT_CONT,
+               .vsel_mask      = LM3632_VOUT_CONT_MASK,
+               .enable_reg     = LM3632_REG_LDO_CTRL2,
+               .enable_mask    = LM3632_EN_CONT_MASK,
+       },
+       {
+               .name           = "ldo_oref",
+               .id             = LM3632_LDO_OREF,
+               .ops            = &lm3632_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3632_ldo_target_vtbl),
+               .volt_table     = lm3632_ldo_target_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3632_REG_VOUT_OREF,
+               .vsel_mask      = LM3632_VOUT_MASK,
+               .enable_reg     = LM3632_REG_LDO_CTRL1,
+               .enable_mask    = LM3632_EN_OREF_MASK,
+       },
+       {
+               .name           = "ldo_vpos",
+               .id             = LM3632_LDO_POS,
+               .ops            = &lm3632_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3632_ldo_target_vtbl),
+               .volt_table     = lm3632_ldo_target_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3632_REG_VOUT_POS,
+               .vsel_mask      = LM3632_VOUT_MASK,
+               .enable_reg     = LM3632_REG_LDO_CTRL1,
+               .enable_mask    = LM3632_EN_VPOS_MASK,
+       },
+       {
+               .name           = "ldo_vneg",
+               .id             = LM3632_LDO_NEG,
+               .ops            = &lm3632_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3632_ldo_target_vtbl),
+               .volt_table     = lm3632_ldo_target_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3632_REG_VOUT_NEG,
+               .vsel_mask      = LM3632_VOUT_MASK,
+               .enable_reg     = LM3632_REG_LDO_CTRL1,
+               .enable_mask    = LM3632_EN_VNEG_MASK,
+       },
+};
+
+static struct of_regulator_match lm3632_regulator_matches[] = {
+       { .name = "vboost", .driver_data = (void *)LM3632_REGULATOR_BOOST, },
+       { .name = "vcont",  .driver_data = (void *)LM3632_LDO_CONT, },
+       { .name = "voref",  .driver_data = (void *)LM3632_LDO_OREF, },
+       { .name = "vpos",   .driver_data = (void *)LM3632_LDO_POS,  },
+       { .name = "vneg",   .driver_data = (void *)LM3632_LDO_NEG,  },
+};
+
+static int lm3632_regulator_parse_dt(struct device *dev,
+                                    struct lm3632_regulator *lm3632_regulator,
+                                    int id)
+{
+       struct device_node *node = dev->of_node;
+       int count;
+
+       count = of_regulator_match(dev, node, &lm3632_regulator_matches[id], 1);
+       if (count <= 0)
+               return -ENODEV;
+
+       lm3632_regulator->init_data = lm3632_regulator_matches[id].init_data;
+
+       return 0;
+}
+
+static int lm3632_regulator_probe(struct platform_device *pdev)
+{
+       struct lm3632 *lm3632 = dev_get_drvdata(pdev->dev.parent);
+       struct lm3632_regulator *lm3632_regulator;
+       struct regulator_config cfg = { };
+       struct regulator_dev *rdev;
+       int id = pdev->id;
+       int ret;
+
+       lm3632_regulator = devm_kzalloc(&pdev->dev, sizeof(*lm3632_regulator),
+                                       GFP_KERNEL);
+       if (!lm3632_regulator)
+               return -ENOMEM;
+
+       lm3632_regulator->lm3632 = lm3632;
+
+       lm3632_regulator->init_data = lm3632->pdata->regulator_data[id];
+       if (!lm3632_regulator->init_data) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = lm3632_regulator_parse_dt(&pdev->dev,
+                                                       lm3632_regulator, id);
+               else
+                       ret = -ENODEV;
+
+               if (ret)
+                       return ret;
+       }
+
+       cfg.dev = pdev->dev.parent;
+       cfg.init_data = lm3632_regulator->init_data;
+       cfg.driver_data = lm3632_regulator;
+       cfg.regmap = lm3632->regmap;
+
+       rdev = regulator_register(&lm3632_regulator_desc[id], &cfg);
+       if (IS_ERR(rdev)) {
+               ret = PTR_ERR(rdev);
+               dev_err(&pdev->dev, "[%d] regulator register err: %d\n",
+                       id + 1, ret);
+               return ret;
+       }
+
+       lm3632_regulator->regulator = rdev;
+       platform_set_drvdata(pdev, lm3632_regulator);
+
+       return 0;
+}
+
+static int lm3632_regulator_remove(struct platform_device *pdev)
+{
+       struct lm3632_regulator *lm3632_regulator = platform_get_drvdata(pdev);
+
+       regulator_unregister(lm3632_regulator->regulator);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3632_regulator_of_match[] = {
+       { .compatible = "ti,lm3632-regulator", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lm3632_regulator_of_match);
+#endif
+
+static struct platform_driver lm3632_regulator_driver = {
+       .probe = lm3632_regulator_probe,
+       .remove = lm3632_regulator_remove,
+       .driver = {
+               .name = "lm3632-regulator",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(lm3632_regulator_of_match),
+       },
+};
+
+module_platform_driver(lm3632_regulator_driver);
+
+MODULE_DESCRIPTION("TI LM3632 Regulator Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3632-regulator");
index bdba28adeab8b7595d8d9ca6e323f64d54d68793..3517722acede9fdcab6baa2aed5b528f644bb344 100644 (file)
@@ -388,6 +388,12 @@ config BACKLIGHT_LM3631
        help
          Say Y to enable the backlight driver for TI LM3631.
 
+config BACKLIGHT_LM3632
+       tristate "Backlight driver for TI LM3632"
+       depends on BACKLIGHT_CLASS_DEVICE && MFD_LM3632
+       help
+         Say Y to enable the backlight driver for TI LM3632.
+
 config BACKLIGHT_LM3639
        tristate "Backlight Driver for LM3639"
        depends on BACKLIGHT_CLASS_DEVICE && I2C
index d3d48e8eb3158a12e5568d191db13416be1cc5c2..d5965193c1e3541770c0f03eba755baecb7e7895 100644 (file)
@@ -40,6 +40,7 @@ obj-$(CONFIG_BACKLIGHT_LM3533)                += lm3533_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3585)         += lm3585_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3630A)                += lm3630a_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3631)         += lm3631_bl.o
+obj-$(CONFIG_BACKLIGHT_LM3632)         += lm3632_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3639)         += lm3639_bl.o
 obj-$(CONFIG_BACKLIGHT_LM36923)                += lm36923_bl.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO)         += locomolcd.o
diff --git a/drivers/video/backlight/lm3632_bl.c b/drivers/video/backlight/lm3632_bl.c
new file mode 100644 (file)
index 0000000..0459bf1
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * TI LM3632 Backlight Driver
+ *
+ * Copyright 2015 Texas Instruments
+ *
+ * Author: Milo 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/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/lm3632.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define LM3632_MAX_BRIGHTNESS          2047
+
+#define DEFAULT_BL_NAME                        "lcd-backlight"
+
+enum lm3632_bl_ctrl_mode {
+       LMU_BL_I2C,
+       LMU_BL_PWM,
+};
+
+struct lm3632_bl {
+       struct device *dev;
+       struct backlight_device *bl_dev;
+
+       struct lm3632 *lm3632;
+       struct lm3632_backlight_platform_data *pdata;
+       enum lm3632_bl_ctrl_mode mode;
+
+       struct pwm_device *pwm;
+};
+
+static int lm3632_bl_enable(struct lm3632_bl *lm3632_bl, int enable)
+{
+       return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_DEVCTRL,
+                                 LM3632_BL_EN_MASK,
+                                 enable << LM3632_BL_EN_SHIFT);
+}
+
+static void lm3632_bl_pwm_ctrl(struct lm3632_bl *lm3632_bl, int br, int max_br)
+{
+       unsigned int period;
+       unsigned int duty;
+       struct pwm_device *pwm;
+
+       if (!lm3632_bl->pdata)
+               return;
+
+       period = lm3632_bl->pdata->pwm_period;
+       duty = br * period / max_br;
+
+       /* Request a PWM device with the consumer name */
+       if (!lm3632_bl->pwm) {
+               pwm = devm_pwm_get(lm3632_bl->dev, "lm3632-backlight");
+               if (IS_ERR(pwm)) {
+                       dev_err(lm3632_bl->dev, "can not get PWM device\n");
+                       return;
+               }
+               lm3632_bl->pwm = pwm;
+       }
+
+       pwm_config(lm3632_bl->pwm, duty, period);
+       if (duty)
+               pwm_enable(lm3632_bl->pwm);
+       else
+               pwm_disable(lm3632_bl->pwm);
+}
+
+static inline int lm3632_bl_set_brightness(struct lm3632_bl *lm3632_bl, int val)
+{
+       u8 data;
+       int ret;
+
+       data = val & LM3632_BRT_LSB_MASK;
+       ret = lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_BRT_LSB,
+                                LM3632_BRT_LSB_MASK, data);
+       if (ret)
+               return ret;
+
+       data = (val >> LM3632_BRT_MSB_SHIFT) & 0xFF;
+       return lm3632_write_byte(lm3632_bl->lm3632, LM3632_REG_BRT_MSB,
+                                data);
+}
+
+static int lm3632_bl_update_status(struct backlight_device *bl_dev)
+{
+       struct lm3632_bl *lm3632_bl = bl_get_data(bl_dev);
+       int brt;
+       int ret;
+
+       if (bl_dev->props.state & BL_CORE_SUSPENDED)
+               bl_dev->props.brightness = 0;
+
+       brt = bl_dev->props.brightness;
+
+       if (brt > 0)
+               ret = lm3632_bl_enable(lm3632_bl, 1);
+       else
+               ret = lm3632_bl_enable(lm3632_bl, 0);
+
+       if (ret)
+               return ret;
+
+       if (lm3632_bl->mode == LMU_BL_PWM)
+               lm3632_bl_pwm_ctrl(lm3632_bl, brt,
+                                  bl_dev->props.max_brightness);
+       else
+               ret = lm3632_bl_set_brightness(lm3632_bl, brt);
+
+       return ret;
+}
+
+static int lm3632_bl_get_brightness(struct backlight_device *bl_dev)
+{
+       return bl_dev->props.brightness;
+}
+
+static const struct backlight_ops lm3632_bl_ops = {
+       .options = BL_CORE_SUSPENDRESUME,
+       .update_status = lm3632_bl_update_status,
+       .get_brightness = lm3632_bl_get_brightness,
+};
+
+static int lm3632_bl_register(struct lm3632_bl *lm3632_bl)
+{
+       struct backlight_device *bl_dev;
+       struct backlight_properties props;
+       struct lm3632_backlight_platform_data *pdata = lm3632_bl->pdata;
+       char name[20];
+
+       props.type = BACKLIGHT_PLATFORM;
+       props.brightness = pdata ? pdata->init_brightness : 0;
+       props.max_brightness = LM3632_MAX_BRIGHTNESS;
+
+       if (!pdata || !pdata->name)
+               snprintf(name, sizeof(name), "%s", DEFAULT_BL_NAME);
+       else
+               snprintf(name, sizeof(name), "%s", pdata->name);
+
+       bl_dev = backlight_device_register(name, lm3632_bl->dev, lm3632_bl,
+                                          &lm3632_bl_ops, &props);
+       if (IS_ERR(bl_dev))
+               return PTR_ERR(bl_dev);
+
+       lm3632_bl->bl_dev = bl_dev;
+
+       return 0;
+}
+
+static void lm3632_bl_unregister(struct lm3632_bl *lm3632_bl)
+{
+       if (lm3632_bl->bl_dev)
+               backlight_device_unregister(lm3632_bl->bl_dev);
+}
+
+static int lm3632_bl_set_ctrl_mode(struct lm3632_bl *lm3632_bl)
+{
+       struct lm3632_backlight_platform_data *pdata = lm3632_bl->pdata;
+
+       /* Brightness control mode is I2C only by default */
+       if (!pdata) {
+               lm3632_bl->mode = LMU_BL_I2C;
+               return lm3632_update_bits(lm3632_bl->lm3632,
+                                         LM3632_REG_BRT_MODE, LM3632_BRT_MASK,
+                                         LM3632_I2C_ONLY);
+       }
+
+       if (pdata->pwm_period > 0)
+               lm3632_bl->mode = LMU_BL_PWM;
+
+       return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_BRT_MODE,
+                                 LM3632_BRT_MASK, pdata->mode);
+}
+
+static int lm3632_bl_string_configure(struct lm3632_bl *lm3632_bl)
+{
+       u8 val;
+
+       if (lm3632_bl->pdata->is_full_strings)
+               val = LM3632_BL_TWO_STRINGS;
+       else
+               val = LM3632_BL_ONE_STRING;
+
+       return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_BL_CFG,
+                                 LM3632_BL_STRING_MASK, val);
+}
+
+static int lm3632_bl_configure(struct lm3632_bl *lm3632_bl)
+{
+       int ret;
+
+       ret = lm3632_bl_set_ctrl_mode(lm3632_bl);
+       if (ret)
+               return ret;
+
+       return lm3632_bl_string_configure(lm3632_bl);
+}
+
+static int lm3632_bl_parse_dt(struct device *dev, struct lm3632_bl *lm3632_bl)
+{
+       struct device_node *node = dev->of_node;
+       struct lm3632_backlight_platform_data *pdata;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       of_property_read_string(node, "bl-name", &pdata->name);
+
+       if (of_find_property(node, "full-strings-used", NULL))
+               pdata->is_full_strings = true;
+
+       if (of_find_property(node, "mode-pwm-only", NULL))
+               pdata->mode = LM3632_PWM_ONLY;
+       else if (of_find_property(node, "mode-comb1", NULL))
+               pdata->mode = LM3632_COMB1;
+       else if (of_find_property(node, "mode-comb2", NULL))
+               pdata->mode = LM3632_COMB2;
+
+       of_property_read_u8(node, "initial-brightness",
+                           (u8 *)&pdata->init_brightness);
+
+       of_property_read_u32(node, "pwm-period", &pdata->pwm_period);
+
+       lm3632_bl->pdata = pdata;
+
+       return 0;
+}
+
+static int lm3632_bl_probe(struct platform_device *pdev)
+{
+       struct lm3632 *lm3632 = dev_get_drvdata(pdev->dev.parent);
+       struct lm3632_backlight_platform_data *pdata = lm3632->pdata->bl_pdata;
+       struct lm3632_bl *lm3632_bl;
+       int ret;
+
+       lm3632_bl = devm_kzalloc(&pdev->dev, sizeof(*lm3632_bl), GFP_KERNEL);
+       if (!lm3632_bl)
+               return -ENOMEM;
+
+       lm3632_bl->pdata = pdata;
+       if (!lm3632_bl->pdata) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = lm3632_bl_parse_dt(&pdev->dev, lm3632_bl);
+               else
+                       return -ENODEV;
+
+               if (ret)
+                       return ret;
+       }
+
+       lm3632_bl->dev = &pdev->dev;
+       lm3632_bl->lm3632 = lm3632;
+       platform_set_drvdata(pdev, lm3632_bl);
+
+       ret = lm3632_bl_configure(lm3632_bl);
+       if (ret) {
+               dev_err(&pdev->dev, "backlight config err: %d\n", ret);
+               return ret;
+       }
+
+       ret = lm3632_bl_register(lm3632_bl);
+       if (ret) {
+               dev_err(&pdev->dev, "register backlight err: %d\n", ret);
+               return ret;
+       }
+
+       backlight_update_status(lm3632_bl->bl_dev);
+
+       return 0;
+}
+
+static int lm3632_bl_remove(struct platform_device *pdev)
+{
+       struct lm3632_bl *lm3632_bl = platform_get_drvdata(pdev);
+       struct backlight_device *bl_dev = lm3632_bl->bl_dev;
+
+       bl_dev->props.brightness = 0;
+       backlight_update_status(bl_dev);
+       lm3632_bl_unregister(lm3632_bl);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3632_bl_of_match[] = {
+       { .compatible = "ti,lm3632-backlight", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lm3632_bl_of_match);
+#endif
+
+static struct platform_driver lm3632_bl_driver = {
+       .probe = lm3632_bl_probe,
+       .remove = lm3632_bl_remove,
+       .driver = {
+               .name = "lm3632-backlight",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(lm3632_bl_of_match),
+       },
+};
+module_platform_driver(lm3632_bl_driver);
+
+MODULE_DESCRIPTION("TI LM3632 Backlight Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3632-backlight");
diff --git a/include/linux/mfd/lm3632.h b/include/linux/mfd/lm3632.h
new file mode 100644 (file)
index 0000000..8470148
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * TI LM3631 MFD Driver
+ *
+ * Copyright 2013 Texas Instruments
+ *
+ * Author: Milo 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.
+ *
+ */
+
+#ifndef __MFD_LM3631_H__
+#define __MFD_LM3631_H__
+
+#include <linux/gpio.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/machine.h>
+
+/* Registers */
+#define LM3631_REG_DEVCTRL             0x00
+#define LM3631_LCD_EN_MASK             BIT(1)
+#define LM3631_LCD_EN_SHIFT            1
+#define LM3631_BL_EN_MASK              BIT(0)
+#define LM3631_BL_EN_SHIFT             0
+
+#define LM3631_REG_BRT_LSB             0x01
+#define LM3631_BRT_LSB_MASK            (BIT(0) | BIT(1) | BIT(2))
+#define LM3631_REG_BRT_MSB             0x02
+#define LM3631_BRT_MSB_SHIFT           3
+
+#define LM3631_REG_BL_CFG              0x06
+#define LM3631_BL_STRING_MASK          BIT(3)
+#define LM3631_BL_TWO_STRINGS          0
+#define LM3631_BL_ONE_STRING           BIT(3)
+
+#define LM3631_REG_BRT_MODE            0x08
+#define LM3631_BRT_MASK                        (BIT(2) | BIT(3))
+
+#define LM3631_REG_LDO_CTRL1           0x0A
+#define LM3631_EN_OREF_MASK            BIT(0)
+#define LM3631_EN_VNEG_MASK            BIT(1)
+#define LM3631_EN_VPOS_MASK            BIT(2)
+
+#define LM3631_REG_LDO_CTRL2           0x0B
+#define LM3631_EN_CONT_MASK            BIT(0)
+
+#define LM3631_REG_VOUT_CONT           0x0C
+#define LM3631_VOUT_CONT_MASK          (BIT(6) | BIT(7))
+
+#define LM3631_REG_VOUT_BOOST          0x0C
+#define LM3631_REG_VOUT_POS            0x0D
+#define LM3631_REG_VOUT_NEG            0x0E
+#define LM3631_REG_VOUT_OREF           0x0F
+#define LM3631_VOUT_MASK               0x3F
+
+#define LM3631_REG_ENTIME_VCONT                0x0B
+#define LM3631_ENTIME_CONT_MASK                0x70
+
+#define LM3631_REG_ENTIME_VOREF                0x0F
+#define LM3631_REG_ENTIME_VPOS         0x10
+#define LM3631_REG_ENTIME_VNEG         0x11
+#define LM3631_ENTIME_MASK             0xF0
+#define LM3631_ENTIME_SHIFT            4
+
+#define LM3631_MAX_REGISTERS           0x16
+
+#define LM3631_NUM_REGULATORS          5
+
+enum lm3631_brightness_mode {
+       LM3631_I2C_ONLY = 0 << 2,
+       LM3631_PWM_ONLY = 1 << 2,
+       LM3631_COMB1    = 2 << 2,       /* I2C x PWM befoer sloping */
+       LM3631_COMB2    = 3 << 2,       /* Sloped I2C x PWM */
+};
+
+/*
+ * struct lm3633_bl_platform_data
+ * @name: Backlight driver name
+ * @is_full_strings: set true if two strings are used
+ * @init_brightness: Initial brightness value
+ * @mode: Backlight control mode
+ * @pwm_period: Platform specific PWM period value. unit is nano
+ */
+struct lm3631_backlight_platform_data {
+       const char *name;
+       bool is_full_strings;
+       u8 init_brightness;
+       enum lm3631_brightness_mode mode;
+
+       /* Only valid in case of PWM mode */
+       unsigned int pwm_period;
+};
+
+/*
+ * struct lmu_platform_data
+ * @en_gpio: GPIO for nRST pin
+ * @regulator_data: Regulator initial data for LCD bias
+ * @bl_pdata: Backlight platform data
+ */
+struct lm3631_platform_data {
+       int en_gpio;
+       struct regulator_init_data *regulator_data[LM3631_NUM_REGULATORS];
+       struct lm3631_backlight_platform_data *bl_pdata;
+};
+
+/*
+ * struct lm3631
+ * @dev: Parent device pointer
+ * @regmap: Used for i2c communcation on accessing registers
+ * @pdata: LMU platform specific data
+ */
+struct lm3631 {
+       struct device *dev;
+       struct regmap *regmap;
+       struct lm3631_platform_data *pdata;
+};
+
+int lm3631_read_byte(struct lm3631 *lm3631, u8 reg, u8 *read);
+int lm3631_write_byte(struct lm3631 *lm3631, u8 reg, u8 data);
+int lm3631_update_bits(struct lm3631 *lm3631, u8 reg, u8 mask, u8 data);
+#endif