Qualcomm Technologies Inc. adreno/snapdragon DSI output
 
+DSI Controller:
 Required properties:
 - compatible:
   * "qcom,mdss-dsi-ctrl"
-- reg: Physical base address and length of the registers of controller, PLL,
-  PHY and PHY regulator
+- reg: Physical base address and length of the registers of controller
 - reg-names: The names of register regions. The following regions are required:
   * "dsi_ctrl"
-  * "dsi_pll"
-  * "dsi_phy"
-  * "dsi_phy_regulator"
 - qcom,dsi-host-index: The ID of DSI controller hardware instance. This should
   be 0 or 1, since we have 2 DSI controllers at most for now.
 - interrupts: The interrupt signal from the DSI block.
   * "iface_clk"
   * "mdp_core_clk"
   * "pixel_clk"
-- #clock-cells: The value should be 1.
 - vdd-supply: phandle to vdd regulator device node
 - vddio-supply: phandle to vdd-io regulator device node
 - vdda-supply: phandle to vdda regulator device node
+- qcom,dsi-phy: phandle to DSI PHY device node
 
 Optional properties:
 - panel@0: Node of panel connected to this DSI controller.
 - interrupt-parent: phandle to the MDP block if the interrupt signal is routed
   through MDP block
 
+DSI PHY:
+Required properties:
+- compatible: Could be the following
+  * "qcom,dsi-phy-28nm-hpm"
+  * "qcom,dsi-phy-28nm-lp"
+- reg: Physical base address and length of the registers of PLL, PHY and PHY
+  regulator
+- reg-names: The names of register regions. The following regions are required:
+  * "dsi_pll"
+  * "dsi_phy"
+  * "dsi_phy_regulator"
+- qcom,dsi-phy-index: The ID of DSI PHY hardware instance. This should
+  be 0 or 1, since we have 2 DSI PHYs at most for now.
+- power-domains: Should be <&mmcc MDSS_GDSC>.
+- clocks: device clocks
+  See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details.
+- clock-names: the following clocks are required:
+  * "iface_clk"
+- vddio-supply: phandle to vdd-io regulator device node
+
 Example:
        mdss_dsi0: qcom,mdss_dsi@fd922800 {
                compatible = "qcom,mdss-dsi-ctrl";
                qcom,dsi-host-index = <0>;
                interrupt-parent = <&mdss_mdp>;
                interrupts = <4 0>;
-               reg-names =
-                       "dsi_ctrl",
-                       "dsi_pll",
-                       "dsi_phy",
-                       "dsi_phy_regulator",
-               reg =   <0xfd922800 0x200>,
-                       <0xfd922a00 0xd4>,
-                       <0xfd922b00 0x2b0>,
-                       <0xfd922d80 0x7b>,
-                       <0xfd828000 0x108>;
+               reg-names = "dsi_ctrl";
+               reg = <0xfd922800 0x200>;
                power-domains = <&mmcc MDSS_GDSC>;
                clock-names =
                        "bus_clk",
                        <&mmcc MDSS_AHB_CLK>,
                        <&mmcc MDSS_MDP_CLK>,
                        <&mmcc MDSS_PCLK0_CLK>;
-               #clock-cells = <1>;
                vdda-supply = <&pma8084_l2>;
                vdd-supply = <&pma8084_l22>;
                vddio-supply = <&pma8084_l12>;
 
+               qcom,dsi-phy = <&mdss_dsi_phy0>;
+
                qcom,dual-panel-mode;
                qcom,master-panel;
                qcom,sync-dual-panel;
                        backlight = <...>;
                };
        };
+
+       mdss_dsi_phy0: qcom,mdss_dsi_phy@fd922a00 {
+               compatible = "qcom,dsi-phy-28nm-hpm";
+               qcom,dsi-phy-index = <0>;
+               reg-names =
+                       "dsi_pll",
+                       "dsi_phy",
+                       "dsi_phy_regulator";
+               reg =   <0xfd922a00 0xd4>,
+                       <0xfd922b00 0x2b0>,
+                       <0xfd922d80 0x7b>;
+               clock-names = "iface_clk";
+               clocks = <&mmcc MDSS_AHB_CLK>;
+               vddio-supply = <&pma8084_l12>;
+       };
 
 
 #define DSI_6G_REG_SHIFT       4
 
-#define DSI_REGULATOR_MAX      8
-struct dsi_reg_entry {
-       char name[32];
-       int min_voltage;
-       int max_voltage;
-       int enable_load;
-       int disable_load;
-};
-
-struct dsi_reg_config {
-       int num;
-       struct dsi_reg_entry regs[DSI_REGULATOR_MAX];
-};
-
 struct dsi_config {
        u32 major;
        u32 minor;
        u32 io_offset;
-       enum msm_dsi_phy_type phy_type;
        struct dsi_reg_config reg_cfg;
 };
 
 static const struct dsi_config dsi_cfgs[] = {
-       {MSM_DSI_VER_MAJOR_V2, 0, 0, MSM_DSI_PHY_UNKNOWN},
+       {MSM_DSI_VER_MAJOR_V2, 0, 0, {0,} },
        { /* 8974 v1 */
                .major = MSM_DSI_VER_MAJOR_6G,
                .minor = MSM_DSI_6G_VER_MINOR_V1_0,
                .io_offset = DSI_6G_REG_SHIFT,
-               .phy_type = MSM_DSI_PHY_28NM_HPM,
                .reg_cfg = {
                        .num = 4,
                        .regs = {
                .major = MSM_DSI_VER_MAJOR_6G,
                .minor = MSM_DSI_6G_VER_MINOR_V1_1,
                .io_offset = DSI_6G_REG_SHIFT,
-               .phy_type = MSM_DSI_PHY_28NM_HPM,
                .reg_cfg = {
                        .num = 4,
                        .regs = {
                .major = MSM_DSI_VER_MAJOR_6G,
                .minor = MSM_DSI_6G_VER_MINOR_V1_1_1,
                .io_offset = DSI_6G_REG_SHIFT,
-               .phy_type = MSM_DSI_PHY_28NM_HPM,
                .reg_cfg = {
                        .num = 4,
                        .regs = {
                .major = MSM_DSI_VER_MAJOR_6G,
                .minor = MSM_DSI_6G_VER_MINOR_V1_2,
                .io_offset = DSI_6G_REG_SHIFT,
-               .phy_type = MSM_DSI_PHY_28NM_HPM,
                .reg_cfg = {
                        .num = 4,
                        .regs = {
                .major = MSM_DSI_VER_MAJOR_6G,
                .minor = MSM_DSI_6G_VER_MINOR_V1_3_1,
                .io_offset = DSI_6G_REG_SHIFT,
-               .phy_type = MSM_DSI_PHY_28NM_LP,
                .reg_cfg = {
                        .num = 4,
                        .regs = {
        int id;
 
        void __iomem *ctrl_base;
-       struct regulator_bulk_data supplies[DSI_REGULATOR_MAX];
+       struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
        struct clk *mdp_core_clk;
        struct clk *ahb_clk;
        struct clk *axi_clk;
 
        msm_dsi->host = &msm_host->base;
        msm_dsi->id = msm_host->id;
-       msm_dsi->phy_type = msm_host->cfg->phy_type;
 
        DBG("Dsi Host %d initialized", msm_host->id);
        return 0;
 
  * GNU General Public License for more details.
  */
 
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
 #include "dsi.h"
 #include "dsi.xml.h"
 
 #define dsi_phy_read(offset) msm_readl((offset))
 #define dsi_phy_write(offset, data) msm_writel((data), (offset))
 
+struct dsi_phy_ops {
+       int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
+               const unsigned long bit_rate, const unsigned long esc_rate);
+       int (*disable)(struct msm_dsi_phy *phy);
+};
+
+struct dsi_phy_cfg {
+       enum msm_dsi_phy_type type;
+       struct dsi_reg_config reg_cfg;
+       struct dsi_phy_ops ops;
+};
+
 struct dsi_dphy_timing {
        u32 clk_pre;
        u32 clk_post;
        int id;
 
        struct clk *ahb_clk;
+       struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
 
        struct dsi_dphy_timing timing;
-       enum msm_dsi_phy_type type;
+       const struct dsi_phy_cfg *cfg;
 
        struct msm_dsi_pll *pll;
-
-       int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
-               const unsigned long bit_rate, const unsigned long esc_rate);
-       int (*disable)(struct msm_dsi_phy *phy);
 };
 
+static int dsi_phy_regulator_init(struct msm_dsi_phy *phy)
+{
+       struct regulator_bulk_data *s = phy->supplies;
+       const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+       struct device *dev = &phy->pdev->dev;
+       int num = phy->cfg->reg_cfg.num;
+       int i, ret;
+
+       for (i = 0; i < num; i++)
+               s[i].supply = regs[i].name;
+
+       ret = devm_regulator_bulk_get(&phy->pdev->dev, num, s);
+       if (ret < 0) {
+               dev_err(dev, "%s: failed to init regulator, ret=%d\n",
+                                               __func__, ret);
+               return ret;
+       }
+
+       for (i = 0; i < num; i++) {
+               if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) {
+                       ret = regulator_set_voltage(s[i].consumer,
+                               regs[i].min_voltage, regs[i].max_voltage);
+                       if (ret < 0) {
+                               dev_err(dev,
+                                       "regulator %d set voltage failed, %d\n",
+                                       i, ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy)
+{
+       struct regulator_bulk_data *s = phy->supplies;
+       const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+       int num = phy->cfg->reg_cfg.num;
+       int i;
+
+       DBG("");
+       for (i = num - 1; i >= 0; i--)
+               if (regs[i].disable_load >= 0)
+                       regulator_set_load(s[i].consumer,
+                                               regs[i].disable_load);
+
+       regulator_bulk_disable(num, s);
+}
+
+static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy)
+{
+       struct regulator_bulk_data *s = phy->supplies;
+       const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+       struct device *dev = &phy->pdev->dev;
+       int num = phy->cfg->reg_cfg.num;
+       int ret, i;
+
+       DBG("");
+       for (i = 0; i < num; i++) {
+               if (regs[i].enable_load >= 0) {
+                       ret = regulator_set_load(s[i].consumer,
+                                                       regs[i].enable_load);
+                       if (ret < 0) {
+                               dev_err(dev,
+                                       "regulator %d set op mode failed, %d\n",
+                                       i, ret);
+                               goto fail;
+                       }
+               }
+       }
+
+       ret = regulator_bulk_enable(num, s);
+       if (ret < 0) {
+               dev_err(dev, "regulator enable failed, %d\n", ret);
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       for (i--; i >= 0; i--)
+               regulator_set_load(s[i].consumer, regs[i].disable_load);
+       return ret;
+}
+
 #define S_DIV_ROUND_UP(n, d)   \
        (((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d)))
 
        pm_runtime_put_sync(&phy->pdev->dev);
 }
 
-#define dsi_phy_func_init(name)        \
-       do {    \
-               phy->enable = dsi_##name##_phy_enable;  \
-               phy->disable = dsi_##name##_phy_disable;        \
-       } while (0)
+static const struct dsi_phy_cfg dsi_phy_cfgs[MSM_DSI_PHY_MAX] = {
+       [MSM_DSI_PHY_28NM_HPM] = {
+               .type = MSM_DSI_PHY_28NM_HPM,
+               .reg_cfg = {
+                       .num = 1,
+                       .regs = {
+                               {"vddio", 1800000, 1800000, 100000, 100},
+                       },
+               },
+               .ops = {
+                       .enable = dsi_28nm_phy_enable,
+                       .disable = dsi_28nm_phy_disable,
+               }
+       },
+       [MSM_DSI_PHY_28NM_LP] = {
+               .type = MSM_DSI_PHY_28NM_LP,
+               .reg_cfg = {
+                       .num = 1,
+                       .regs = {
+                               {"vddio", 1800000, 1800000, 100000, 100},
+                       },
+               },
+               .ops = {
+                       .enable = dsi_28nm_phy_enable,
+                       .disable = dsi_28nm_phy_disable,
+               }
+       },
+};
+
+static const struct of_device_id dsi_phy_dt_match[] = {
+       { .compatible = "qcom,dsi-phy-28nm-hpm",
+         .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_HPM],},
+       { .compatible = "qcom,dsi-phy-28nm-lp",
+         .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_LP],},
+       {}
+};
 
-struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
-                       enum msm_dsi_phy_type type, int id)
+static int dsi_phy_driver_probe(struct platform_device *pdev)
 {
        struct msm_dsi_phy *phy;
+       const struct of_device_id *match;
        int ret;
 
        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
        if (!phy)
-               return NULL;
+               return -ENOMEM;
+
+       match = of_match_node(dsi_phy_dt_match, pdev->dev.of_node);
+       if (!match)
+               return -ENODEV;
+
+       phy->cfg = match->data;
+       phy->pdev = pdev;
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+                               "qcom,dsi-phy-index", &phy->id);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "%s: PHY index not specified, ret=%d\n",
+                       __func__, ret);
+               goto fail;
+       }
 
        phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
        if (IS_ERR(phy->base)) {
-               pr_err("%s: failed to map phy base\n", __func__);
-               return NULL;
+               dev_err(&pdev->dev, "%s: failed to map phy base\n", __func__);
+               ret = -ENOMEM;
+               goto fail;
        }
        phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG");
        if (IS_ERR(phy->reg_base)) {
-               pr_err("%s: failed to map phy regulator base\n", __func__);
-               return NULL;
+               dev_err(&pdev->dev,
+                       "%s: failed to map phy regulator base\n", __func__);
+               ret = -ENOMEM;
+               goto fail;
        }
 
-       switch (type) {
-       case MSM_DSI_PHY_28NM_HPM:
-       case MSM_DSI_PHY_28NM_LP:
-               dsi_phy_func_init(28nm);
-               break;
-       default:
-               pr_err("%s: unsupported type, %d\n", __func__, type);
-               return NULL;
+       ret = dsi_phy_regulator_init(phy);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to init regulator\n", __func__);
+               goto fail;
        }
 
-       phy->type = type;
-       phy->id = id;
-       phy->pdev = pdev;
-
        phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
        if (IS_ERR(phy->ahb_clk)) {
                pr_err("%s: Unable to get ahb clk\n", __func__);
-               return NULL;
+               ret = PTR_ERR(phy->ahb_clk);
+               goto fail;
        }
 
        /* PLL init will call into clk_register which requires
         */
        ret = dsi_phy_enable_resource(phy);
        if (ret)
-               return NULL;
+               goto fail;
 
-       phy->pll = msm_dsi_pll_init(pdev, type, id);
+       phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id);
        if (!phy->pll)
-               pr_info("%s: pll init failed, need separate pll clk driver\n",
+               dev_info(&pdev->dev,
+                       "%s: pll init failed, need separate pll clk driver\n",
                        __func__);
 
        dsi_phy_disable_resource(phy);
 
-       return phy;
+       platform_set_drvdata(pdev, phy);
+
+       return 0;
+
+fail:
+       return ret;
 }
 
-void msm_dsi_phy_destroy(struct msm_dsi_phy *phy)
+static int dsi_phy_driver_remove(struct platform_device *pdev)
 {
-       if (phy->pll) {
+       struct msm_dsi_phy *phy = platform_get_drvdata(pdev);
+
+       if (phy && phy->pll) {
                msm_dsi_pll_destroy(phy->pll);
                phy->pll = NULL;
        }
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver dsi_phy_platform_driver = {
+       .probe      = dsi_phy_driver_probe,
+       .remove     = dsi_phy_driver_remove,
+       .driver     = {
+               .name   = "msm_dsi_phy",
+               .of_match_table = dsi_phy_dt_match,
+       },
+};
+
+void __init msm_dsi_phy_driver_register(void)
+{
+       platform_driver_register(&dsi_phy_platform_driver);
+}
+
+void __exit msm_dsi_phy_driver_unregister(void)
+{
+       platform_driver_unregister(&dsi_phy_platform_driver);
 }
 
 int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
        const unsigned long bit_rate, const unsigned long esc_rate)
 {
-       if (!phy || !phy->enable)
+       int ret;
+
+       if (!phy || !phy->cfg->ops.enable)
                return -EINVAL;
-       return phy->enable(phy, is_dual_panel, bit_rate, esc_rate);
+
+       ret = dsi_phy_regulator_enable(phy);
+       if (ret) {
+               dev_err(&phy->pdev->dev, "%s: regulator enable failed, %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       return phy->cfg->ops.enable(phy, is_dual_panel, bit_rate, esc_rate);
 }
 
 int msm_dsi_phy_disable(struct msm_dsi_phy *phy)
 {
-       if (!phy || !phy->disable)
+       if (!phy || !phy->cfg->ops.disable)
                return -EINVAL;
-       return phy->disable(phy);
+
+       phy->cfg->ops.disable(phy);
+       dsi_phy_regulator_disable(phy);
+
+       return 0;
 }
 
 void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,