From 768859c239922264f91d8a49ff8b1b227e7ad7d9 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:29 +0100 Subject: drm/mcde: Provide vblank handling unconditionally At the moment, vblank handling is only enabled together with TE synchronization. However, the vblank IRQ is also working with on displays without TE synchronization (e.g. DSI video mode panels). It seems like the vblank IRQ is actually generated by the MCDE hardware for the channel. Therefore, the vblank handling should be working correctly in all the cases and we can enable it unconditionally. Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-2-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_display.c | 15 +++++---------- drivers/gpu/drm/mcde/mcde_drv.c | 16 ++++------------ 2 files changed, 9 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index 751454ae3cd1..65522481b367 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -934,10 +934,10 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe, val = readl(mcde->regs + MCDE_CRC); val |= MCDE_CRC_SYCEN0; writel(val, mcde->regs + MCDE_CRC); - - drm_crtc_vblank_on(crtc); } + drm_crtc_vblank_on(crtc); + dev_info(drm->dev, "MCDE display is enabled\n"); } @@ -947,8 +947,7 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe) struct drm_device *drm = crtc->dev; struct mcde *mcde = drm->dev_private; - if (mcde->te_sync) - drm_crtc_vblank_off(crtc); + drm_crtc_vblank_off(crtc); /* Disable FIFO A flow */ mcde_disable_fifo(mcde, MCDE_FIFO_A, true); @@ -1097,6 +1096,8 @@ static struct drm_simple_display_pipe_funcs mcde_display_funcs = { .enable = mcde_display_enable, .disable = mcde_display_disable, .update = mcde_display_update, + .enable_vblank = mcde_display_enable_vblank, + .disable_vblank = mcde_display_disable_vblank, .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, }; @@ -1123,12 +1124,6 @@ int mcde_display_init(struct drm_device *drm) DRM_FORMAT_YUV422, }; - /* Provide vblank only when we have TE enabled */ - if (mcde->te_sync) { - mcde_display_funcs.enable_vblank = mcde_display_enable_vblank; - mcde_display_funcs.disable_vblank = mcde_display_disable_vblank; - } - ret = drm_simple_display_pipe_init(drm, &mcde->pipe, &mcde_display_funcs, formats, ARRAY_SIZE(formats), diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 5649887d2b90..0ccd3b0308c2 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -179,18 +179,10 @@ static int mcde_modeset_init(struct drm_device *drm) mode_config->min_height = 1; mode_config->max_height = 1080; - /* - * Currently we only support vblank handling on the DSI bridge, using - * TE synchronization. If TE sync is not set up, it is still possible - * to push out a single update on demand, but this is hard for DRM to - * exploit. - */ - if (mcde->te_sync) { - ret = drm_vblank_init(drm, 1); - if (ret) { - dev_err(drm->dev, "failed to init vblank\n"); - goto out_config; - } + ret = drm_vblank_init(drm, 1); + if (ret) { + dev_err(drm->dev, "failed to init vblank\n"); + goto out_config; } ret = mcde_display_init(drm); -- cgit v1.2.3 From d920e8da3d837bcc041800b8da9b335a728490f7 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:30 +0100 Subject: drm/mcde: Fix frame sync setup for video mode panels The MCDE driver differentiates only between "te_sync" (for hardware TE0 sync) and software sync (i.e. manually triggered updates) at the moment. However, none of these options work correctly for video mode panels. Therefore, we need to make some changes to make them work correctly: - Select hardware sync coming from the (DSI) formatter. - Keep the FIFO permanently enabled (otherwise MCDE will stop feeding data to the panel). - Skip manual software sync (this is not necessary in video mode). Automatically detect if the connected panel is using video mode and enable the necessary changes in that case. Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-3-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_display.c | 32 ++++++++++++++++++-------------- drivers/gpu/drm/mcde/mcde_drm.h | 1 + drivers/gpu/drm/mcde/mcde_drv.c | 2 -- drivers/gpu/drm/mcde/mcde_dsi.c | 13 +++++++++++-- 4 files changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index 65522481b367..a3375a974caf 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -498,24 +498,20 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch, } /* Set up channel 0 sync (based on chnl_update_registers()) */ - if (mcde->te_sync) { - /* - * Turn on hardware TE0 synchronization - */ + if (mcde->video_mode || mcde->te_sync) val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; - val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 - << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; - } else { - /* - * Set up sync source to software, out sync formatter - * Code mostly from mcde_hw.c chnl_update_registers() - */ + else val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; + + if (mcde->te_sync) + val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 + << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; + else val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; - } + writel(val, mcde->regs + sync); /* Set up pixels per line and lines per frame */ @@ -938,6 +934,13 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe, drm_crtc_vblank_on(crtc); + if (mcde->video_mode) + /* + * Keep FIFO permanently enabled in video mode, + * otherwise MCDE will stop feeding data to the panel. + */ + mcde_enable_fifo(mcde, MCDE_FIFO_A); + dev_info(drm->dev, "MCDE display is enabled\n"); } @@ -1047,8 +1050,9 @@ 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)); - /* Send a single frame using software sync */ - mcde_display_send_one_frame(mcde); + if (!mcde->video_mode) + /* Send a single frame using software sync */ + mcde_display_send_one_frame(mcde); dev_info_once(mcde->dev, "sent first display update\n"); } else { /* diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h index dab4db021231..80edd6628979 100644 --- a/drivers/gpu/drm/mcde/mcde_drm.h +++ b/drivers/gpu/drm/mcde/mcde_drm.h @@ -19,6 +19,7 @@ struct mcde { struct mipi_dsi_device *mdsi; s16 stride; bool te_sync; + bool video_mode; bool oneshot_mode; unsigned int flow_active; spinlock_t flow_lock; /* Locks the channel flow control */ diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 0ccd3b0308c2..9008ddcfc528 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -331,8 +331,6 @@ static int mcde_probe(struct platform_device *pdev) drm->dev_private = mcde; platform_set_drvdata(pdev, drm); - /* Enable use of the TE signal and interrupt */ - mcde->te_sync = true; /* Enable continuous updates: this is what Linux' framebuffer expects */ mcde->oneshot_mode = false; drm->dev_private = mcde; diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index d6214d3c8b33..ffd2e0b64628 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -130,6 +130,15 @@ bool mcde_dsi_irq(struct mipi_dsi_device *mdsi) return te_received; } +static void mcde_dsi_attach_to_mcde(struct mcde_dsi *d) +{ + d->mcde->mdsi = d->mdsi; + + d->mcde->video_mode = !!(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO); + /* Enable use of the TE signal for all command mode panels */ + d->mcde->te_sync = !d->mcde->video_mode; +} + static int mcde_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *mdsi) { @@ -148,7 +157,7 @@ static int mcde_dsi_host_attach(struct mipi_dsi_host *host, d->mdsi = mdsi; if (d->mcde) - d->mcde->mdsi = mdsi; + mcde_dsi_attach_to_mcde(d); return 0; } @@ -901,7 +910,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master, d->mcde = mcde; /* If the display attached before binding, set this up */ if (d->mdsi) - d->mcde->mdsi = d->mdsi; + mcde_dsi_attach_to_mcde(d); /* Obtain the clocks */ d->hs_clk = devm_clk_get(dev, "hs"); -- cgit v1.2.3 From 57efea87db43ba0dbb2a9b2987d5f41637c965c1 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:31 +0100 Subject: drm/mcde: dsi: Make video mode errors more verbose Triggering an error conditions in DSI video mode only results in a very generic "some video mode error status" error message at the moment. Make this more clear by adding separate error messages for each bit. Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-4-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_dsi.c | 22 +++++++++++++++++++++- drivers/gpu/drm/mcde/mcde_dsi_regs.h | 10 ++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index ffd2e0b64628..c7956c92b51b 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -124,7 +124,27 @@ bool mcde_dsi_irq(struct mipi_dsi_device *mdsi) val = readl(d->regs + DSI_VID_MODE_STS_FLAG); if (val) - dev_err(d->dev, "some video mode error status\n"); + dev_dbg(d->dev, "DSI_VID_MODE_STS_FLAG = %08x\n", val); + if (val & DSI_VID_MODE_STS_VSG_RUNNING) + dev_dbg(d->dev, "VID mode VSG running\n"); + if (val & DSI_VID_MODE_STS_ERR_MISSING_DATA) + dev_err(d->dev, "VID mode missing data\n"); + if (val & DSI_VID_MODE_STS_ERR_MISSING_HSYNC) + dev_err(d->dev, "VID mode missing HSYNC\n"); + if (val & DSI_VID_MODE_STS_ERR_MISSING_VSYNC) + dev_err(d->dev, "VID mode missing VSYNC\n"); + if (val & DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH) + dev_err(d->dev, "VID mode less bytes than expected between two HSYNC\n"); + if (val & DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT) + dev_err(d->dev, "VID mode less lines than expected between two VSYNC\n"); + if (val & (DSI_VID_MODE_STS_ERR_BURSTWRITE | + DSI_VID_MODE_STS_ERR_LINEWRITE | + DSI_VID_MODE_STS_ERR_LONGREAD)) + dev_err(d->dev, "VID mode read/write error\n"); + if (val & DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH) + dev_err(d->dev, "VID mode received packets differ from expected size\n"); + if (val & DSI_VID_MODE_STS_VSG_RECOVERY) + dev_err(d->dev, "VID mode VSG in recovery mode\n"); writel(val, d->regs + DSI_VID_MODE_STS_CLR); return te_received; diff --git a/drivers/gpu/drm/mcde/mcde_dsi_regs.h b/drivers/gpu/drm/mcde/mcde_dsi_regs.h index c9253321a3be..b03a336c235f 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi_regs.h +++ b/drivers/gpu/drm/mcde/mcde_dsi_regs.h @@ -248,6 +248,16 @@ #define DSI_VID_MODE_STS 0x000000BC #define DSI_VID_MODE_STS_VSG_RUNNING BIT(0) +#define DSI_VID_MODE_STS_ERR_MISSING_DATA BIT(1) +#define DSI_VID_MODE_STS_ERR_MISSING_HSYNC BIT(2) +#define DSI_VID_MODE_STS_ERR_MISSING_VSYNC BIT(3) +#define DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH BIT(4) +#define DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT BIT(5) +#define DSI_VID_MODE_STS_ERR_BURSTWRITE BIT(6) +#define DSI_VID_MODE_STS_ERR_LINEWRITE BIT(7) +#define DSI_VID_MODE_STS_ERR_LONGREAD BIT(8) +#define DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH BIT(9) +#define DSI_VID_MODE_STS_VSG_RECOVERY BIT(10) #define DSI_VID_VCA_SETTING1 0x000000C0 #define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_SHIFT 0 -- cgit v1.2.3 From 6ddfb00d7d7aceded3409c83b0bd45f0d00785c1 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:32 +0100 Subject: drm/mcde: dsi: Delay start of video stream generator The initialization order for DSI video mode is important - if we enable the video stream generator (VSG) before the MCDE DSI formatter starts sending pixel data, it will immediately run into an error and disable itself again. Avoid this problem by delaying the activation of the VSG until the MCDE DSI formatter is properly set up and running (i.e. when mcde_dsi_bridge_enable() is called). Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-5-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_dsi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index c7956c92b51b..4710f23b2966 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -583,11 +583,6 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d, 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); } static void mcde_dsi_start(struct mcde_dsi *d) @@ -699,6 +694,14 @@ static void mcde_dsi_start(struct mcde_dsi *d) 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"); }; -- cgit v1.2.3 From 1f79c60e1028144ec1f3f120f734f7cb16c1c924 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:33 +0100 Subject: drm/mcde: dsi: Fix duplicated DSI connector Using a (single) DSI display with MCDE currently results in two "connected" connectors: Connector: DSI-1 id : 34 encoder id : 0 conn : connected size : 0x0 (mm) count_modes : 0 count_props : 5 props : 1 2 5 6 4 count_encoders : 1 encoders : 33 Connector: DSI-2 id : 35 encoder id : 33 conn : connected size : 53x89 (mm) count_modes : 1 count_props : 5 props : 1 2 5 6 4 count_encoders : 1 encoders : 33 Mode: "480x800" 480x800 60 Although both show up as connected, the first one does not have any size and no available modes. This confuses userspace tools (e.g. kmscube) who look for available modes for the first connector. The reason for the duplicated connector is that mcde_dsi.c and the DRM panel bridge helper both set up a DSI connector, with more or less the same code. The connector set up by the DRM panel bridge is the one that is correctly set up in the example above. Therefore we can just remove the connector setup from mcde_dsi.c and let the DRM core handle all the hard work. Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-6-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_dsi.c | 52 +---------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index 4710f23b2966..df963e078c35 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -39,7 +39,6 @@ struct mcde_dsi { struct device *dev; struct mcde *mcde; struct drm_bridge bridge; - struct drm_connector connector; struct drm_panel *panel; struct drm_bridge *bridge_out; struct mipi_dsi_host dsi_host; @@ -64,11 +63,6 @@ static inline struct mcde_dsi *host_to_mcde_dsi(struct mipi_dsi_host *h) return container_of(h, struct mcde_dsi, dsi_host); } -static inline struct mcde_dsi *connector_to_mcde_dsi(struct drm_connector *c) -{ - return container_of(c, struct mcde_dsi, connector); -} - bool mcde_dsi_irq(struct mipi_dsi_device *mdsi) { struct mcde_dsi *d; @@ -843,67 +837,23 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge) clk_disable_unprepare(d->lp_clk); } -/* - * This connector needs no special handling, just use the default - * helpers for everything. It's pretty dummy. - */ -static const struct drm_connector_funcs mcde_dsi_connector_funcs = { - .reset = drm_atomic_helper_connector_reset, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static int mcde_dsi_get_modes(struct drm_connector *connector) -{ - struct mcde_dsi *d = connector_to_mcde_dsi(connector); - - /* Just pass the question to the panel */ - if (d->panel) - return drm_panel_get_modes(d->panel); - - /* TODO: deal with bridges */ - - return 0; -} - -static const struct drm_connector_helper_funcs -mcde_dsi_connector_helper_funcs = { - .get_modes = mcde_dsi_get_modes, -}; - static int mcde_dsi_bridge_attach(struct drm_bridge *bridge) { struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); struct drm_device *drm = bridge->dev; int ret; - drm_connector_helper_add(&d->connector, - &mcde_dsi_connector_helper_funcs); - if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) { dev_err(d->dev, "we need atomic updates\n"); return -ENOTSUPP; } - ret = drm_connector_init(drm, &d->connector, - &mcde_dsi_connector_funcs, - DRM_MODE_CONNECTOR_DSI); - if (ret) { - dev_err(d->dev, "failed to initialize DSI bridge connector\n"); - return ret; - } - d->connector.polled = DRM_CONNECTOR_POLL_CONNECT; - /* The encoder in the bridge attached to the DSI bridge */ - drm_connector_attach_encoder(&d->connector, bridge->encoder); - /* Then we attach the DSI bridge to the output (panel etc) bridge */ + /* Attach the DSI bridge to the output (panel etc) bridge */ ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge); if (ret) { dev_err(d->dev, "failed to attach the DSI bridge\n"); return ret; } - d->connector.status = connector_status_connected; return 0; } -- cgit v1.2.3 From 3c5824bdc49046d187d49207730d5074ba59ca20 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:34 +0100 Subject: drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set() The DSI initialization sequence incorrectly assumes that the mode_set() function of the DRM bridge is always called when (re-)enabling the display. This is not necessarily the case. Keeping the device idle in the framebuffer console for a while results in the display being turned off using the disable() function. However, as soon as any key is pressed only (pre_)enable() are called. mode_set() is skipped because the mode has not been changed. In this case, the DSI HS/LP clocks are never turned back on, preventing the display from working. Fix this by moving a part of the initialization sequence from mode_set() to pre_enable(). Keep most of the video mode setup in mode_set() since most of the registers are only dependent on the mode that is set for the panel - there is no need to write them again each time we re-enable the display. Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-7-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_dsi.c | 67 +++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 30 deletions(-) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index df963e078c35..03896a1f339a 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -562,21 +562,6 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d, DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT; writel(val, d->regs + DSI_VID_VCA_SETTING2); - /* Put IF1 into video mode */ - val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); - val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE; - writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); - - /* Disable command mode on IF1 */ - val = readl(d->regs + DSI_CMD_MODE_CTL); - val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN; - writel(val, d->regs + DSI_CMD_MODE_CTL); - - /* Enable some error interrupts */ - val = readl(d->regs + DSI_VID_MODE_STS_CTL); - 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); } static void mcde_dsi_start(struct mcde_dsi *d) @@ -700,26 +685,13 @@ static void mcde_dsi_bridge_enable(struct drm_bridge *bridge) dev_info(d->dev, "enable DSI master\n"); }; -static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adj) +static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge) { struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); - unsigned long pixel_clock_hz = mode->clock * 1000; unsigned long hs_freq, lp_freq; u32 val; int ret; - if (!d->mdsi) { - dev_err(d->dev, "no DSI device attached to encoder!\n"); - return; - } - - dev_info(d->dev, "set DSI master to %dx%d %lu Hz %s mode\n", - mode->hdisplay, mode->vdisplay, pixel_clock_hz, - (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD" - ); - /* Copy maximum clock frequencies */ if (d->mdsi->lp_rate) lp_freq = d->mdsi->lp_rate; @@ -758,7 +730,21 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, d->hs_freq); if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { - mcde_dsi_setup_video_mode(d, mode); + /* Put IF1 into video mode */ + val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); + val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE; + writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); + + /* Disable command mode on IF1 */ + val = readl(d->regs + DSI_CMD_MODE_CTL); + val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN; + writel(val, d->regs + DSI_CMD_MODE_CTL); + + /* Enable some error interrupts */ + val = readl(d->regs + DSI_VID_MODE_STS_CTL); + 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); } else { /* Command mode, clear IF1 ID */ val = readl(d->regs + DSI_CMD_MODE_CTL); @@ -772,6 +758,26 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, } } +static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adj) +{ + struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); + + if (!d->mdsi) { + dev_err(d->dev, "no DSI device attached to encoder!\n"); + return; + } + + dev_info(d->dev, "set DSI master to %dx%d %u Hz %s mode\n", + mode->hdisplay, mode->vdisplay, mode->clock * 1000, + (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD" + ); + + if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) + mcde_dsi_setup_video_mode(d, mode); +} + static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d) { u32 val; @@ -863,6 +869,7 @@ static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = { .mode_set = mcde_dsi_bridge_mode_set, .disable = mcde_dsi_bridge_disable, .enable = mcde_dsi_bridge_enable, + .pre_enable = mcde_dsi_bridge_pre_enable, }; static int mcde_dsi_bind(struct device *dev, struct device *master, -- cgit v1.2.3 From 97de863673f07f424dd0666aefb4b6ecaba10171 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 6 Nov 2019 17:58:35 +0100 Subject: drm/mcde: Handle pending vblank while disabling display Disabling the display using MCDE currently results in a warning together with a delay caused by some timeouts: mcde a0350000.mcde: MCDE display is disabled ------------[ cut here ]------------ WARNING: CPU: 0 PID: 20 at drivers/gpu/drm/drm_atomic_helper.c:2258 drm_atomic_helper_commit_hw_done+0xe0/0xe4 Hardware name: ST-Ericsson Ux5x0 platform (Device Tree Support) Workqueue: events drm_mode_rmfb_work_fn [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0x84/0x98) [] (dump_stack) from [] (__warn+0xb8/0xd4) [] (__warn) from [] (warn_slowpath_fmt+0x64/0xc4) [] (warn_slowpath_fmt) from [] (drm_atomic_helper_commit_hw_done+0xe0/0xe4) [] (drm_atomic_helper_commit_hw_done) from [] (drm_atomic_helper_commit_tail_rpm+0x44/0x6c) [] (drm_atomic_helper_commit_tail_rpm) from [] (commit_tail+0x50/0x10c) [] (commit_tail) from [] (drm_atomic_helper_commit+0xbc/0x128) [] (drm_atomic_helper_commit) from [] (drm_framebuffer_remove+0x390/0x428) [] (drm_framebuffer_remove) from [] (drm_mode_rmfb_work_fn+0x38/0x48) [] (drm_mode_rmfb_work_fn) from [] (process_one_work+0x1f0/0x43c) [] (process_one_work) from [] (worker_thread+0x254/0x55c) [] (worker_thread) from [] (kthread+0x124/0x150) [] (kthread) from [] (ret_from_fork+0x14/0x2c) Exception stack(0xeb14dfb0 to 0xeb14dff8) dfa0: 00000000 00000000 00000000 00000000 dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 dfe0: 00000000 00000000 00000000 00000000 00000013 00000000 ---[ end trace 314909bcd4c7d50c ]--- [drm:drm_atomic_helper_wait_for_dependencies] *ERROR* [CRTC:32:crtc-0] flip_done timed out [drm:drm_atomic_helper_wait_for_dependencies] *ERROR* [CONNECTOR:34:DSI-1] flip_done timed out [drm:drm_atomic_helper_wait_for_dependencies] *ERROR* [PLANE:31:plane-0] flip_done timed out The reason for this is that there is a vblank event pending, but we never handle it after disabling the vblank interrupts. Check if there is an vblank event pending when disabling the display, and clear it by sending a fake vblank event in that case. Signed-off-by: Stephan Gerhold Tested-by: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191106165835.2863-8-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_display.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index a3375a974caf..e59907e68854 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -949,12 +949,22 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_device *drm = crtc->dev; struct mcde *mcde = drm->dev_private; + struct drm_pending_vblank_event *event; drm_crtc_vblank_off(crtc); /* Disable FIFO A flow */ mcde_disable_fifo(mcde, MCDE_FIFO_A, true); + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } + dev_info(drm->dev, "MCDE display is disabled\n"); } -- cgit v1.2.3 From ce9cde04205a8f376e01d75a156f8810a98a2405 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 22 Nov 2019 08:24:57 +0100 Subject: drm/mcde: Reuse global DSI command defs The MCDE DSI include file redefines some commands that already exist in the common