summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: f75be32)
raw | patch | inline | side by side (parent: f75be32)
author | Daniel Jeong <gshark.jeong@gmail.com> | |
Thu, 12 Feb 2015 11:51:30 +0000 (20:51 +0900) | ||
committer | Daniel 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] | patch | blob |
Documentation/devicetree/bindings/regulator/lm3632-regulator.txt | [new file with mode: 0644] | patch | blob |
Documentation/devicetree/bindings/video/backlight/lm3632_bl.txt | [new file with mode: 0644] | patch | blob |
drivers/mfd/Kconfig | patch | blob | history | |
drivers/mfd/Makefile | patch | blob | history | |
drivers/mfd/lm3632.c | [new file with mode: 0644] | patch | blob |
drivers/regulator/Kconfig | patch | blob | history | |
drivers/regulator/Makefile | patch | blob | history | |
drivers/regulator/lm3632-regulator.c | [new file with mode: 0644] | patch | blob |
drivers/video/backlight/Kconfig | patch | blob | history | |
drivers/video/backlight/Makefile | patch | blob | history | |
drivers/video/backlight/lm3632_bl.c | [new file with mode: 0644] | patch | blob |
include/linux/mfd/lm3632.h | [new file with mode: 0644] | patch | blob |
diff --git a/Documentation/devicetree/bindings/mfd/lm3632.txt b/Documentation/devicetree/bindings/mfd/lm3632.txt
--- /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
--- /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
--- /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";
+ };
+};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 28e29fcb7e49b031ba0fa8c85135d71b27674e06..3d250c5c291fcf1be5699124fa4e2f0409364ba4 100644 (file)
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
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
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 109247099be48de1dde321f27d25fbd1605e9bad..9a35a740bfafd67774870fe881db6e5e5cbc6ab3 100644 (file)
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
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
--- /dev/null
+++ b/drivers/mfd/lm3632.c
@@ -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)
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)
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
--- /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)
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)
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
--- /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
--- /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