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,