From: ChiYuan Huang <cy_hu...@richtek.com>

Adds support for the RT4801 DSV. It has two regulators (DSVP/DSVN) with an I2C
interface. DSVP/DSVN can provide the display panel module for the 
positive/negative
voltage range from (+/-)4V to (+/-)6V.
---
 .../regulator/richtek,rt4801-regulator.yaml        |  80 ++++++++
 drivers/regulator/Kconfig                          |   7 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/rt4801-regulator.c               | 223 +++++++++++++++++++++
 4 files changed, 311 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/regulator/richtek,rt4801-regulator.yaml
 create mode 100644 drivers/regulator/rt4801-regulator.c

diff --git 
a/Documentation/devicetree/bindings/regulator/richtek,rt4801-regulator.yaml 
b/Documentation/devicetree/bindings/regulator/richtek,rt4801-regulator.yaml
new file mode 100644
index 00000000..28d30e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/richtek,rt4801-regulator.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/richtek,rt4801-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Richtek RT4801 Display Bias regulators
+
+maintainers:
+  - ChiYuan Huang <cy_hu...@richtek.com>
+
+description: |
+  Regulator nodes should be named to DSVP and DSVN. The
+  definition for each of these nodes is defined using the standard
+  binding for regulators at
+  Documentation/devicetree/bindings/regulator/regulator.txt.
+  Datasheet is available at
+  https://www.richtek.com/assets/product_file/RT4801H/DS4801H-00.pdf
+
+#The valid names for RT4801 regulator nodes are:
+#DSVP, DSVN
+
+properties:
+  compatible:
+    enum:
+      - richtek,rt4801
+
+  reg:
+    maxItems: 1
+
+  enable-gpios:
+    description: GPIOs to use to enable DSVP/DSVN regulator.
+      The first one is ENP to enable DSVP, and second one is ENM to enable 
DSVN.
+      Number of GPIO in the array list could be 1 or 2.
+      If only one gpio is specified, only one gpio used to control ENP/ENM.
+      Else both are spefied, DSVP/DSVN could be controlled individually.
+      Othersie, this property not specified. treat both as always-on regulator.
+    minItems: 1
+    maxItems: 2
+
+patternProperties:
+  "^DSV(P|N)$":
+    type: object
+    $ref: regulator.yaml#
+    description:
+      Properties for single display bias regulator.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties:
+  - enable-gpios
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        rt4801@73 {
+            compatible = "richtek,rt4801";
+            reg = <0x73>;
+            enable-gpios = <&gpio26 2 0>, <&gpio26 3 0>;
+
+            dsvp: DSVP {
+                regulator-name = "rt4801,dsvp";
+                regulator-min-microvolt = <4000000>;
+                regulator-max-microvolt = <6000000>;
+                regulator-boot-on;
+            };
+            dsvn: DSVN {
+                regulator-name = "rt4801,dsvn";
+                regulator-min-microvolt = <4000000>;
+                regulator-max-microvolt = <6000000>;
+                regulator-boot-on;
+            };
+
+        };
+    };
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index de17ef7..2786f11 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -894,6 +894,13 @@ config REGULATOR_RN5T618
 config REGULATOR_ROHM
        tristate
 
+config REGULATOR_RT4801
+       tristate "Richtek RT4801 Regulators"
+       depends on I2C
+       help
+         This adds support for voltage regulators in Richtek RT4801 Display 
Bias IC.
+         The device supports two regulators (DSVP/DSVN).
+
 config REGULATOR_RT5033
        tristate "Richtek RT5033 Regulators"
        depends on MFD_RT5033
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index d8d3ecf..d091e52d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -111,6 +111,7 @@ obj-$(CONFIG_REGULATOR_RC5T583)  += rc5t583-regulator.o
 obj-$(CONFIG_REGULATOR_RK808)   += rk808-regulator.o
 obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
 obj-$(CONFIG_REGULATOR_ROHM)   += rohm-regulator.o
+obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o
 obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
 obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
diff --git a/drivers/regulator/rt4801-regulator.c 
b/drivers/regulator/rt4801-regulator.c
new file mode 100644
index 00000000..0ddc670
--- /dev/null
+++ b/drivers/regulator/rt4801-regulator.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+#define RT4801_REG_VOP 0x00
+#define RT4801_REG_VON 0x01
+#define RT4801_REG_APPS        0x03
+
+#define VOUT_MASK      0x1F
+
+#define MIN_UV         4000000
+#define STEP_UV                100000
+#define MAX_UV         6000000
+#define N_VOLTAGES     ((MAX_UV - MIN_UV) / STEP_UV + 1)
+
+#define DSV_OUT_POS    0
+#define DSV_OUT_NEG    1
+#define DSV_OUT_MAX    2
+
+#define DSVP_ENABLE    BIT(0)
+#define DSVN_ENABLE    BIT(1)
+#define DSVALL_ENABLE  (DSVP_ENABLE | DSVN_ENABLE)
+
+struct rt4801_priv {
+       struct device *dev;
+       struct gpio_descs *enable_gpios;
+       unsigned int enable_flag;
+       unsigned int volt_sel[DSV_OUT_MAX];
+};
+
+static int rt4801_set_voltage_sel(struct regulator_dev *rdev, unsigned int 
selector)
+{
+       struct rt4801_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev), ret;
+
+       if (priv->enable_flag & BIT(id)) {
+               ret = regulator_set_voltage_sel_regmap(rdev, selector);
+               if (ret)
+                       return ret;
+       }
+
+       priv->volt_sel[id] = selector;
+       return 0;
+}
+
+static int rt4801_get_voltage_sel(struct regulator_dev *rdev)
+{
+       struct rt4801_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev);
+
+       if (priv->enable_flag & BIT(id))
+               return regulator_get_voltage_sel_regmap(rdev);
+
+       return priv->volt_sel[id];
+}
+
+static int rt4801_enable(struct regulator_dev *rdev)
+{
+       struct rt4801_priv *priv = rdev_get_drvdata(rdev);
+       struct gpio_descs *gpios = priv->enable_gpios;
+       int id = rdev_get_id(rdev), ret;
+
+       if (gpios->ndescs <= id) {
+               dev_warn(&rdev->dev, "no dedicated gpio can control\n");
+               goto bypass_gpio;
+       }
+
+       gpiod_set_value(gpios->desc[id], 1);
+
+bypass_gpio:
+       ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, 
priv->volt_sel[id]);
+       if (ret)
+               return ret;
+
+       priv->enable_flag |= BIT(id);
+       return 0;
+}
+
+static int rt4801_disable(struct regulator_dev *rdev)
+{
+       struct rt4801_priv *priv = rdev_get_drvdata(rdev);
+       struct gpio_descs *gpios = priv->enable_gpios;
+       int id = rdev_get_id(rdev);
+
+       if (gpios->ndescs <= id) {
+               dev_warn(&rdev->dev, "no dedicated gpio can control\n");
+               goto bypass_gpio;
+       }
+
+       gpiod_set_value(gpios->desc[id], 0);
+
+bypass_gpio:
+       priv->enable_flag &= ~BIT(id);
+       return 0;
+}
+
+static int rt4801_is_enabled(struct regulator_dev *rdev)
+{
+       struct rt4801_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev);
+
+       return !!(priv->enable_flag & BIT(id));
+}
+
+static const struct regulator_ops rt4801_regulator_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+       .set_voltage_sel = rt4801_set_voltage_sel,
+       .get_voltage_sel = rt4801_get_voltage_sel,
+       .enable = rt4801_enable,
+       .disable = rt4801_disable,
+       .is_enabled = rt4801_is_enabled,
+};
+
+static const struct regulator_desc rt4801_regulator_descs[] = {
+       {
+               .name = "DSVP",
+               .ops = &rt4801_regulator_ops,
+               .of_match = of_match_ptr("DSVP"),
+               .type = REGULATOR_VOLTAGE,
+               .id = DSV_OUT_POS,
+               .min_uV = MIN_UV,
+               .uV_step = STEP_UV,
+               .n_voltages = N_VOLTAGES,
+               .owner = THIS_MODULE,
+               .vsel_reg = RT4801_REG_VOP,
+               .vsel_mask = VOUT_MASK,
+       },
+       {
+               .name = "DSVN",
+               .ops = &rt4801_regulator_ops,
+               .of_match = of_match_ptr("DSVN"),
+               .type = REGULATOR_VOLTAGE,
+               .id = DSV_OUT_NEG,
+               .min_uV = MIN_UV,
+               .uV_step = STEP_UV,
+               .n_voltages = N_VOLTAGES,
+               .owner = THIS_MODULE,
+               .vsel_reg = RT4801_REG_VON,
+               .vsel_mask = VOUT_MASK,
+       },
+};
+
+static const struct regmap_config rt4801_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = RT4801_REG_APPS,
+};
+
+static int rt4801_probe(struct i2c_client *i2c)
+{
+       struct rt4801_priv *priv;
+       struct regmap *regmap;
+       int i;
+
+       priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev = &i2c->dev;
+       /* bootloader will on, driver only reconfigure enable to all output 
high */
+       priv->enable_flag = DSVALL_ENABLE;
+
+       regmap = devm_regmap_init_i2c(i2c, &rt4801_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&i2c->dev, "Failed to init regmap\n");
+               return PTR_ERR(regmap);
+       }
+
+       priv->enable_gpios = devm_gpiod_get_array_optional(&i2c->dev, "enable", 
GPIOD_OUT_HIGH);
+       if (IS_ERR(priv->enable_gpios)) {
+               dev_err(&i2c->dev, "Failed to get gpios\n");
+               return PTR_ERR(priv->enable_gpios);
+       }
+
+       for (i = 0; i < DSV_OUT_MAX; i++) {
+               const struct regulator_desc *desc = rt4801_regulator_descs + i;
+               struct regulator_config config = { .dev = &i2c->dev, 
.driver_data = priv,
+                                                  .regmap = regmap, };
+               struct regulator_dev *rdev;
+               unsigned int val;
+               int ret;
+
+               /* initialize volt_sel variable */
+               ret = regmap_read(regmap, desc->vsel_reg, &val);
+               if (ret)
+                       return ret;
+
+               priv->volt_sel[i] = val & desc->vsel_mask;
+
+               rdev = devm_regulator_register(&i2c->dev, desc, &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(&i2c->dev, "Failed to register [%d] 
regulator\n", i);
+                       return PTR_ERR(rdev);
+               }
+       }
+
+       return 0;
+}
+
+static const struct of_device_id rt4801_of_id[] = {
+       { .compatible = "richtek,rt4801", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rt4801_of_id);
+
+static struct i2c_driver rt4801_driver = {
+       .driver = {
+               .name = "rt4801",
+               .of_match_table = of_match_ptr(rt4801_of_id),
+       },
+       .probe_new = rt4801_probe,
+};
+module_i2c_driver(rt4801_driver);
+
+MODULE_AUTHOR("ChiYuan Hwang <cy_hu...@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT4801 Display Bias Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

Reply via email to