[PATCH 4/4 v2] drm/mcde: Enable the DSI link with display

Daniel Vetter daniel at ffwll.ch
Mon Aug 10 09:03:30 EDT 2020


On Sun, Aug 09, 2020 at 12:31:22AM +0200, Linus Walleij wrote:
> The MCDE DSI link hardware which is modeled like a bridge
> in DRM, connected further to the panel bridge, creating
> a pipeline.
> 
> We have been using the .pre_enable(), .enable(),
> .disable() and .post_disable() callbacks from the bridge
> to set this up in a chained manner: first the display
> controller goes online and then in successive order
> each bridge in the pipeline. Inside DRM it works
> like this:
> 
> drm_atomic_helper_commit_tail()
>   drm_atomic_helper_commit_modeset_enables()
>     struct drm_crtc_helper_funcs .atomic_enable()
>       struct drm_simple_display_pipe_funcs .enable()
>         MCDE display enable call
>     drm_atomic_bridge_chain_enable()
>       struct drm_bridge_funcs .pre_enable()
>         mcde_dsi_bridge_pre_enable()
>         panel_bridge_pre_enable()
>           struct drm_panel_funcs .prepare()
>       struct drm_bridge_funcs .enable()
>         mcde_dsi_bridge_enable()
>         panel_bridge_enable()
>           struct drm_panel_funcs .enable()
> 
> A similar sequence is executed for disabling.
> 
> Unfortunately this is not what the hardware needs: at
> a certain stage in the enablement of the display
> controller the DSI link needs to come up to support
> video mode, else something (like a FIFO flow) locks
> up the hardware and we never get picture.
> 
> Fix this by simply leaving the pre|enable and
> post|disable callbacks unused, and establish two
> cross-calls from the display controller to bring up
> the DSI link at the right place in the display
> bring-up sequence and vice versa in the shutdown
> sequence.
> 
> For command mode displays, it works just fine to
> also enable the display flow early. The only time
> we hold it back right now is in one-shot mode,
> on-demand display updates.
> 
> When combined with the previous patch and some patches
> for the S6E63M0 display controller to support DSI
> mode, this gives working display on the Samsung
> GT-I8190 (Golden) phone. It has also been tested working
> on the Samsung GT-S7710 (Skomer) phone.
> 
> Cc: newbytee at protonmail.com
> Cc: Stephan Gerhold <stephan at gerhold.net>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>

So the idea kinda was that if you need this, you'd write your own modeset
code. That's why atomic helpers are fairly modular. Then you can place the
various drm_bridge_enable/disable calls exactly where you need them to
make your upstream blocks happy.

You could even do that with simple pipe helpers by overwriting the
atomic_commit_tail function with your special enable/disable sequence.
Since you have simple hw this would look something like:

static void mcde_helper_commit_tail(struct drm_atomic_state *old_state)
{
	struct drm_device *dev = old_state->dev;
	bool fence_cookie = dma_fence_begin_signalling();

	mcde_display_disable(...)

	drm_atomic_helper_commit_planes(dev, old_state, 0);

	mcde_display_enable(....)

	drm_atomic_helper_fake_vblank(old_state);

	drm_atomic_helper_commit_hw_done(old_state);

	dma_fence_end_signalling(fence_cookie);

	drm_atomic_helper_wait_for_vblanks(dev, old_state);

	drm_atomic_helper_cleanup_planes(dev, old_state);
}

And mcde_display_enable/disable would then call into bridges and anything
else you need to be called.

Anyway just an idea, for patches 2-4:

Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>

> ---
>  drivers/gpu/drm/mcde/mcde_display.c | 36 +++++++++++++++++------
>  drivers/gpu/drm/mcde/mcde_drm.h     |  2 ++
>  drivers/gpu/drm/mcde/mcde_dsi.c     | 44 +++++++++++------------------
>  3 files changed, 47 insertions(+), 35 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
> index d608cc894e01..c271e5bf042e 100644
> --- a/drivers/gpu/drm/mcde/mcde_display.c
> +++ b/drivers/gpu/drm/mcde/mcde_display.c
> @@ -999,6 +999,16 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
>  	mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
>  			    fifo_wtrmrk);
>  
> +	/*
> +	 * This brings up the DSI bridge which is tightly connected
> +	 * to the MCDE DSI formatter.
> +	 *
> +	 * FIXME: if we want to use another formatter, such as DPI,
> +	 * we need to be more elaborate here and select the appropriate
> +	 * bridge.
> +	 */
> +	mcde_dsi_enable(mcde->bridge);
> +
>  	/* Configure the DSI formatter 0 for the DSI panel output */
>  	mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
>  				     formatter_frame, pkt_size);
> @@ -1025,16 +1035,20 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
>  
>  	drm_crtc_vblank_on(crtc);
>  
> -	if (mcde_flow_is_video(mcde)) {
> -		/*
> -		 * Keep FIFO permanently enabled in video mode,
> -		 * otherwise MCDE will stop feeding data to the panel.
> -		 */
> +	/*
> +	 * If we're using oneshot mode we don't start the flow
> +	 * until each time the display is given an update, and
> +	 * then we disable it immediately after. For all other
> +	 * modes (command or video) we start the FIFO flow
> +	 * right here. This is necessary for the hardware to
> +	 * behave right.
> +	 */
> +	if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) {
>  		mcde_enable_fifo(mcde, MCDE_FIFO_A);
>  		dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
>  	}
>  
> -	/* Enable automatic clock gating */
> +	/* Enable MCDE with automatic clock gating */
>  	val = readl(mcde->regs + MCDE_CR);
>  	val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
>  	writel(val, mcde->regs + MCDE_CR);
> @@ -1055,6 +1069,9 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
>  	/* Disable FIFO A flow */
>  	mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
>  
> +	/* This disables the DSI bridge */
> +	mcde_dsi_disable(mcde->bridge);
> +
>  	event = crtc->state->event;
>  	if (event) {
>  		crtc->state->event = NULL;
> @@ -1164,8 +1181,11 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe,
>  	if (fb) {
>  		mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0));
>  		dev_info_once(mcde->dev, "first update of display contents\n");
> -		/* The flow is already active in video mode */
> -		if (!mcde_flow_is_video(mcde) && mcde->flow_active == 0)
> +		/*
> +		 * Usually the flow is already active, unless we are in
> +		 * oneshot mode, then we need to kick the flow right here.
> +		 */
> +		if (mcde->flow_active == 0)
>  			mcde_start_flow(mcde);
>  	} else {
>  		/*
> diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
> index 9f197f4e9ced..8253e2f9993e 100644
> --- a/drivers/gpu/drm/mcde/mcde_drm.h
> +++ b/drivers/gpu/drm/mcde/mcde_drm.h
> @@ -97,6 +97,8 @@ static inline bool mcde_flow_is_video(struct mcde *mcde)
>  
>  bool mcde_dsi_irq(struct mipi_dsi_device *mdsi);
>  void mcde_dsi_te_request(struct mipi_dsi_device *mdsi);
> +void mcde_dsi_enable(struct drm_bridge *bridge);
> +void mcde_dsi_disable(struct drm_bridge *bridge);
>  extern struct platform_driver mcde_dsi_driver;
>  
>  void mcde_display_irq(struct mcde *mcde);
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index 0d7ebb59b727..a46a45c5cd52 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
> @@ -844,23 +844,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
>  	dev_info(d->dev, "DSI link enabled\n");
>  }
>  
> -
> -static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
> -{
> -	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
> -	u32 val;
> -
> -	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> -		/* Enable video mode */
> -		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
> -		val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
> -		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
> -	}
> -
> -	dev_info(d->dev, "enable DSI master\n");
> -};
> -
> -static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
> +/*
> + * Notice that this is called from inside the display controller
> + * and not from the bridge callbacks.
> + */
> +void mcde_dsi_enable(struct drm_bridge *bridge)
>  {
>  	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  	unsigned long hs_freq, lp_freq;
> @@ -938,6 +926,11 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
>  		val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
>  		val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
>  		writel(val, d->regs + DSI_VID_MODE_STS_CTL);
> +
> +		/* Enable video mode */
> +		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
> +		val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
> +		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
>  	} else {
>  		/* Command mode, clear IF1 ID */
>  		val = readl(d->regs + DSI_CMD_MODE_CTL);
> @@ -950,6 +943,8 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
>  		val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
>  		writel(val, d->regs + DSI_CMD_MODE_CTL);
>  	}
> +
> +	dev_info(d->dev, "enabled MCDE DSI master\n");
>  }
>  
>  static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
> @@ -1012,7 +1007,11 @@ static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d)
>  	}
>  }
>  
> -static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
> +/*
> + * Notice that this is called from inside the display controller
> + * and not from the bridge callbacks.
> + */
> +void mcde_dsi_disable(struct drm_bridge *bridge)
>  {
>  	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  	u32 val;
> @@ -1027,11 +1026,6 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
>  		/* Stop command mode */
>  		mcde_dsi_wait_for_command_mode_stop(d);
>  	}
> -}
> -
> -static void mcde_dsi_bridge_post_disable(struct drm_bridge *bridge)
> -{
> -	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  
>  	/*
>  	 * Stop clocks and terminate any DSI traffic here so the panel can
> @@ -1070,10 +1064,6 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
>  static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
>  	.attach = mcde_dsi_bridge_attach,
>  	.mode_set = mcde_dsi_bridge_mode_set,
> -	.disable = mcde_dsi_bridge_disable,
> -	.enable = mcde_dsi_bridge_enable,
> -	.pre_enable = mcde_dsi_bridge_pre_enable,
> -	.post_disable = mcde_dsi_bridge_post_disable,
>  };
>  
>  static int mcde_dsi_bind(struct device *dev, struct device *master,
> -- 
> 2.26.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch



More information about the linux-arm-kernel mailing list