mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 14:42:37 +02:00
drm/rockchip: dsi: Add support for PX30/RK1808/RK3128/RK3368
Change-Id: Ia9934a50c63e046e34ea694e10b0e1d17b53a6f0 Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
parent
b2028b88c9
commit
a96ba7fcd5
|
|
@ -4,21 +4,32 @@ Rockchip specific extensions to the Synopsys Designware MIPI DSI
|
|||
Required properties:
|
||||
- #address-cells: Should be <1>.
|
||||
- #size-cells: Should be <0>.
|
||||
- compatible: "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
"rockchip,rk3399-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
- compatible: must be one of:
|
||||
"rockchip,px30-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
"rockchip,rk1808-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
"rockchip,rk3128-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
"rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
"rockchip,rk3368-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
"rockchip,rk3399-mipi-dsi", "snps,dw-mipi-dsi".
|
||||
- reg: Represent the physical address range of the controller.
|
||||
- interrupts: Represent the controller's interrupt to the CPU(s).
|
||||
- power-domains: a phandle to mipi dsi power domain node.
|
||||
- resets: list of phandle + reset specifier pairs, as described in [3].
|
||||
- reset-names: string reset name, must be "apb".
|
||||
- clocks, clock-names: Phandles to the controller's pll reference
|
||||
clock(ref) and APB clock(pclk). For RK3399, a phy config clock
|
||||
(phy_cfg) and a grf clock(grf) are required. As described in [1].
|
||||
- clocks, clock-names: Phandles to the controller's APB clock(pclk),
|
||||
As described in [1].
|
||||
- rockchip,grf: this soc should set GRF regs to mux vopl/vopb.
|
||||
- ports: contain a port node with endpoint definitions as defined in [2].
|
||||
For vopb,set the reg = <0> and set the reg = <1> for vopl.
|
||||
|
||||
Optional properties:
|
||||
- clocks, clock-names:
|
||||
phandle to the SNPS-PHY config clock, name should be "phy_cfg".
|
||||
phandle to the SNPS-PHY PLL reference clock, name should be "ref".
|
||||
phandle to the Non-SNPS PHY high speed clock, name should be "hs_clk".
|
||||
- phys: phandle to Non-SNPS PHY node
|
||||
- phy-names: the string "mipi_dphy" when is found in a node, along with "phys"
|
||||
attribute, provides phandle to MIPI PHY node
|
||||
- rockchip,lane-rate: optional override of the desired bandwidth.
|
||||
- rockchip,dual-channel: for dual-channel mode, phandle to the slave channel.
|
||||
|
||||
|
|
@ -27,7 +38,7 @@ Optional properties:
|
|||
[3] Documentation/devicetree/bindings/reset/reset.txt
|
||||
|
||||
Example:
|
||||
mipi_dsi: mipi@ff960000 {
|
||||
dsi: dsi@ff960000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi";
|
||||
|
|
@ -44,16 +55,18 @@ Example:
|
|||
#size-cells = <0>;
|
||||
reg = <1>;
|
||||
|
||||
mipi_in: port {
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
mipi_in_vopb: endpoint@0 {
|
||||
|
||||
dsi_in_vopb: endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&vopb_out_mipi>;
|
||||
remote-endpoint = <&vopb_out_dsi>;
|
||||
};
|
||||
mipi_in_vopl: endpoint@1 {
|
||||
|
||||
dsi_in_vopl: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&vopl_out_mipi>;
|
||||
remote-endpoint = <&vopl_out_dsi>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
|
@ -236,11 +237,16 @@ struct dw_mipi_dsi_plat_data {
|
|||
};
|
||||
|
||||
struct mipi_dphy {
|
||||
/* SNPS PHY */
|
||||
struct regmap *regmap;
|
||||
struct clk *ref_clk;
|
||||
struct clk *cfg_clk;
|
||||
u16 input_div;
|
||||
u16 feedback_div;
|
||||
|
||||
/* Non-SNPS PHY */
|
||||
struct phy *phy;
|
||||
struct clk *hs_clk;
|
||||
};
|
||||
|
||||
struct dw_mipi_dsi {
|
||||
|
|
@ -502,7 +508,7 @@ static inline void mipi_dphy_rstz_deassert(struct dw_mipi_dsi *dsi)
|
|||
udelay(1);
|
||||
}
|
||||
|
||||
static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
|
||||
static void dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
struct mipi_dphy *dphy = &dsi->dphy;
|
||||
const struct {
|
||||
|
|
@ -523,28 +529,6 @@ static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
|
|||
u8 hsfreqrange, counter;
|
||||
unsigned int index, txbyteclkhs;
|
||||
u16 n, m;
|
||||
unsigned int val, mask;
|
||||
int ret;
|
||||
|
||||
mipi_dphy_enableclk_deassert(dsi);
|
||||
mipi_dphy_shutdownz_assert(dsi);
|
||||
mipi_dphy_rstz_assert(dsi);
|
||||
testif_testclr_assert(dsi);
|
||||
|
||||
/* Configures DPHY to work as a Master */
|
||||
grf_field_write(dsi, MASTERSLAVEZ, 1);
|
||||
|
||||
/* Configures lane as TX */
|
||||
grf_field_write(dsi, BASEDIR, 0);
|
||||
|
||||
/* Set all REQUEST inputs to zero */
|
||||
grf_field_write(dsi, TURNREQUEST, 0);
|
||||
grf_field_write(dsi, TURNDISABLE, 0);
|
||||
grf_field_write(dsi, FORCETXSTOPMODE, 0);
|
||||
grf_field_write(dsi, FORCERXMODE, 0);
|
||||
udelay(1);
|
||||
|
||||
testif_testclr_deassert(dsi);
|
||||
|
||||
for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
|
||||
if (dsi->lane_mbps <= hsfreqrange_table[index].max_lane_mbps)
|
||||
|
|
@ -568,6 +552,36 @@ static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
|
|||
regmap_write(dphy->regmap, 0x17, INPUT_DIV(n));
|
||||
regmap_write(dphy->regmap, 0x18, FEEDBACK_DIV_LO(m));
|
||||
regmap_write(dphy->regmap, 0x18, FEEDBACK_DIV_HI(m >> 5));
|
||||
}
|
||||
|
||||
static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
struct mipi_dphy *dphy = &dsi->dphy;
|
||||
unsigned int val, mask;
|
||||
int ret;
|
||||
|
||||
mipi_dphy_enableclk_deassert(dsi);
|
||||
mipi_dphy_shutdownz_assert(dsi);
|
||||
mipi_dphy_rstz_assert(dsi);
|
||||
testif_testclr_assert(dsi);
|
||||
|
||||
/* Configures DPHY to work as a Master */
|
||||
grf_field_write(dsi, MASTERSLAVEZ, 1);
|
||||
|
||||
/* Configures lane as TX */
|
||||
grf_field_write(dsi, BASEDIR, 0);
|
||||
|
||||
/* Set all REQUEST inputs to zero */
|
||||
grf_field_write(dsi, TURNREQUEST, 0);
|
||||
grf_field_write(dsi, TURNDISABLE, 0);
|
||||
grf_field_write(dsi, FORCETXSTOPMODE, 0);
|
||||
grf_field_write(dsi, FORCERXMODE, 0);
|
||||
udelay(1);
|
||||
|
||||
testif_testclr_deassert(dsi);
|
||||
|
||||
if (!dphy->phy)
|
||||
dw_mipi_dsi_phy_init(dsi);
|
||||
|
||||
/* Enable Data Lane Module */
|
||||
grf_field_write(dsi, ENABLE_N, GENMASK(dsi->lanes - 1, 0));
|
||||
|
|
@ -580,6 +594,17 @@ static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
|
|||
mipi_dphy_rstz_deassert(dsi);
|
||||
usleep_range(1500, 2000);
|
||||
|
||||
if (dphy->phy) {
|
||||
ret = phy_set_mode(dphy->phy, PHY_MODE_VIDEO_MIPI);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dsi->dev, "failed to set phy mode: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_power_on(dphy->phy);
|
||||
}
|
||||
|
||||
ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS,
|
||||
val, val & PHY_LOCK, 0, 1000);
|
||||
if (ret < 0) {
|
||||
|
|
@ -605,7 +630,12 @@ static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
|
|||
|
||||
static void mipi_dphy_power_off(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
struct mipi_dphy *dphy = &dsi->dphy;
|
||||
|
||||
regmap_write(dsi->regmap, DSI_PHY_RSTZ, 0);
|
||||
|
||||
if (dphy->phy)
|
||||
phy_power_off(dphy->phy);
|
||||
}
|
||||
|
||||
static const struct regmap_config testif_regmap_config = {
|
||||
|
|
@ -624,31 +654,52 @@ static int mipi_dphy_attach(struct dw_mipi_dsi *dsi)
|
|||
struct device *dev = dsi->dev;
|
||||
int ret;
|
||||
|
||||
dphy->ref_clk = devm_clk_get(dev, "ref");
|
||||
if (IS_ERR(dphy->ref_clk)) {
|
||||
ret = PTR_ERR(dphy->ref_clk);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get pll reference clock: %d\n", ret);
|
||||
dphy->phy = devm_phy_optional_get(dev, "mipi_dphy");
|
||||
if (IS_ERR(dphy->phy)) {
|
||||
ret = PTR_ERR(dphy->phy);
|
||||
DRM_DEV_ERROR(dev, "failed to get mipi dphy: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dphy->cfg_clk = devm_clk_get(dev, "phy_cfg");
|
||||
if (IS_ERR(dphy->cfg_clk)) {
|
||||
if (PTR_ERR(dphy->cfg_clk) != -ENOENT) {
|
||||
ret = PTR_ERR(dphy->cfg_clk);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get phy cfg clk: %d\n", ret);
|
||||
if (dphy->phy) {
|
||||
dphy->hs_clk = devm_clk_get(dev, "hs_clk");
|
||||
if (IS_ERR(dphy->hs_clk)) {
|
||||
ret = PTR_ERR(dphy->hs_clk);
|
||||
DRM_DEV_ERROR(dev, "failed to get hs clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dphy->ref_clk = devm_clk_get(dev, "ref");
|
||||
if (IS_ERR(dphy->ref_clk)) {
|
||||
ret = PTR_ERR(dphy->ref_clk);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get pll reference clock: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
/* Clock is optional (for RK3288) */
|
||||
dphy->cfg_clk = NULL;
|
||||
}
|
||||
|
||||
dphy->regmap = devm_regmap_init(dev, NULL, dsi, &testif_regmap_config);
|
||||
if (IS_ERR(dphy->regmap)) {
|
||||
ret = PTR_ERR(dphy->regmap);
|
||||
DRM_DEV_ERROR(dev, "failed to int phy regmap: %d\n", ret);
|
||||
return ret;
|
||||
dphy->cfg_clk = devm_clk_get(dev, "phy_cfg");
|
||||
if (IS_ERR(dphy->cfg_clk)) {
|
||||
if (PTR_ERR(dphy->cfg_clk) != -ENOENT) {
|
||||
ret = PTR_ERR(dphy->cfg_clk);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get phy cfg clk: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clock is optional (for RK3288) */
|
||||
dphy->cfg_clk = NULL;
|
||||
}
|
||||
|
||||
dphy->regmap = devm_regmap_init(dev, NULL, dsi,
|
||||
&testif_regmap_config);
|
||||
if (IS_ERR(dphy->regmap)) {
|
||||
ret = PTR_ERR(dphy->regmap);
|
||||
DRM_DEV_ERROR(dev, "failed to int phy regmap: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1235,12 +1286,23 @@ static void dw_mipi_dsi_enable(struct dw_mipi_dsi *dsi)
|
|||
dw_mipi_dsi_enable(dsi->slave);
|
||||
}
|
||||
|
||||
static void dw_mipi_dsi_set_hs_clk(struct dw_mipi_dsi *dsi, unsigned long rate)
|
||||
{
|
||||
rate = clk_round_rate(dsi->dphy.hs_clk, rate);
|
||||
clk_set_rate(dsi->dphy.hs_clk, rate);
|
||||
dsi->lane_mbps = rate / USEC_PER_SEC;
|
||||
}
|
||||
|
||||
static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
|
||||
unsigned long lane_rate = dw_mipi_dsi_get_lane_rate(dsi);
|
||||
|
||||
dw_mipi_dsi_calc_pll_cfg(dsi, lane_rate);
|
||||
if (dsi->dphy.phy)
|
||||
dw_mipi_dsi_set_hs_clk(dsi, lane_rate);
|
||||
else
|
||||
dw_mipi_dsi_calc_pll_cfg(dsi, lane_rate);
|
||||
|
||||
DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n",
|
||||
dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes);
|
||||
|
||||
|
|
@ -1615,6 +1677,7 @@ static int __maybe_unused dw_mipi_dsi_runtime_suspend(struct device *dev)
|
|||
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(dsi->pclk);
|
||||
clk_disable_unprepare(dsi->dphy.hs_clk);
|
||||
clk_disable_unprepare(dsi->dphy.ref_clk);
|
||||
clk_disable_unprepare(dsi->dphy.cfg_clk);
|
||||
|
||||
|
|
@ -1627,6 +1690,7 @@ static int __maybe_unused dw_mipi_dsi_runtime_resume(struct device *dev)
|
|||
|
||||
clk_prepare_enable(dsi->dphy.cfg_clk);
|
||||
clk_prepare_enable(dsi->dphy.ref_clk);
|
||||
clk_prepare_enable(dsi->dphy.hs_clk);
|
||||
clk_prepare_enable(dsi->pclk);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1637,6 +1701,49 @@ static const struct dev_pm_ops dw_mipi_dsi_pm_ops = {
|
|||
dw_mipi_dsi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const u32 px30_dsi_grf_reg_fields[MAX_FIELDS] = {
|
||||
[DPIUPDATECFG] = GRF_REG_FIELD(0x0434, 7, 7),
|
||||
[DPICOLORM] = GRF_REG_FIELD(0x0434, 3, 3),
|
||||
[DPISHUTDN] = GRF_REG_FIELD(0x0434, 2, 2),
|
||||
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0438, 7, 10),
|
||||
[FORCERXMODE] = GRF_REG_FIELD(0x0438, 6, 6),
|
||||
[TURNDISABLE] = GRF_REG_FIELD(0x0438, 5, 5),
|
||||
[VOPSEL] = GRF_REG_FIELD(0x0438, 0, 0),
|
||||
};
|
||||
|
||||
static const struct dw_mipi_dsi_plat_data px30_mipi_dsi_plat_data = {
|
||||
.dsi0_grf_reg_fields = px30_dsi_grf_reg_fields,
|
||||
.max_bit_rate_per_lane = 1000000000UL,
|
||||
};
|
||||
|
||||
static const u32 rk1808_dsi_grf_reg_fields[MAX_FIELDS] = {
|
||||
[MASTERSLAVEZ] = GRF_REG_FIELD(0x0440, 8, 8),
|
||||
[DPIUPDATECFG] = GRF_REG_FIELD(0x0440, 7, 7),
|
||||
[DPICOLORM] = GRF_REG_FIELD(0x0440, 3, 3),
|
||||
[DPISHUTDN] = GRF_REG_FIELD(0x0440, 2, 2),
|
||||
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0444, 7, 10),
|
||||
[FORCERXMODE] = GRF_REG_FIELD(0x0444, 6, 6),
|
||||
[TURNDISABLE] = GRF_REG_FIELD(0x0444, 5, 5),
|
||||
};
|
||||
|
||||
static const struct dw_mipi_dsi_plat_data rk1808_mipi_dsi_plat_data = {
|
||||
.dsi0_grf_reg_fields = rk1808_dsi_grf_reg_fields,
|
||||
.max_bit_rate_per_lane = 2000000000UL,
|
||||
};
|
||||
|
||||
static const u32 rk3128_dsi_grf_reg_fields[MAX_FIELDS] = {
|
||||
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0150, 10, 13),
|
||||
[FORCERXMODE] = GRF_REG_FIELD(0x0150, 9, 9),
|
||||
[TURNDISABLE] = GRF_REG_FIELD(0x0150, 8, 8),
|
||||
[DPICOLORM] = GRF_REG_FIELD(0x0150, 5, 5),
|
||||
[DPISHUTDN] = GRF_REG_FIELD(0x0150, 4, 4),
|
||||
};
|
||||
|
||||
static const struct dw_mipi_dsi_plat_data rk3128_mipi_dsi_plat_data = {
|
||||
.dsi0_grf_reg_fields = rk3128_dsi_grf_reg_fields,
|
||||
.max_bit_rate_per_lane = 1000000000UL,
|
||||
};
|
||||
|
||||
static const u32 rk3288_dsi0_grf_reg_fields[MAX_FIELDS] = {
|
||||
[DPICOLORM] = GRF_REG_FIELD(0x025c, 8, 8),
|
||||
[DPISHUTDN] = GRF_REG_FIELD(0x025c, 7, 7),
|
||||
|
|
@ -1669,6 +1776,20 @@ static const struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_plat_data = {
|
|||
.max_bit_rate_per_lane = 1500000000UL,
|
||||
};
|
||||
|
||||
static const u32 rk3368_dsi_grf_reg_fields[MAX_FIELDS] = {
|
||||
[DPIUPDATECFG] = GRF_REG_FIELD(0x0418, 7, 7),
|
||||
[DPICOLORM] = GRF_REG_FIELD(0x0418, 3, 3),
|
||||
[DPISHUTDN] = GRF_REG_FIELD(0x0418, 2, 2),
|
||||
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x041c, 7, 10),
|
||||
[FORCERXMODE] = GRF_REG_FIELD(0x041c, 6, 6),
|
||||
[TURNDISABLE] = GRF_REG_FIELD(0x041c, 5, 5),
|
||||
};
|
||||
|
||||
static const struct dw_mipi_dsi_plat_data rk3368_mipi_dsi_plat_data = {
|
||||
.dsi0_grf_reg_fields = rk3368_dsi_grf_reg_fields,
|
||||
.max_bit_rate_per_lane = 1000000000UL,
|
||||
};
|
||||
|
||||
static const u32 rk3399_dsi0_grf_reg_fields[MAX_FIELDS] = {
|
||||
[DPIUPDATECFG] = GRF_REG_FIELD(0x6224, 15, 15),
|
||||
[DPISHUTDN] = GRF_REG_FIELD(0x6224, 14, 14),
|
||||
|
|
@ -1703,8 +1824,20 @@ static const struct dw_mipi_dsi_plat_data rk3399_mipi_dsi_plat_data = {
|
|||
|
||||
static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,px30-mipi-dsi",
|
||||
.data = &px30_mipi_dsi_plat_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk1808-mipi-dsi",
|
||||
.data = &rk1808_mipi_dsi_plat_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3128-mipi-dsi",
|
||||
.data = &rk3128_mipi_dsi_plat_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3288-mipi-dsi",
|
||||
.data = &rk3288_mipi_dsi_plat_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3368-mipi-dsi",
|
||||
.data = &rk3368_mipi_dsi_plat_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3399-mipi-dsi",
|
||||
.data = &rk3399_mipi_dsi_plat_data,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user