mfd : add new mfd device LM3631-backlight and regulator
authorDaniel Jeong <gshark.jeong@gmail.com>
Thu, 12 Feb 2015 11:29:40 +0000 (20:29 +0900)
committerDaniel Jeong <gshark.jeong@gmail.com>
Thu, 12 Feb 2015 11:29:40 +0000 (20:29 +0900)
Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com>
15 files changed:
Documentation/devicetree/bindings/mfd/lm3631.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/lm3631-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/backlight/lm3631_bl.txt [new file with mode: 0644]
Examples/board-plat-lm3631.c [new file with mode: 0644]
Examples/lm-lcd-driver.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/lm3631.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/lm3631-regulator.c [new file with mode: 0644]
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/lm3631_bl.c [new file with mode: 0644]
include/linux/mfd/lm3631.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/mfd/lm3631.txt b/Documentation/devicetree/bindings/mfd/lm3631.txt
new file mode 100644 (file)
index 0000000..5247f8c
--- /dev/null
@@ -0,0 +1,59 @@
+TI LM3631 MFD Driver
+
+Required properties:
+  - compatible: "ti,lm3631"
+  - reg: I2C slave address. 0x29.
+  - ti,en-gpio: GPIO number of LM3631 nRST pin.
+
+LM3631 consists of two sub-devices, lm3631-regulator and lm3631-bl.
+
+For the LM3631 regulator properties please refer to:
+Documentation/devicetree/bindings/regulator/lm3631-regulator.txt
+
+For the LM3631 backlight properties please refer to:
+Documentation/devicetree/bindings/video/backlight/lm3631_bl.txt
+
+Example:
+
+lm3631@29 {
+       compatible = "ti,lm3631";
+       reg = <0x29>;
+
+       /* GPIO134 for HWEN pin */
+       ti,en-gpio = <&gpio5 6 0>;
+
+       /* Only Vpos and Vneg are used with LCD boost */
+       regulators {
+               compatible = "ti,lm3631-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,lm3631-backlight";
+
+               bl-name = "lcd";
+               full-strings-used;
+               mode-comb1;
+       };
+};
diff --git a/Documentation/devicetree/bindings/regulator/lm3631-regulator.txt b/Documentation/devicetree/bindings/regulator/lm3631-regulator.txt
new file mode 100644 (file)
index 0000000..f547632
--- /dev/null
@@ -0,0 +1,57 @@
+TI LM3631 Regulator Driver
+
+Required properties:
+  - compatible: "ti,lm3631-regulator"
+  - Regulator init data from of-regulator structure.
+    Please refer to regulator.txt in this directory.
+
+Example:
+
+&i2c4 {
+       clock-frequency = <400000>;
+
+       lm3631@29 {
+               compatible = "ti,lm3631";
+               reg = <0x29>;
+
+               /* GPIO134 for HWEN pin */
+               ti,en-gpio = <&gpio5 6 0>;
+
+               regulators {
+                       compatible = "ti,lm3631-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/lm3631_bl.txt b/Documentation/devicetree/bindings/video/backlight/lm3631_bl.txt
new file mode 100644 (file)
index 0000000..0e5b22e
--- /dev/null
@@ -0,0 +1,65 @@
+TI LM3631 Backlight Driver
+
+Required properties:
+  - compatible: "ti,lm3631-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>;
+
+       lm3631@29 {
+               compatible = "ti,lm3631";
+               reg = <0x29>;
+
+               /* GPIO134 for HWEN pin */
+               ti,en-gpio = <&gpio5 6 0>;
+
+               backlight {
+                       compatible = "ti,lm3631-backlight";
+                       bl-name = "lcd";
+                       full-strings-used;
+               };
+};
+
+Example 2:
+LM3631 brightness is controlled by PWM3943 controller.
+PWM3943 is a PWM controller. PWM#1 is port number of PMW3943.
+
+&i2c4 {
+       clock-frequency = <400000>;
+
+       lm3631@29 {
+               compatible = "ti,lm3631";
+               reg = <0x29>;
+
+               /* GPIO134 for HWEN pin */
+               ti,en-gpio = <&gpio5 6 0>;
+
+               backlight {
+                       compatible = "ti,lm3631-backlight";
+                       bl-name = "lcd";
+                       full-strings-used;
+                       mode-pwm-only;
+
+                       pwm-period = <10000>;
+
+                       pwms = <&pwm3943 1 10000>;
+                       pwm-names = "lm3631-backlight";
+               };
+};
diff --git a/Examples/board-plat-lm3631.c b/Examples/board-plat-lm3631.c
new file mode 100644 (file)
index 0000000..5787c56
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *     Platform Specific LM3631 Example
+ *
+ *                     Copyright (C) 2013 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.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/mfd/lm3631.h>
+#include <linux/pwm.h>
+
+#include "mux.h"
+#include "board-plat-lm3631.h"
+
+static struct regulator_consumer_supply lcd_vboost[] = {
+       REGULATOR_SUPPLY("lcd_boost", NULL),
+};
+
+static struct regulator_consumer_supply lcd_vio[] = {
+       REGULATOR_SUPPLY("lcd_io", NULL),
+};
+
+static struct regulator_consumer_supply lcd_vpos[] = {
+       REGULATOR_SUPPLY("lcd_vpos", NULL),
+};
+
+static struct regulator_consumer_supply lcd_vneg[] = {
+       REGULATOR_SUPPLY("lcd_vneg", NULL),
+};
+
+static struct regulator_consumer_supply lcd_vgamma[] = {
+       REGULATOR_SUPPLY("lcd_gamma", NULL),
+};
+
+struct regulator_init_data lcd_boost = {
+       .constraints = {
+               .name = "LCD_BOOST",
+               .min_uV = 4500000,
+               .max_uV = 6350000,
+               .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+               .always_on = 1,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(lcd_vboost),
+       .consumer_supplies      = lcd_vboost,
+};
+
+struct regulator_init_data lcd_cont = {
+       .constraints = {
+               .name = "LCD_CONT",
+               .min_uV = 1800000,
+               .max_uV = 3300000,
+               .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(lcd_vio),
+       .consumer_supplies      = lcd_vio,
+};
+
+struct regulator_init_data lcd_oref = {
+       .constraints = {
+               .name = "LCD_OREF",
+               .min_uV = 4000000,
+               .max_uV = 6000000,
+               .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(lcd_vgamma),
+       .consumer_supplies      = lcd_vgamma,
+};
+
+struct regulator_init_data lcd_pos = {
+       .constraints = {
+               .name = "LCD_VPOS",
+               .min_uV = 4000000,
+               .max_uV = 6000000,
+               .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS,
+               .boot_on = 1,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(lcd_vpos),
+       .consumer_supplies      = lcd_vpos,
+};
+
+struct regulator_init_data lcd_neg = {
+       .constraints = {
+               .name = "LCD_VNEG",
+               .min_uV = 4000000,
+               .max_uV = 6000000,
+               .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS,
+               .boot_on = 1,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(lcd_vneg),
+       .consumer_supplies      = lcd_vneg,
+};
+
+struct lm3631_backlight_platform_data lm3631_bl_pdata = {
+       .name = "lcd-bl",
+       .is_full_strings = true,
+       .mode = LM3631_COMB1,
+};
+
+#define LM3631_EN_GPIO         134
+static struct lm3631_platform_data lm3631_pdata = {
+       .en_gpio = LM3631_EN_GPIO,
+       .regulator_data = {
+               &lcd_boost,
+               &lcd_cont,
+               &lcd_oref,
+               &lcd_pos,
+               &lcd_neg,
+       }, 
+       .bl_pdata = &lm3631_bl_pdata,
+};
+
+static struct i2c_board_info __initdata led_i2c_boardinfo[] = {
+       {
+               I2C_BOARD_INFO("lm3631", 0x29),
+               .platform_data = &lm3631_pdata,
+       },
+};
+
+int plat_lm3631_init(void)
+{
+       i2c_register_board_info(4, led_i2c_boardinfo,
+                               ARRAY_SIZE(led_i2c_boardinfo));
+       return 0;
+}
diff --git a/Examples/lm-lcd-driver.c b/Examples/lm-lcd-driver.c
new file mode 100644 (file)
index 0000000..a6705b9
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * LCD Driver Example
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define LCD_BOOST_VOUT         5800000
+#define LCD_BIAS_VOUT          5500000
+
+enum lcd_ldo_id {
+       VBOOST,
+       VPOS,
+       VNEG,
+       NUM_REGULATORS,
+};
+
+const char *supplies[] = { "lcd_boost", "lcd_vpos", "lcd_vneg", };
+
+struct lcd_ctrl {
+       struct regulator *r[NUM_REGULATORS];
+       struct device *dev;
+};
+
+static int lm_lcd_enable(struct lcd_ctrl *ctl, enum lcd_ldo_id id, int enable)
+{
+       if (!ctl->r[id]) {
+               dev_info(ctl->dev, "regulator is not ready\n");
+               return 0;
+       }
+
+       if (enable)
+               return regulator_enable(ctl->r[id]);
+       else
+               return regulator_disable(ctl->r[id]); 
+}
+
+static int lm_lcd_set_voltage(struct lcd_ctrl *ctl, enum lcd_ldo_id id, int uV)
+{
+       return regulator_set_voltage(ctl->r[id], uV, uV + 10000);
+}
+
+static int lm_lcd_probe(struct platform_device *pdev)
+{
+       struct lcd_ctrl *ctl;
+       struct device *dev = &pdev->dev;
+       struct regulator *r;
+       int i;
+
+       ctl = devm_kzalloc(dev, sizeof(*ctl), GFP_KERNEL);
+       if (!ctl)
+               return -ENOMEM;
+
+       ctl->dev = dev;
+
+       for (i = 0; i < NUM_REGULATORS; i++) {
+               r = devm_regulator_get(dev, supplies[i]);
+               if (IS_ERR(r))
+                       continue;
+
+               ctl->r[i] = r;
+       }
+
+       platform_set_drvdata(pdev, ctl);
+
+       lm_lcd_set_voltage(ctl, VBOOST, LCD_BOOST_VOUT);
+       lm_lcd_set_voltage(ctl, VPOS, LCD_BIAS_VOUT);
+       lm_lcd_set_voltage(ctl, VNEG, LCD_BIAS_VOUT);
+
+       lm_lcd_enable(ctl, VPOS, 1);
+       lm_lcd_enable(ctl, VNEG, 1);
+
+       return 0;
+}
+
+static int lm_lcd_remove(struct platform_device *pdev)
+{
+       struct lcd_ctrl *ctl = platform_get_drvdata(pdev);
+
+       lm_lcd_enable(ctl, VPOS, 0);
+       lm_lcd_enable(ctl, VNEG, 0);
+
+       return 0;
+}
+
+static struct platform_driver lcd_ctrl_driver = {
+       .probe = lm_lcd_probe,
+       .remove = lm_lcd_remove,
+       .driver = {
+               .name = "lm-lcd-driver",
+               .owner = THIS_MODULE,
+       },
+};
+module_platform_driver(lcd_ctrl_driver);
+
+MODULE_DESCRIPTION("LCD Controller Driver Example");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm-lcd-driver");
index 49bb445d846aa76e206dfe7fb7d30ad5f24b6b97..28e29fcb7e49b031ba0fa8c85135d71b27674e06 100644 (file)
@@ -49,6 +49,15 @@ config PMIC_ADP5520
          individual components like LCD backlight, LEDs, GPIOs and Kepad
          under the corresponding menus.
 
+config MFD_LM3631
+       tristate "TI LM3631 Backlight and Bias Power Driver"
+       depends on I2C
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+         Say yes here to enable support for TI LM3631 chip.
+         LM3631 has 2 strings for backlight with 5 regulators for LCD bias.
+
 config MFD_AAT2870_CORE
        bool "AnalogicTech AAT2870"
        select MFD_CORE
index 5aea5ef0a62f51eff03a14404569c68a7700262f..109247099be48de1dde321f27d25fbd1605e9bad 100644 (file)
@@ -103,6 +103,7 @@ obj-$(CONFIG_MFD_DA9052_SPI)        += da9052-spi.o
 obj-$(CONFIG_MFD_DA9052_I2C)   += da9052-i2c.o
 
 obj-$(CONFIG_MFD_LP3943)       += lp3943.o
+obj-$(CONFIG_MFD_LM3631)       += lm3631.o
 obj-$(CONFIG_MFD_LP8788)       += lp8788.o lp8788-irq.o
 
 da9055-objs                    := da9055-core.o da9055-i2c.o
diff --git a/drivers/mfd/lm3631.c b/drivers/mfd/lm3631.c
new file mode 100644 (file)
index 0000000..d914122
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/lm3631.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#define LM3631_DEV_LCD_BIAS(_id)               \
+{                                              \
+       .name = "lm3631-regulator",             \
+       .id   = _id,                            \
+       .of_compatible = "ti,lm3631-regulator", \
+}
+
+#define LM3631_DEV_BL                          \
+{                                              \
+       .name = "lm3631-backlight",             \
+       .of_compatible = "ti,lm3631-backlight", \
+}
+
+static struct mfd_cell lm3631_devs[] = {
+       /* 5 Regulators */
+       LM3631_DEV_LCD_BIAS(1),
+       LM3631_DEV_LCD_BIAS(2),
+       LM3631_DEV_LCD_BIAS(3),
+       LM3631_DEV_LCD_BIAS(4),
+       LM3631_DEV_LCD_BIAS(5),
+
+       /* Backlight */
+       LM3631_DEV_BL,
+};
+
+int lm3631_read_byte(struct lm3631 *lm3631, u8 reg, u8 *read)
+{
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(lm3631->regmap, reg, &val);
+       if (ret < 0)
+               return ret;
+
+       *read = (u8)val;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(lm3631_read_byte);
+
+int lm3631_write_byte(struct lm3631 *lm3631, u8 reg, u8 data)
+{
+       return regmap_write(lm3631->regmap, reg, data);
+}
+EXPORT_SYMBOL_GPL(lm3631_write_byte);
+
+int lm3631_update_bits(struct lm3631 *lm3631, u8 reg, u8 mask, u8 data)
+{
+       return regmap_update_bits(lm3631->regmap, reg, mask, data);
+}
+EXPORT_SYMBOL_GPL(lm3631_update_bits);
+
+static int lm3631_init_device(struct lm3631 *lm3631)
+{
+       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(lm3631->dev, lm3631->pdata->en_gpio,
+                                   GPIOF_OUT_INIT_HIGH, "lm3631_hwen");
+       if (ret)
+               return ret;
+
+       usleep_range(1000, 1500);
+
+       return lm3631_update_bits(lm3631, LM3631_REG_DEVCTRL,
+                                 LM3631_LCD_EN_MASK,
+                                 1 << LM3631_LCD_EN_SHIFT);
+}
+
+static void lm3631_deinit_device(struct lm3631 *lm3631)
+{
+       gpio_set_value(lm3631->pdata->en_gpio, 0);
+}
+
+static int lm3631_parse_dt(struct device *dev, struct lm3631 *lm3631)
+{
+       struct device_node *node = dev->of_node;
+       struct lm3631_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;
+
+       lm3631->pdata = pdata;
+
+       return 0;
+}
+
+static struct regmap_config lm3631_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = LM3631_MAX_REGISTERS,
+};
+
+static int lm3631_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+       struct lm3631 *lm3631;
+       struct device *dev = &cl->dev;
+       struct lm3631_platform_data *pdata = dev_get_platdata(dev);
+       int ret;
+
+       lm3631 = devm_kzalloc(dev, sizeof(*lm3631), GFP_KERNEL);
+       if (!lm3631)
+               return -ENOMEM;
+
+       lm3631->pdata = pdata;
+       if (!pdata) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = lm3631_parse_dt(dev, lm3631);
+               else
+                       ret = -ENODEV;
+
+               if (ret)
+                       return ret;
+       }
+
+       lm3631->regmap = devm_regmap_init_i2c(cl, &lm3631_regmap_config);
+       if (IS_ERR(lm3631->regmap))
+               return PTR_ERR(lm3631->regmap);
+
+       lm3631->dev = &cl->dev;
+       i2c_set_clientdata(cl, lm3631);
+
+       ret = lm3631_init_device(lm3631);
+       if (ret)
+               return ret;
+
+       return mfd_add_devices(dev, -1, lm3631_devs, ARRAY_SIZE(lm3631_devs),
+                              NULL, 0, NULL);
+}
+
+static int lm3631_remove(struct i2c_client *cl)
+{
+       struct lm3631 *lm3631 = i2c_get_clientdata(cl);
+
+       lm3631_deinit_device(lm3631);
+       mfd_remove_devices(lm3631->dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id lm3631_ids[] = {
+       { "lm3631", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lm3631_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3631_of_match[] = {
+       { .compatible = "ti,lm3631", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lm3631_of_match);
+#endif
+
+static struct i2c_driver lm3631_driver = {
+       .probe = lm3631_probe,
+       .remove = lm3631_remove,
+       .driver = {
+               .name = "lm3631",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(lm3631_of_match),
+       },
+       .id_table = lm3631_ids,
+};
+module_i2c_driver(lm3631_driver);
+
+MODULE_DESCRIPTION("TI LM3631 MFD Core");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
index 6a7932822e373317caba9c5c799c9439f5444927..df9c16fc0171b37b91716eb1269c957b078b026d 100644 (file)
@@ -391,6 +391,12 @@ config REGULATOR_PCAP
         This driver provides support for the voltage regulators of the
         PCAP2 PMIC.
 
+config REGULATOR_LM3631
+       tristate "TI LM3631 voltage regulators"
+       depends on MFD_LM3631
+       help
+         This driver supports LM3631 voltage regulators for the LCD bias.
+
 config REGULATOR_PCF50633
        tristate "NXP PCF50633 regulator driver"
        depends on MFD_PCF50633
index 979f9ddcf259bd5a82b6b5d91f81981902e6185c..b20070d9799267a228569d23b23247c4ff78322a 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
 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_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
 obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
diff --git a/drivers/regulator/lm3631-regulator.c b/drivers/regulator/lm3631-regulator.c
new file mode 100644 (file)
index 0000000..3b68d19
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * TI LM3631 LDO Regulator Driver
+ *
+ * Copyright 2013 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/lm3631.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 lm3631_regulator_id {
+       LM3631_REGULATOR_BOOST,
+       LM3631_LDO_CONT,
+       LM3631_LDO_OREF,
+       LM3631_LDO_POS,
+       LM3631_LDO_NEG,
+};
+
+struct lm3631_regulator {
+       struct lm3631 *lm3631;
+       struct regulator_desc *desc;
+       struct regulator_dev *regulator;
+       struct regulator_init_data *init_data;
+};
+
+static const int lm3631_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 lm3631_ldo_cont_vtbl[] = {
+       1800000, 2300000, 2800000, 3300000,
+};
+
+static const int lm3631_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 lm3631_regulator_enable_time(struct regulator_dev *rdev)
+{
+       struct lm3631_regulator *lm3631_regulator = rdev_get_drvdata(rdev);
+       enum lm3631_regulator_id id = rdev_get_id(rdev);
+       u8 val, addr, mask;
+
+       switch (id) {
+       case LM3631_LDO_CONT:
+               addr = LM3631_REG_ENTIME_VCONT;
+               mask = LM3631_ENTIME_CONT_MASK;
+               break;
+       case LM3631_LDO_OREF:
+               addr = LM3631_REG_ENTIME_VOREF;
+               mask = LM3631_ENTIME_MASK;
+               break;
+       case LM3631_LDO_POS:
+               addr = LM3631_REG_ENTIME_VPOS;
+               mask = LM3631_ENTIME_MASK;
+               break;
+       case LM3631_LDO_NEG:
+               addr = LM3631_REG_ENTIME_VNEG;
+               mask = LM3631_ENTIME_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (lm3631_read_byte(lm3631_regulator->lm3631, addr, &val))
+               return -EINVAL;
+
+       val = (val & mask) >> LM3631_ENTIME_SHIFT;
+
+       if (id == LM3631_LDO_CONT)
+               return ldo_cont_enable_time[val];
+       else
+               return ENABLE_TIME_USEC * val;
+}
+
+static struct regulator_ops lm3631_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 lm3631_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      = lm3631_regulator_enable_time,
+};
+
+static struct regulator_desc lm3631_regulator_desc[] = {
+       {
+               .name           = "vboost",
+               .id             = LM3631_REGULATOR_BOOST,
+               .ops            = &lm3631_boost_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3631_boost_vtbl),
+               .volt_table     = lm3631_boost_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3631_REG_VOUT_BOOST,
+               .vsel_mask      = LM3631_VOUT_MASK,
+       },
+       {
+               .name           = "ldo_cont",
+               .id             = LM3631_LDO_CONT,
+               .ops            = &lm3631_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3631_ldo_cont_vtbl),
+               .volt_table     = lm3631_ldo_cont_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3631_REG_VOUT_CONT,
+               .vsel_mask      = LM3631_VOUT_CONT_MASK,
+               .enable_reg     = LM3631_REG_LDO_CTRL2,
+               .enable_mask    = LM3631_EN_CONT_MASK,
+       },
+       {
+               .name           = "ldo_oref",
+               .id             = LM3631_LDO_OREF,
+               .ops            = &lm3631_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3631_ldo_target_vtbl),
+               .volt_table     = lm3631_ldo_target_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3631_REG_VOUT_OREF,
+               .vsel_mask      = LM3631_VOUT_MASK,
+               .enable_reg     = LM3631_REG_LDO_CTRL1,
+               .enable_mask    = LM3631_EN_OREF_MASK,
+       },
+       {
+               .name           = "ldo_vpos",
+               .id             = LM3631_LDO_POS,
+               .ops            = &lm3631_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3631_ldo_target_vtbl),
+               .volt_table     = lm3631_ldo_target_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3631_REG_VOUT_POS,
+               .vsel_mask      = LM3631_VOUT_MASK,
+               .enable_reg     = LM3631_REG_LDO_CTRL1,
+               .enable_mask    = LM3631_EN_VPOS_MASK,
+       },
+       {
+               .name           = "ldo_vneg",
+               .id             = LM3631_LDO_NEG,
+               .ops            = &lm3631_regulator_voltage_table_ops,
+               .n_voltages     = ARRAY_SIZE(lm3631_ldo_target_vtbl),
+               .volt_table     = lm3631_ldo_target_vtbl,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .vsel_reg       = LM3631_REG_VOUT_NEG,
+               .vsel_mask      = LM3631_VOUT_MASK,
+               .enable_reg     = LM3631_REG_LDO_CTRL1,
+               .enable_mask    = LM3631_EN_VNEG_MASK,
+       },
+};
+
+static struct of_regulator_match lm3631_regulator_matches[] = {
+       { .name = "vboost", .driver_data = (void *)LM3631_REGULATOR_BOOST, },
+       { .name = "vcont",  .driver_data = (void *)LM3631_LDO_CONT, },
+       { .name = "voref",  .driver_data = (void *)LM3631_LDO_OREF, },
+       { .name = "vpos",   .driver_data = (void *)LM3631_LDO_POS,  },
+       { .name = "vneg",   .driver_data = (void *)LM3631_LDO_NEG,  },
+};
+
+static int lm3631_regulator_parse_dt(struct device *dev,
+                                    struct lm3631_regulator *lm3631_regulator,
+                                    int id)
+{
+       struct device_node *node = dev->of_node;
+       int count;
+
+       count = of_regulator_match(dev, node, &lm3631_regulator_matches[id], 1);
+       if (count <= 0)
+               return -ENODEV;
+
+       lm3631_regulator->init_data = lm3631_regulator_matches[id].init_data;
+
+       return 0;
+}
+
+static int lm3631_regulator_probe(struct platform_device *pdev)
+{
+       struct lm3631 *lm3631 = dev_get_drvdata(pdev->dev.parent);
+       struct lm3631_regulator *lm3631_regulator;
+       struct regulator_config cfg = { };
+       struct regulator_dev *rdev;
+       int id = pdev->id;
+       int ret;
+
+       lm3631_regulator = devm_kzalloc(&pdev->dev, sizeof(*lm3631_regulator),
+                                       GFP_KERNEL);
+       if (!lm3631_regulator)
+               return -ENOMEM;
+
+       lm3631_regulator->lm3631 = lm3631;
+
+       lm3631_regulator->init_data = lm3631->pdata->regulator_data[id];
+       if (!lm3631_regulator->init_data) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = lm3631_regulator_parse_dt(&pdev->dev,
+                                                       lm3631_regulator, id);
+               else
+                       ret = -ENODEV;
+
+               if (ret)
+                       return ret;
+       }
+
+       cfg.dev = pdev->dev.parent;
+       cfg.init_data = lm3631_regulator->init_data;
+       cfg.driver_data = lm3631_regulator;
+       cfg.regmap = lm3631->regmap;
+
+       rdev = regulator_register(&lm3631_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;
+       }
+
+       lm3631_regulator->regulator = rdev;
+       platform_set_drvdata(pdev, lm3631_regulator);
+
+       return 0;
+}
+
+static int lm3631_regulator_remove(struct platform_device *pdev)
+{
+       struct lm3631_regulator *lm3631_regulator = platform_get_drvdata(pdev);
+
+       regulator_unregister(lm3631_regulator->regulator);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3631_regulator_of_match[] = {
+       { .compatible = "ti,lm3631-regulator", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lm3631_regulator_of_match);
+#endif
+
+static struct platform_driver lm3631_regulator_driver = {
+       .probe = lm3631_regulator_probe,
+       .remove = lm3631_regulator_remove,
+       .driver = {
+               .name = "lm3631-regulator",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(lm3631_regulator_of_match),
+       },
+};
+
+module_platform_driver(lm3631_regulator_driver);
+
+MODULE_DESCRIPTION("TI LM3631 Regulator Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3631-regulator");
index 4341377fb2614f6cac62d909c16a56cc25d5aab1..bdba28adeab8b7595d8d9ca6e323f64d54d68793 100644 (file)
@@ -382,6 +382,12 @@ config BACKLIGHT_LM3630A
        help
          This supports TI LM3630A Backlight Driver
 
+config BACKLIGHT_LM3631
+       tristate "Backlight driver for TI LM3631"
+       depends on BACKLIGHT_CLASS_DEVICE && MFD_LM3631
+       help
+         Say Y to enable the backlight driver for TI LM3631.
+
 config BACKLIGHT_LM3639
        tristate "Backlight Driver for LM3639"
        depends on BACKLIGHT_CLASS_DEVICE && I2C
index ffd0717bf30dd99ac3266afec30488be444c2c74..d3d48e8eb3158a12e5568d191db13416be1cc5c2 100644 (file)
@@ -39,6 +39,7 @@ 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_LM3631)         += lm3631_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/lm3631_bl.c b/drivers/video/backlight/lm3631_bl.c
new file mode 100644 (file)
index 0000000..ba875e9
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * TI LM3631 Backlight 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.
+ *
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/lm3631.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define LM3631_MAX_BRIGHTNESS          2047
+
+#define DEFAULT_BL_NAME                        "lcd-backlight"
+
+enum lm3631_bl_ctrl_mode {
+       LMU_BL_I2C,
+       LMU_BL_PWM,
+};
+
+struct lm3631_bl {
+       struct device *dev;
+       struct backlight_device *bl_dev;
+
+       struct lm3631 *lm3631;
+       struct lm3631_backlight_platform_data *pdata;
+       enum lm3631_bl_ctrl_mode mode;
+
+       struct pwm_device *pwm;
+};
+
+static int lm3631_bl_enable(struct lm3631_bl *lm3631_bl, int enable)
+{
+       return lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_DEVCTRL,
+                                 LM3631_BL_EN_MASK,
+                                 enable << LM3631_BL_EN_SHIFT);
+}
+
+static void lm3631_bl_pwm_ctrl(struct lm3631_bl *lm3631_bl, int br, int max_br)
+{
+       unsigned int period;
+       unsigned int duty;
+       struct pwm_device *pwm;
+
+       if (!lm3631_bl->pdata)
+               return;
+
+       period = lm3631_bl->pdata->pwm_period;
+       duty = br * period / max_br;
+
+       /* Request a PWM device with the consumer name */
+       if (!lm3631_bl->pwm) {
+               pwm = devm_pwm_get(lm3631_bl->dev, "lm3631-backlight");
+               if (IS_ERR(pwm)) {
+                       dev_err(lm3631_bl->dev, "can not get PWM device\n");
+                       return;
+               }
+               lm3631_bl->pwm = pwm;
+       }
+
+       pwm_config(lm3631_bl->pwm, duty, period);
+       if (duty)
+               pwm_enable(lm3631_bl->pwm);
+       else
+               pwm_disable(lm3631_bl->pwm);
+}
+
+static inline int lm3631_bl_set_brightness(struct lm3631_bl *lm3631_bl, int val)
+{
+       u8 data;
+       int ret;
+
+       data = val & LM3631_BRT_LSB_MASK;
+       ret = lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_BRT_LSB,
+                                LM3631_BRT_LSB_MASK, data);
+       if (ret)
+               return ret;
+
+       data = (val >> LM3631_BRT_MSB_SHIFT) & 0xFF;
+       return lm3631_write_byte(lm3631_bl->lm3631, LM3631_REG_BRT_MSB,
+                                data);
+}
+
+static int lm3631_bl_update_status(struct backlight_device *bl_dev)
+{
+       struct lm3631_bl *lm3631_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 = lm3631_bl_enable(lm3631_bl, 1);
+       else
+               ret = lm3631_bl_enable(lm3631_bl, 0);
+
+       if (ret)
+               return ret;
+
+       if (lm3631_bl->mode == LMU_BL_PWM)
+               lm3631_bl_pwm_ctrl(lm3631_bl, brt,
+                                  bl_dev->props.max_brightness);
+       else
+               ret = lm3631_bl_set_brightness(lm3631_bl, brt);
+
+       return ret;
+}
+
+static int lm3631_bl_get_brightness(struct backlight_device *bl_dev)
+{
+       return bl_dev->props.brightness;
+}
+
+static const struct backlight_ops lm3631_bl_ops = {
+       .options = BL_CORE_SUSPENDRESUME,
+       .update_status = lm3631_bl_update_status,
+       .get_brightness = lm3631_bl_get_brightness,
+};
+
+static int lm3631_bl_register(struct lm3631_bl *lm3631_bl)
+{
+       struct backlight_device *bl_dev;
+       struct backlight_properties props;
+       struct lm3631_backlight_platform_data *pdata = lm3631_bl->pdata;
+       char name[20];
+
+       props.type = BACKLIGHT_PLATFORM;
+       props.brightness = pdata ? pdata->init_brightness : 0;
+       props.max_brightness = LM3631_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, lm3631_bl->dev, lm3631_bl,
+                                          &lm3631_bl_ops, &props);
+       if (IS_ERR(bl_dev))
+               return PTR_ERR(bl_dev);
+
+       lm3631_bl->bl_dev = bl_dev;
+
+       return 0;
+}
+
+static void lm3631_bl_unregister(struct lm3631_bl *lm3631_bl)
+{
+       if (lm3631_bl->bl_dev)
+               backlight_device_unregister(lm3631_bl->bl_dev);
+}
+
+static int lm3631_bl_set_ctrl_mode(struct lm3631_bl *lm3631_bl)
+{
+       struct lm3631_backlight_platform_data *pdata = lm3631_bl->pdata;
+
+       /* Brightness control mode is I2C only by default */
+       if (!pdata) {
+               lm3631_bl->mode = LMU_BL_I2C;
+               return lm3631_update_bits(lm3631_bl->lm3631,
+                                         LM3631_REG_BRT_MODE, LM3631_BRT_MASK,
+                                         LM3631_I2C_ONLY);
+       }
+
+       if (pdata->pwm_period > 0)
+               lm3631_bl->mode = LMU_BL_PWM;
+
+       return lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_BRT_MODE,
+                                 LM3631_BRT_MASK, pdata->mode);
+}
+
+static int lm3631_bl_string_configure(struct lm3631_bl *lm3631_bl)
+{
+       u8 val;
+
+       if (lm3631_bl->pdata->is_full_strings)
+               val = LM3631_BL_TWO_STRINGS;
+       else
+               val = LM3631_BL_ONE_STRING;
+
+       return lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_BL_CFG,
+                                 LM3631_BL_STRING_MASK, val);
+}
+
+static int lm3631_bl_configure(struct lm3631_bl *lm3631_bl)
+{
+       int ret;
+
+       ret = lm3631_bl_set_ctrl_mode(lm3631_bl);
+       if (ret)
+               return ret;
+
+       return lm3631_bl_string_configure(lm3631_bl);
+}
+
+static int lm3631_bl_parse_dt(struct device *dev, struct lm3631_bl *lm3631_bl)
+{
+       struct device_node *node = dev->of_node;
+       struct lm3631_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 = LM3631_PWM_ONLY;
+       else if (of_find_property(node, "mode-comb1", NULL))
+               pdata->mode = LM3631_COMB1;
+       else if (of_find_property(node, "mode-comb2", NULL))
+               pdata->mode = LM3631_COMB2;
+
+       of_property_read_u8(node, "initial-brightness",
+                           (u8 *)&pdata->init_brightness);
+
+       of_property_read_u32(node, "pwm-period", &pdata->pwm_period);
+
+       lm3631_bl->pdata = pdata;
+
+       return 0;
+}
+
+static int lm3631_bl_probe(struct platform_device *pdev)
+{
+       struct lm3631 *lm3631 = dev_get_drvdata(pdev->dev.parent);
+       struct lm3631_backlight_platform_data *pdata = lm3631->pdata->bl_pdata;
+       struct lm3631_bl *lm3631_bl;
+       int ret;
+
+       lm3631_bl = devm_kzalloc(&pdev->dev, sizeof(*lm3631_bl), GFP_KERNEL);
+       if (!lm3631_bl)
+               return -ENOMEM;
+
+       lm3631_bl->pdata = pdata;
+       if (!lm3631_bl->pdata) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = lm3631_bl_parse_dt(&pdev->dev, lm3631_bl);
+               else
+                       return -ENODEV;
+
+               if (ret)
+                       return ret;
+       }
+
+       lm3631_bl->dev = &pdev->dev;
+       lm3631_bl->lm3631 = lm3631;
+       platform_set_drvdata(pdev, lm3631_bl);
+
+       ret = lm3631_bl_configure(lm3631_bl);
+       if (ret) {
+               dev_err(&pdev->dev, "backlight config err: %d\n", ret);
+               return ret;
+       }
+
+       ret = lm3631_bl_register(lm3631_bl);
+       if (ret) {
+               dev_err(&pdev->dev, "register backlight err: %d\n", ret);
+               return ret;
+       }
+
+       backlight_update_status(lm3631_bl->bl_dev);
+
+       return 0;
+}
+
+static int lm3631_bl_remove(struct platform_device *pdev)
+{
+       struct lm3631_bl *lm3631_bl = platform_get_drvdata(pdev);
+       struct backlight_device *bl_dev = lm3631_bl->bl_dev;
+
+       bl_dev->props.brightness = 0;
+       backlight_update_status(bl_dev);
+       lm3631_bl_unregister(lm3631_bl);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3631_bl_of_match[] = {
+       { .compatible = "ti,lm3631-backlight", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lm3631_bl_of_match);
+#endif
+
+static struct platform_driver lm3631_bl_driver = {
+       .probe = lm3631_bl_probe,
+       .remove = lm3631_bl_remove,
+       .driver = {
+               .name = "lm3631-backlight",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(lm3631_bl_of_match),
+       },
+};
+module_platform_driver(lm3631_bl_driver);
+
+MODULE_DESCRIPTION("TI LM3631 Backlight Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3631-backlight");
diff --git a/include/linux/mfd/lm3631.h b/include/linux/mfd/lm3631.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