Some MIPI DSI panel drivers like 'raydium,rm68200' send MIPI_DCS_SET_DISPLAY_ON commands in panel_funcs->prepare(), which requires the MIPI DSI controller and PHY to be ready beforehand. Without this patch, the nwl-dsi driver gets the MIPI DSI controller and PHY ready in bridge_funcs->pre_enable(), which happens after the panel_funcs->prepare(). So, this patch shifts the bridge operation ealier from bridge_funcs->pre_enable() to bridge_funcs->mode_set(). This way, more MIPI DSI panels can connect to this nwl-dsi bridge. Care is taken to make sure bridge_funcs->mode_set()/atomic_disable() are called in pairs, which includes removing a check on unchanged HS clock rate and forcing a full modeset when only connector's DPMS is brought out of "Off" status.
Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Robert Foss robert.foss@linaro.org Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Guido Günther agx@sigxcpu.org Cc: Robert Chiras robert.chiras@nxp.com Cc: NXP Linux Team linux-imx@nxp.com Signed-off-by: Liu Ying victor.liu@nxp.com --- v1->v2: * Fix commit message typo - s/unchange/unchanged/
drivers/gpu/drm/bridge/nwl-dsi.c | 86 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index be6bfc5..229f0b1 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -21,6 +21,7 @@ #include <linux/sys_soc.h> #include <linux/time64.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -661,7 +662,7 @@ static irqreturn_t nwl_dsi_irq_handler(int irq, void *data) return IRQ_HANDLED; }
-static int nwl_dsi_enable(struct nwl_dsi *dsi) +static int nwl_dsi_mode_set(struct nwl_dsi *dsi) { struct device *dev = dsi->dev; union phy_configure_opts *phy_cfg = &dsi->phy_cfg; @@ -748,7 +749,9 @@ static int nwl_dsi_disable(struct nwl_dsi *dsi) return 0; }
-static void nwl_dsi_bridge_disable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -809,17 +812,6 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi, return 0; }
-static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - /* At least LCDIF + NWL needs active high sync */ - adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); - adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); - - return true; -} - static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -837,6 +829,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, return MODE_OK; }
+static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + + /* At least LCDIF + NWL needs active high sync */ + adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); + adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); + + /* + * Do a full modeset if crtc_state->active is changed to be true. + * This ensures our ->mode_set() is called to get the DSI controller + * and the PHY ready to send DCS commands, when only the connector's + * DPMS is brought out of "Off" status. + */ + if (crtc_state->active_changed && crtc_state->active) + crtc_state->mode_changed = true; + + return 0; +} + static void nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, @@ -852,13 +867,6 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, if (ret < 0) return;
- /* - * If hs clock is unchanged, we're all good - all parameters are - * derived from it atm. - */ - if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate) - return; - phy_ref_rate = clk_get_rate(dsi->phy_ref_clk); DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate); /* Save the new desired phy config */ @@ -866,14 +874,8 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); drm_mode_debug_printmodeline(adjusted_mode); -}
-static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) -{ - struct nwl_dsi *dsi = bridge_to_dsi(bridge); - int ret; - - pm_runtime_get_sync(dsi->dev); + pm_runtime_get_sync(dev);
if (clk_prepare_enable(dsi->lcdif_clk) < 0) return; @@ -883,27 +885,29 @@ static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) /* Step 1 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_pclk); if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, "Failed to deassert PCLK: %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret); return; }
/* Step 2 from DSI reset-out instructions */ - nwl_dsi_enable(dsi); + nwl_dsi_mode_set(dsi);
/* Step 3 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_esc); if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, "Failed to deassert ESC: %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret); return; } ret = reset_control_deassert(dsi->rst_byte); if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, "Failed to deassert BYTE: %d\n", ret); + DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret); return; } }
-static void nwl_dsi_bridge_enable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -948,14 +952,16 @@ static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) }
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = { - .pre_enable = nwl_dsi_bridge_pre_enable, - .enable = nwl_dsi_bridge_enable, - .disable = nwl_dsi_bridge_disable, - .mode_fixup = nwl_dsi_bridge_mode_fixup, - .mode_set = nwl_dsi_bridge_mode_set, - .mode_valid = nwl_dsi_bridge_mode_valid, - .attach = nwl_dsi_bridge_attach, - .detach = nwl_dsi_bridge_detach, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_check = nwl_dsi_bridge_atomic_check, + .atomic_enable = nwl_dsi_bridge_atomic_enable, + .atomic_disable = nwl_dsi_bridge_atomic_disable, + .mode_set = nwl_dsi_bridge_mode_set, + .mode_valid = nwl_dsi_bridge_mode_valid, + .attach = nwl_dsi_bridge_attach, + .detach = nwl_dsi_bridge_detach, };
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
Hi,
On 22/04/2021 07:14, Liu Ying wrote:
Some MIPI DSI panel drivers like 'raydium,rm68200' send MIPI_DCS_SET_DISPLAY_ON commands in panel_funcs->prepare(), which requires the MIPI DSI controller and PHY to be ready beforehand. Without this patch, the nwl-dsi driver gets the MIPI DSI controller and PHY ready in bridge_funcs->pre_enable(), which happens after the panel_funcs->prepare(). So, this patch shifts the bridge operation ealier from bridge_funcs->pre_enable() to bridge_funcs->mode_set().
This makes sense, this is how we do on the synopsys dw mipi dsi driver.
This way, more MIPI DSI panels can connect to this nwl-dsi bridge. Care is taken to make sure bridge_funcs->mode_set()/atomic_disable() are called in pairs, which includes removing a check on unchanged HS clock rate and forcing a full modeset when only connector's DPMS is brought out of "Off" status.
I would split the changes in multiple patches to clarify the changes.
Neil
Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Robert Foss robert.foss@linaro.org Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Guido Günther agx@sigxcpu.org Cc: Robert Chiras robert.chiras@nxp.com Cc: NXP Linux Team linux-imx@nxp.com Signed-off-by: Liu Ying victor.liu@nxp.com
v1->v2:
- Fix commit message typo - s/unchange/unchanged/
drivers/gpu/drm/bridge/nwl-dsi.c | 86 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index be6bfc5..229f0b1 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -21,6 +21,7 @@ #include <linux/sys_soc.h> #include <linux/time64.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -661,7 +662,7 @@ static irqreturn_t nwl_dsi_irq_handler(int irq, void *data) return IRQ_HANDLED; }
-static int nwl_dsi_enable(struct nwl_dsi *dsi) +static int nwl_dsi_mode_set(struct nwl_dsi *dsi) { struct device *dev = dsi->dev; union phy_configure_opts *phy_cfg = &dsi->phy_cfg; @@ -748,7 +749,9 @@ static int nwl_dsi_disable(struct nwl_dsi *dsi) return 0; }
-static void nwl_dsi_bridge_disable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -809,17 +812,6 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi, return 0; }
-static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
-{
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- return true;
-}
static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -837,6 +829,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, return MODE_OK; }
+static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- /*
* Do a full modeset if crtc_state->active is changed to be true.
* This ensures our ->mode_set() is called to get the DSI controller
* and the PHY ready to send DCS commands, when only the connector's
* DPMS is brought out of "Off" status.
*/
- if (crtc_state->active_changed && crtc_state->active)
crtc_state->mode_changed = true;
- return 0;
+}
static void nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, @@ -852,13 +867,6 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, if (ret < 0) return;
- /*
* If hs clock is unchanged, we're all good - all parameters are
* derived from it atm.
*/
- if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
return;
- phy_ref_rate = clk_get_rate(dsi->phy_ref_clk); DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate); /* Save the new desired phy config */
@@ -866,14 +874,8 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); drm_mode_debug_printmodeline(adjusted_mode); -}
-static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) -{
- struct nwl_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- pm_runtime_get_sync(dsi->dev);
pm_runtime_get_sync(dev);
if (clk_prepare_enable(dsi->lcdif_clk) < 0) return;
@@ -883,27 +885,29 @@ static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) /* Step 1 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_pclk); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert PCLK: %d\n", ret);
DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret);
return; }
/* Step 2 from DSI reset-out instructions */
- nwl_dsi_enable(dsi);
nwl_dsi_mode_set(dsi);
/* Step 3 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_esc); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert ESC: %d\n", ret);
return; } ret = reset_control_deassert(dsi->rst_byte); if (ret < 0) {DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret);
DRM_DEV_ERROR(dsi->dev, "Failed to deassert BYTE: %d\n", ret);
return; }DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret);
}
-static void nwl_dsi_bridge_enable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -948,14 +952,16 @@ static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) }
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
- .pre_enable = nwl_dsi_bridge_pre_enable,
- .enable = nwl_dsi_bridge_enable,
- .disable = nwl_dsi_bridge_disable,
- .mode_fixup = nwl_dsi_bridge_mode_fixup,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .atomic_check = nwl_dsi_bridge_atomic_check,
- .atomic_enable = nwl_dsi_bridge_atomic_enable,
- .atomic_disable = nwl_dsi_bridge_atomic_disable,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
};
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
Hi Neil,
On Thu, 2021-04-22 at 10:48 +0200, Neil Armstrong wrote:
Hi,
On 22/04/2021 07:14, Liu Ying wrote:
Some MIPI DSI panel drivers like 'raydium,rm68200' send MIPI_DCS_SET_DISPLAY_ON commands in panel_funcs->prepare(), which requires the MIPI DSI controller and PHY to be ready beforehand. Without this patch, the nwl-dsi driver gets the MIPI DSI controller and PHY ready in bridge_funcs->pre_enable(), which happens after the panel_funcs->prepare(). So, this patch shifts the bridge operation ealier from bridge_funcs->pre_enable() to bridge_funcs->mode_set().
This makes sense, this is how we do on the synopsys dw mipi dsi driver.
This way, more MIPI DSI panels can connect to this nwl-dsi bridge. Care is taken to make sure bridge_funcs->mode_set()/atomic_disable() are called in pairs, which includes removing a check on unchanged HS clock rate and forcing a full modeset when only connector's DPMS is brought out of "Off" status.
I would split the changes in multiple patches to clarify the changes.
Maybe, I can split this into the below 3 patches: 1) Remove a check on unchanged HS clock rate from ->mode_set(). 2) Force a full modeset when only connector's DPMS is brought out of "Off" status. This will be done in ->atomic_check() together with the fixup for H/VSYNC polarities. 3) Shift the bridge operation as the last step.
I'll mention that 1) & 2) are needed by 3) in their commit message.
Does this split-up sound reasonable?
Thanks, Liu Ying
Neil
Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Robert Foss robert.foss@linaro.org Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Guido Günther agx@sigxcpu.org Cc: Robert Chiras robert.chiras@nxp.com Cc: NXP Linux Team linux-imx@nxp.com Signed-off-by: Liu Ying victor.liu@nxp.com
v1->v2:
- Fix commit message typo - s/unchange/unchanged/
drivers/gpu/drm/bridge/nwl-dsi.c | 86 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index be6bfc5..229f0b1 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -21,6 +21,7 @@ #include <linux/sys_soc.h> #include <linux/time64.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -661,7 +662,7 @@ static irqreturn_t nwl_dsi_irq_handler(int irq, void *data) return IRQ_HANDLED; }
-static int nwl_dsi_enable(struct nwl_dsi *dsi) +static int nwl_dsi_mode_set(struct nwl_dsi *dsi) { struct device *dev = dsi->dev; union phy_configure_opts *phy_cfg = &dsi->phy_cfg; @@ -748,7 +749,9 @@ static int nwl_dsi_disable(struct nwl_dsi *dsi) return 0; }
-static void nwl_dsi_bridge_disable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -809,17 +812,6 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi, return 0; }
-static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
-{
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- return true;
-}
static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -837,6 +829,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, return MODE_OK; }
+static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- /*
* Do a full modeset if crtc_state->active is changed to be true.
* This ensures our ->mode_set() is called to get the DSI controller
* and the PHY ready to send DCS commands, when only the connector's
* DPMS is brought out of "Off" status.
*/
- if (crtc_state->active_changed && crtc_state->active)
crtc_state->mode_changed = true;
- return 0;
+}
static void nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, @@ -852,13 +867,6 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, if (ret < 0) return;
- /*
* If hs clock is unchanged, we're all good - all parameters are
* derived from it atm.
*/
- if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
return;
- phy_ref_rate = clk_get_rate(dsi->phy_ref_clk); DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate); /* Save the new desired phy config */
@@ -866,14 +874,8 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); drm_mode_debug_printmodeline(adjusted_mode); -}
-static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) -{
- struct nwl_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- pm_runtime_get_sync(dsi->dev);
pm_runtime_get_sync(dev);
if (clk_prepare_enable(dsi->lcdif_clk) < 0) return;
@@ -883,27 +885,29 @@ static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) /* Step 1 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_pclk); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert PCLK: %d\n", ret);
DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret);
return; }
/* Step 2 from DSI reset-out instructions */
- nwl_dsi_enable(dsi);
nwl_dsi_mode_set(dsi);
/* Step 3 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_esc); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert ESC: %d\n", ret);
return; } ret = reset_control_deassert(dsi->rst_byte); if (ret < 0) {DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret);
DRM_DEV_ERROR(dsi->dev, "Failed to deassert BYTE: %d\n", ret);
return; }DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret);
}
-static void nwl_dsi_bridge_enable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -948,14 +952,16 @@ static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) }
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
- .pre_enable = nwl_dsi_bridge_pre_enable,
- .enable = nwl_dsi_bridge_enable,
- .disable = nwl_dsi_bridge_disable,
- .mode_fixup = nwl_dsi_bridge_mode_fixup,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .atomic_check = nwl_dsi_bridge_atomic_check,
- .atomic_enable = nwl_dsi_bridge_atomic_enable,
- .atomic_disable = nwl_dsi_bridge_atomic_disable,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
};
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
Hi,
On 22/04/2021 11:31, Liu Ying wrote:
Hi Neil,
On Thu, 2021-04-22 at 10:48 +0200, Neil Armstrong wrote:
Hi,
On 22/04/2021 07:14, Liu Ying wrote:
Some MIPI DSI panel drivers like 'raydium,rm68200' send MIPI_DCS_SET_DISPLAY_ON commands in panel_funcs->prepare(), which requires the MIPI DSI controller and PHY to be ready beforehand. Without this patch, the nwl-dsi driver gets the MIPI DSI controller and PHY ready in bridge_funcs->pre_enable(), which happens after the panel_funcs->prepare(). So, this patch shifts the bridge operation ealier from bridge_funcs->pre_enable() to bridge_funcs->mode_set().
This makes sense, this is how we do on the synopsys dw mipi dsi driver.
This way, more MIPI DSI panels can connect to this nwl-dsi bridge. Care is taken to make sure bridge_funcs->mode_set()/atomic_disable() are called in pairs, which includes removing a check on unchanged HS clock rate and forcing a full modeset when only connector's DPMS is brought out of "Off" status.
I would split the changes in multiple patches to clarify the changes.
Maybe, I can split this into the below 3 patches:
- Remove a check on unchanged HS clock rate from ->mode_set().
- Force a full modeset when only connector's DPMS is brought out of
"Off" status. This will be done in ->atomic_check() together with the fixup for H/VSYNC polarities. 3) Shift the bridge operation as the last step.
I'll mention that 1) & 2) are needed by 3) in their commit message.
Sure, you can also put this split explanation in the cover letter.
Does this split-up sound reasonable?
Yes makes sense, thanks
Neil
Thanks, Liu Ying
Neil
Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Robert Foss robert.foss@linaro.org Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Guido Günther agx@sigxcpu.org Cc: Robert Chiras robert.chiras@nxp.com Cc: NXP Linux Team linux-imx@nxp.com Signed-off-by: Liu Ying victor.liu@nxp.com
v1->v2:
- Fix commit message typo - s/unchange/unchanged/
drivers/gpu/drm/bridge/nwl-dsi.c | 86 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index be6bfc5..229f0b1 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -21,6 +21,7 @@ #include <linux/sys_soc.h> #include <linux/time64.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -661,7 +662,7 @@ static irqreturn_t nwl_dsi_irq_handler(int irq, void *data) return IRQ_HANDLED; }
-static int nwl_dsi_enable(struct nwl_dsi *dsi) +static int nwl_dsi_mode_set(struct nwl_dsi *dsi) { struct device *dev = dsi->dev; union phy_configure_opts *phy_cfg = &dsi->phy_cfg; @@ -748,7 +749,9 @@ static int nwl_dsi_disable(struct nwl_dsi *dsi) return 0; }
-static void nwl_dsi_bridge_disable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -809,17 +812,6 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi, return 0; }
-static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
-{
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- return true;
-}
static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -837,6 +829,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, return MODE_OK; }
+static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- /*
* Do a full modeset if crtc_state->active is changed to be true.
* This ensures our ->mode_set() is called to get the DSI controller
* and the PHY ready to send DCS commands, when only the connector's
* DPMS is brought out of "Off" status.
*/
- if (crtc_state->active_changed && crtc_state->active)
crtc_state->mode_changed = true;
- return 0;
+}
static void nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, @@ -852,13 +867,6 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, if (ret < 0) return;
- /*
* If hs clock is unchanged, we're all good - all parameters are
* derived from it atm.
*/
- if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
return;
- phy_ref_rate = clk_get_rate(dsi->phy_ref_clk); DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate); /* Save the new desired phy config */
@@ -866,14 +874,8 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); drm_mode_debug_printmodeline(adjusted_mode); -}
-static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) -{
- struct nwl_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- pm_runtime_get_sync(dsi->dev);
pm_runtime_get_sync(dev);
if (clk_prepare_enable(dsi->lcdif_clk) < 0) return;
@@ -883,27 +885,29 @@ static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) /* Step 1 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_pclk); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert PCLK: %d\n", ret);
DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret);
return; }
/* Step 2 from DSI reset-out instructions */
- nwl_dsi_enable(dsi);
nwl_dsi_mode_set(dsi);
/* Step 3 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_esc); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert ESC: %d\n", ret);
return; } ret = reset_control_deassert(dsi->rst_byte); if (ret < 0) {DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret);
DRM_DEV_ERROR(dsi->dev, "Failed to deassert BYTE: %d\n", ret);
return; }DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret);
}
-static void nwl_dsi_bridge_enable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -948,14 +952,16 @@ static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) }
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
- .pre_enable = nwl_dsi_bridge_pre_enable,
- .enable = nwl_dsi_bridge_enable,
- .disable = nwl_dsi_bridge_disable,
- .mode_fixup = nwl_dsi_bridge_mode_fixup,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .atomic_check = nwl_dsi_bridge_atomic_check,
- .atomic_enable = nwl_dsi_bridge_atomic_enable,
- .atomic_disable = nwl_dsi_bridge_atomic_disable,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
};
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
Hi Neil,
On Thu, 2021-04-22 at 14:07 +0200, Neil Armstrong wrote:
Hi,
On 22/04/2021 11:31, Liu Ying wrote:
Hi Neil,
On Thu, 2021-04-22 at 10:48 +0200, Neil Armstrong wrote:
Hi,
On 22/04/2021 07:14, Liu Ying wrote:
Some MIPI DSI panel drivers like 'raydium,rm68200' send MIPI_DCS_SET_DISPLAY_ON commands in panel_funcs->prepare(), which requires the MIPI DSI controller and PHY to be ready beforehand. Without this patch, the nwl-dsi driver gets the MIPI DSI controller and PHY ready in bridge_funcs->pre_enable(), which happens after the panel_funcs->prepare(). So, this patch shifts the bridge operation ealier from bridge_funcs->pre_enable() to bridge_funcs->mode_set().
This makes sense, this is how we do on the synopsys dw mipi dsi driver.
This way, more MIPI DSI panels can connect to this nwl-dsi bridge. Care is taken to make sure bridge_funcs->mode_set()/atomic_disable() are called in pairs, which includes removing a check on unchanged HS clock rate and forcing a full modeset when only connector's DPMS is brought out of "Off" status.
I would split the changes in multiple patches to clarify the changes.
Maybe, I can split this into the below 3 patches:
- Remove a check on unchanged HS clock rate from ->mode_set().
- Force a full modeset when only connector's DPMS is brought out of
"Off" status. This will be done in ->atomic_check() together with the fixup for H/VSYNC polarities. 3) Shift the bridge operation as the last step.
I'll mention that 1) & 2) are needed by 3) in their commit message.
Sure, you can also put this split explanation in the cover letter.
Does this split-up sound reasonable?
Yes makes sense, thanks
I've sent v3 to do the split-up. I actually swap 1) and 2), which is trivial I assume.
Regards, Liu Ying
Neil
Thanks, Liu Ying
Neil
Cc: Andrzej Hajda a.hajda@samsung.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Robert Foss robert.foss@linaro.org Cc: Laurent Pinchart Laurent.pinchart@ideasonboard.com Cc: Jonas Karlman jonas@kwiboo.se Cc: Jernej Skrabec jernej.skrabec@siol.net Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Guido Günther agx@sigxcpu.org Cc: Robert Chiras robert.chiras@nxp.com Cc: NXP Linux Team linux-imx@nxp.com Signed-off-by: Liu Ying victor.liu@nxp.com
v1->v2:
- Fix commit message typo - s/unchange/unchanged/
drivers/gpu/drm/bridge/nwl-dsi.c | 86 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index be6bfc5..229f0b1 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -21,6 +21,7 @@ #include <linux/sys_soc.h> #include <linux/time64.h>
+#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -661,7 +662,7 @@ static irqreturn_t nwl_dsi_irq_handler(int irq, void *data) return IRQ_HANDLED; }
-static int nwl_dsi_enable(struct nwl_dsi *dsi) +static int nwl_dsi_mode_set(struct nwl_dsi *dsi) { struct device *dev = dsi->dev; union phy_configure_opts *phy_cfg = &dsi->phy_cfg; @@ -748,7 +749,9 @@ static int nwl_dsi_disable(struct nwl_dsi *dsi) return 0; }
-static void nwl_dsi_bridge_disable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -809,17 +812,6 @@ static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi, return 0; }
-static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
-{
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- return true;
-}
static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -837,6 +829,29 @@ nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, return MODE_OK; }
+static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
+{
- struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- /* At least LCDIF + NWL needs active high sync */
- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
- /*
* Do a full modeset if crtc_state->active is changed to be true.
* This ensures our ->mode_set() is called to get the DSI controller
* and the PHY ready to send DCS commands, when only the connector's
* DPMS is brought out of "Off" status.
*/
- if (crtc_state->active_changed && crtc_state->active)
crtc_state->mode_changed = true;
- return 0;
+}
static void nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, @@ -852,13 +867,6 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, if (ret < 0) return;
- /*
* If hs clock is unchanged, we're all good - all parameters are
* derived from it atm.
*/
- if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
return;
- phy_ref_rate = clk_get_rate(dsi->phy_ref_clk); DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate); /* Save the new desired phy config */
@@ -866,14 +874,8 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); drm_mode_debug_printmodeline(adjusted_mode); -}
-static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) -{
- struct nwl_dsi *dsi = bridge_to_dsi(bridge);
- int ret;
- pm_runtime_get_sync(dsi->dev);
pm_runtime_get_sync(dev);
if (clk_prepare_enable(dsi->lcdif_clk) < 0) return;
@@ -883,27 +885,29 @@ static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge) /* Step 1 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_pclk); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert PCLK: %d\n", ret);
DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret);
return; }
/* Step 2 from DSI reset-out instructions */
- nwl_dsi_enable(dsi);
nwl_dsi_mode_set(dsi);
/* Step 3 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_esc); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "Failed to deassert ESC: %d\n", ret);
return; } ret = reset_control_deassert(dsi->rst_byte); if (ret < 0) {DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret);
DRM_DEV_ERROR(dsi->dev, "Failed to deassert BYTE: %d\n", ret);
return; }DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret);
}
-static void nwl_dsi_bridge_enable(struct drm_bridge *bridge) +static void +nwl_dsi_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ struct nwl_dsi *dsi = bridge_to_dsi(bridge); int ret; @@ -948,14 +952,16 @@ static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) }
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
- .pre_enable = nwl_dsi_bridge_pre_enable,
- .enable = nwl_dsi_bridge_enable,
- .disable = nwl_dsi_bridge_disable,
- .mode_fixup = nwl_dsi_bridge_mode_fixup,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .atomic_check = nwl_dsi_bridge_atomic_check,
- .atomic_enable = nwl_dsi_bridge_atomic_enable,
- .atomic_disable = nwl_dsi_bridge_atomic_disable,
- .mode_set = nwl_dsi_bridge_mode_set,
- .mode_valid = nwl_dsi_bridge_mode_valid,
- .attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
};
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
dri-devel@lists.freedesktop.org