This series adds support for the dsi controller on the px30.
The main difference to previous incarnations is the use of an
external dphy for the output.
changes in v2:
- drop handling the dphy-pll manually, instead use the regular
phy configuration operations, thanks Laurent for the suggestion
- add missing px30 compatible to the binding and make
binding changes separate patches
Heiko Stuebner (5):
drm/bridge/synopsys: dsi: move phy_ops callbacks around panel
enablement
dt-bindings: display: rockchip-dsi: document external phys
drm/rockchip: add ability to handle external dphys in mipi-dsi
dt-bindings: display: rockchip-dsi: add px30 compatible
drm/rockchip: dsi: add px30 support
.../display/rockchip/dw_mipi_dsi_rockchip.txt | 13 ++-
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 13 ++-
.../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 95 ++++++++++++++++++-
3 files changed, 106 insertions(+), 15 deletions(-)
--
2.23.0
The px30 SoC also uses a dw-mipi-dsi controller, so add the
compatible value for it.
Signed-off-by: Heiko Stuebner <[email protected]>
---
.../bindings/display/rockchip/dw_mipi_dsi_rockchip.txt | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
index 1ba9237d0ac0..151be3bba06f 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
@@ -4,8 +4,10 @@ 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: one of
+ "rockchip,px30-mipi-dsi", "snps,dw-mipi-dsi"
+ "rockchip,rk3288-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).
- clocks, clock-names: Phandles to the controller's pll reference
--
2.23.0
While the common case is that the dsi controller uses an internal dphy,
accessed through the phy registers inside the dsi controller, there is
also the possibility to use a separate dphy from a different vendor.
One such case is the Rockchip px30 that uses a Innosilicon Mipi dphy,
so add the support for handling such a constellation, including the pll
also getting generated inside that external phy.
Signed-off-by: Heiko Stuebner <[email protected]>
---
.../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 68 +++++++++++++++++--
1 file changed, 64 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index bc073ec5c183..1e6578f911a0 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -12,6 +12,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -223,6 +224,10 @@ struct dw_mipi_dsi_rockchip {
bool is_slave;
struct dw_mipi_dsi_rockchip *slave;
+ /* optional external dphy */
+ struct phy *phy;
+ union phy_configure_opts phy_opts;
+
unsigned int lane_mbps; /* per lane */
u16 input_div;
u16 feedback_div;
@@ -359,6 +364,9 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
struct dw_mipi_dsi_rockchip *dsi = priv_data;
int ret, i, vco;
+ if (dsi->phy)
+ return 0;
+
/*
* Get vco from frequency(lane_mbps)
* vco frequency table
@@ -467,6 +475,28 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
return ret;
}
+static void dw_mipi_dsi_phy_power_on(void *priv_data)
+{
+ struct dw_mipi_dsi_rockchip *dsi = priv_data;
+ int ret;
+
+ ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY);
+ if (ret) {
+ DRM_DEV_ERROR(dsi->dev, "failed to set phy mode: %d\n", ret);
+ return;
+ }
+
+ phy_configure(dsi->phy, &dsi->phy_opts);
+ phy_power_on(dsi->phy);
+}
+
+static void dw_mipi_dsi_phy_power_off(void *priv_data)
+{
+ struct dw_mipi_dsi_rockchip *dsi = priv_data;
+
+ phy_power_off(dsi->phy);
+}
+
static int
dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
unsigned long mode_flags, u32 lanes, u32 format,
@@ -504,6 +534,17 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
"DPHY clock frequency is out of range\n");
}
+ /* for external phy only a the mipi_dphy_config is necessary */
+ if (dsi->phy) {
+ phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8,
+ bpp, lanes,
+ &dsi->phy_opts.mipi_dphy);
+ dsi->lane_mbps = target_mbps;
+ *lane_mbps = dsi->lane_mbps;
+
+ return 0;
+ }
+
fin = clk_get_rate(dsi->pllref_clk);
fout = target_mbps * USEC_PER_SEC;
@@ -561,6 +602,8 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = {
.init = dw_mipi_dsi_phy_init,
+ .power_on = dw_mipi_dsi_phy_power_on,
+ .power_off = dw_mipi_dsi_phy_power_off,
.get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
};
@@ -920,12 +963,29 @@ static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
return -EINVAL;
}
+ /* try to get a possible external dphy */
+ dsi->phy = devm_phy_optional_get(dev, "dphy");
+ if (IS_ERR(dsi->phy)) {
+ ret = PTR_ERR(dsi->phy);
+ DRM_DEV_ERROR(dev, "failed to get mipi dphy: %d\n", ret);
+ return ret;
+ }
+
dsi->pllref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(dsi->pllref_clk)) {
- ret = PTR_ERR(dsi->pllref_clk);
- DRM_DEV_ERROR(dev,
- "Unable to get pll reference clock: %d\n", ret);
- return ret;
+ if (dsi->phy) {
+ /*
+ * if external phy is present, pll will be
+ * generated there.
+ */
+ dsi->pllref_clk = NULL;
+ } else {
+ ret = PTR_ERR(dsi->pllref_clk);
+ DRM_DEV_ERROR(dev,
+ "Unable to get pll reference clock: %d\n",
+ ret);
+ return ret;
+ }
}
if (dsi->cdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
--
2.23.0
If implementation-specific phy_ops need to be defined they probably
should be enabled before trying to talk to the panel and disabled only
after the panel was disabled.
Right now they are enabled last and disabled first, so might make it
impossible to talk to some panels - example for this being the px30
with an external Innosilicon dphy that needs the phy to be enabled
to transfer commands to the panel.
So move the calls appropriately.
Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 675442bfc1bd..49f5600a1dea 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -797,9 +797,6 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
- if (phy_ops->power_off)
- phy_ops->power_off(dsi->plat_data->priv_data);
-
/*
* Switch to command mode before panel-bridge post_disable &
* panel unprepare.
@@ -816,6 +813,9 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
*/
dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
+ if (phy_ops->power_off)
+ phy_ops->power_off(dsi->plat_data->priv_data);
+
if (dsi->slave) {
dw_mipi_dsi_disable(dsi->slave);
clk_disable_unprepare(dsi->slave->pclk);
@@ -882,6 +882,9 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
/* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
dw_mipi_dsi_set_mode(dsi, 0);
+
+ if (phy_ops->power_on)
+ phy_ops->power_on(dsi->plat_data->priv_data);
}
static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
@@ -898,15 +901,11 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
- const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
/* Switch to video mode for panel-bridge enable & panel enable */
dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
if (dsi->slave)
dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
-
- if (phy_ops->power_on)
- phy_ops->power_on(dsi->plat_data->priv_data);
}
static enum drm_mode_status
--
2.23.0
Some dw-mipi-dsi instances in Rockchip SoCs use external dphys.
In these cases the needs clock will also be generated externally
so these don't need the ref-clock as well.
Signed-off-by: Heiko Stuebner <[email protected]>
---
.../bindings/display/rockchip/dw_mipi_dsi_rockchip.txt | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
index ce4c1fc9116c..1ba9237d0ac0 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
@@ -9,8 +9,9 @@ Required properties:
- reg: Represent the physical address range of the controller.
- interrupts: Represent the controller's interrupt to the CPU(s).
- 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].
+ clock(ref) when using an internal dphy and APB clock(pclk).
+ For RK3399, a phy config clock (phy_cfg) and a grf clock(grf)
+ are required. 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.
@@ -18,6 +19,8 @@ Required properties:
- video port 1 for either a panel or subsequent encoder
Optional properties:
+- phys: from general PHY binding: the phandle for the PHY device.
+- phy-names: Should be "dphy" if phys references an external phy.
- 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".
--
2.23.0
Add the compatible and GRF definitions for the PX30 soc.
Signed-off-by: Heiko Stuebner <[email protected]>
---
.../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 1e6578f911a0..13858f377a0c 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -140,6 +140,12 @@
#define DW_MIPI_NEEDS_PHY_CFG_CLK BIT(0)
#define DW_MIPI_NEEDS_GRF_CLK BIT(1)
+#define PX30_GRF_PD_VO_CON1 0x0438
+#define PX30_DSI_FORCETXSTOPMODE (0xf << 7)
+#define PX30_DSI_FORCERXMODE BIT(6)
+#define PX30_DSI_TURNDISABLE BIT(5)
+#define PX30_DSI_LCDC_SEL BIT(0)
+
#define RK3288_GRF_SOC_CON6 0x025c
#define RK3288_DSI0_LCDC_SEL BIT(6)
#define RK3288_DSI1_LCDC_SEL BIT(9)
@@ -1049,6 +1055,24 @@ static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev)
return 0;
}
+static const struct rockchip_dw_dsi_chip_data px30_chip_data[] = {
+ {
+ .reg = 0xff450000,
+ .lcdsel_grf_reg = PX30_GRF_PD_VO_CON1,
+ .lcdsel_big = HIWORD_UPDATE(0, PX30_DSI_LCDC_SEL),
+ .lcdsel_lit = HIWORD_UPDATE(PX30_DSI_LCDC_SEL,
+ PX30_DSI_LCDC_SEL),
+
+ .lanecfg1_grf_reg = PX30_GRF_PD_VO_CON1,
+ .lanecfg1 = HIWORD_UPDATE(0, PX30_DSI_TURNDISABLE |
+ PX30_DSI_FORCERXMODE |
+ PX30_DSI_FORCETXSTOPMODE),
+
+ .max_data_lanes = 4,
+ },
+ { /* sentinel */ }
+};
+
static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
{
.reg = 0xff960000,
@@ -1117,6 +1141,9 @@ static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {
static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = {
{
+ .compatible = "rockchip,px30-mipi-dsi",
+ .data = &px30_chip_data,
+ }, {
.compatible = "rockchip,rk3288-mipi-dsi",
.data = &rk3288_chip_data,
}, {
--
2.23.0
On 08/11/2019 01:02, Heiko Stuebner wrote:
> If implementation-specific phy_ops need to be defined they probably
> should be enabled before trying to talk to the panel and disabled only
> after the panel was disabled.
>
> Right now they are enabled last and disabled first, so might make it
> impossible to talk to some panels - example for this being the px30
> with an external Innosilicon dphy that needs the phy to be enabled
> to transfer commands to the panel.
>
> So move the calls appropriately.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 13 ++++++-------
> 1 file changed, 6 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index 675442bfc1bd..49f5600a1dea 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -797,9 +797,6 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
> struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
>
> - if (phy_ops->power_off)
> - phy_ops->power_off(dsi->plat_data->priv_data);
> -
> /*
> * Switch to command mode before panel-bridge post_disable &
> * panel unprepare.
> @@ -816,6 +813,9 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
> */
> dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
>
> + if (phy_ops->power_off)
> + phy_ops->power_off(dsi->plat_data->priv_data);
> +
> if (dsi->slave) {
> dw_mipi_dsi_disable(dsi->slave);
> clk_disable_unprepare(dsi->slave->pclk);
> @@ -882,6 +882,9 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
>
> /* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
> dw_mipi_dsi_set_mode(dsi, 0);
> +
> + if (phy_ops->power_on)
> + phy_ops->power_on(dsi->plat_data->priv_data);
> }
>
> static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> @@ -898,15 +901,11 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
> {
> struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> - const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
>
> /* Switch to video mode for panel-bridge enable & panel enable */
> dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
> if (dsi->slave)
> dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
> -
> - if (phy_ops->power_on)
> - phy_ops->power_on(dsi->plat_data->priv_data);
> }
>
> static enum drm_mode_status
>
Reviewed-by: Neil Armstrong <[email protected]>
Hi Heiko,
On 11/8/19 1:02 AM, Heiko Stuebner wrote:
> If implementation-specific phy_ops need to be defined they probably
> should be enabled before trying to talk to the panel and disabled only
> after the panel was disabled.
>
> Right now they are enabled last and disabled first, so might make it
> impossible to talk to some panels - example for this being the px30
> with an external Innosilicon dphy that needs the phy to be enabled
> to transfer commands to the panel.
>
> So move the calls appropriately.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 13 ++++++-------
> 1 file changed, 6 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index 675442bfc1bd..49f5600a1dea 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -797,9 +797,6 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
> struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
>
> - if (phy_ops->power_off)
> - phy_ops->power_off(dsi->plat_data->priv_data);
> -
> /*
> * Switch to command mode before panel-bridge post_disable &
> * panel unprepare.
> @@ -816,6 +813,9 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
> */
> dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
>
> + if (phy_ops->power_off)
> + phy_ops->power_off(dsi->plat_data->priv_data);
> +
> if (dsi->slave) {
> dw_mipi_dsi_disable(dsi->slave);
> clk_disable_unprepare(dsi->slave->pclk);
> @@ -882,6 +882,9 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
>
> /* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
> dw_mipi_dsi_set_mode(dsi, 0);
> +
> + if (phy_ops->power_on)
> + phy_ops->power_on(dsi->plat_data->priv_data);
> }
>
> static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> @@ -898,15 +901,11 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
> {
> struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> - const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
>
> /* Switch to video mode for panel-bridge enable & panel enable */
> dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
> if (dsi->slave)
> dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
> -
> - if (phy_ops->power_on)
> - phy_ops->power_on(dsi->plat_data->priv_data);
> }
>
> static enum drm_mode_status
>
Tested-by: Yannick Fertre <[email protected]>
Reviewed-by: Philippe Cornu <[email protected]>
Many thanks,
Philippe :-)
On Fri, 8 Nov 2019 01:02:50 +0100, Heiko Stuebner wrote:
> Some dw-mipi-dsi instances in Rockchip SoCs use external dphys.
> In these cases the needs clock will also be generated externally
> so these don't need the ref-clock as well.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> .../bindings/display/rockchip/dw_mipi_dsi_rockchip.txt | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
Reviewed-by: Rob Herring <[email protected]>
On Fri, 8 Nov 2019 01:02:52 +0100, Heiko Stuebner wrote:
> The px30 SoC also uses a dw-mipi-dsi controller, so add the
> compatible value for it.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> .../bindings/display/rockchip/dw_mipi_dsi_rockchip.txt | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
Acked-by: Rob Herring <[email protected]>