Commit ec31abf6 authored by Hai Li's avatar Hai Li Committed by Rob Clark

drm/msm/dsi: Separate PHY to another platform device

There are different types of PHY from one chipset to another, while
the DSI host controller is relatively consistent across platforms.
Also, the PLL inside PHY is providing the source of DSI byte and
pixel clocks, which are used by DSI host controller. Separated devices
for clock provider and clock consumer make DSI driver better fit into
common clock framework.
Signed-off-by: default avatarHai Li <hali@codeaurora.org>
Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 9d32c498
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.
......@@ -24,10 +21,10 @@ Required properties:
* "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.
......@@ -42,22 +39,34 @@ Optional properties:
- 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",
......@@ -75,11 +84,12 @@ Example:
<&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;
......@@ -93,3 +103,18 @@ Example:
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>;
};
......@@ -23,6 +23,34 @@ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi)
msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID];
}
static int dsi_get_phy(struct msm_dsi *msm_dsi)
{
struct platform_device *pdev = msm_dsi->pdev;
struct platform_device *phy_pdev;
struct device_node *phy_node;
phy_node = of_parse_phandle(pdev->dev.of_node, "qcom,dsi-phy", 0);
if (!phy_node) {
dev_err(&pdev->dev, "cannot find phy device\n");
return -ENXIO;
}
phy_pdev = of_find_device_by_node(phy_node);
if (phy_pdev)
msm_dsi->phy = platform_get_drvdata(phy_pdev);
of_node_put(phy_node);
if (!phy_pdev || !msm_dsi->phy) {
dev_err(&pdev->dev, "%s: phy driver is not ready\n", __func__);
return -EPROBE_DEFER;
}
msm_dsi->phy_dev = get_device(&phy_pdev->dev);
return 0;
}
static void dsi_destroy(struct msm_dsi *msm_dsi)
{
if (!msm_dsi)
......@@ -30,9 +58,10 @@ static void dsi_destroy(struct msm_dsi *msm_dsi)
msm_dsi_manager_unregister(msm_dsi);
if (msm_dsi->phy) {
msm_dsi_phy_destroy(msm_dsi->phy);
if (msm_dsi->phy_dev) {
put_device(msm_dsi->phy_dev);
msm_dsi->phy = NULL;
msm_dsi->phy_dev = NULL;
}
if (msm_dsi->host) {
......@@ -49,7 +78,6 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev)
int ret;
if (!pdev) {
dev_err(&pdev->dev, "no dsi device\n");
ret = -ENXIO;
goto fail;
}
......@@ -69,13 +97,10 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev)
if (ret)
goto fail;
/* Init dsi PHY */
msm_dsi->phy = msm_dsi_phy_init(pdev, msm_dsi->phy_type, msm_dsi->id);
if (!msm_dsi->phy) {
ret = -ENXIO;
pr_err("%s: phy init failed\n", __func__);
/* GET dsi PHY */
ret = dsi_get_phy(msm_dsi);
if (ret)
goto fail;
}
/* Register to dsi manager */
ret = msm_dsi_manager_register(msm_dsi);
......@@ -156,12 +181,14 @@ static struct platform_driver dsi_driver = {
void __init msm_dsi_register(void)
{
DBG("");
msm_dsi_phy_driver_register();
platform_driver_register(&dsi_driver);
}
void __exit msm_dsi_unregister(void)
{
DBG("");
msm_dsi_phy_driver_unregister();
platform_driver_unregister(&dsi_driver);
}
......
......@@ -14,6 +14,7 @@
#ifndef __DSI_CONNECTOR_H__
#define __DSI_CONNECTOR_H__
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "drm_crtc.h"
......@@ -39,12 +40,27 @@
#define DSI_ENCODER_SLAVE DSI_0
enum msm_dsi_phy_type {
MSM_DSI_PHY_UNKNOWN,
MSM_DSI_PHY_28NM_HPM,
MSM_DSI_PHY_28NM_LP,
MSM_DSI_PHY_MAX
};
#define DSI_DEV_REGULATOR_MAX 8
/* Regulators for DSI devices */
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_DEV_REGULATOR_MAX];
};
struct msm_dsi {
struct drm_device *dev;
struct platform_device *pdev;
......@@ -57,7 +73,7 @@ struct msm_dsi {
struct drm_panel *panel;
unsigned long panel_flags;
enum msm_dsi_phy_type phy_type;
struct device *phy_dev;
bool phy_enabled;
/* the encoders we are hooked to (outside of dsi block) */
......@@ -135,9 +151,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi);
/* dsi phy */
struct msm_dsi_phy;
struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
enum msm_dsi_phy_type type, int id);
void msm_dsi_phy_destroy(struct msm_dsi_phy *phy);
void msm_dsi_phy_driver_register(void);
void msm_dsi_phy_driver_unregister(void);
int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
const unsigned long bit_rate, const unsigned long esc_rate);
int msm_dsi_phy_disable(struct msm_dsi_phy *phy);
......
......@@ -36,35 +36,19 @@
#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 = {
......@@ -79,7 +63,6 @@ static const struct dsi_config dsi_cfgs[] = {
.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 = {
......@@ -94,7 +77,6 @@ static const struct dsi_config dsi_cfgs[] = {
.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 = {
......@@ -109,7 +91,6 @@ static const struct dsi_config dsi_cfgs[] = {
.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 = {
......@@ -124,7 +105,6 @@ static const struct dsi_config dsi_cfgs[] = {
.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 = {
......@@ -197,7 +177,7 @@ struct msm_dsi_host {
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;
......@@ -1534,7 +1514,6 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
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;
......
......@@ -11,12 +11,27 @@
* 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;
......@@ -40,17 +55,100 @@ struct msm_dsi_phy {
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)))
......@@ -313,51 +411,94 @@ static void dsi_phy_disable_resource(struct msm_dsi_phy *phy)
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
......@@ -365,39 +506,84 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
*/
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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment