diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2020-08-09 01:31:21 +0300 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2020-08-13 17:39:19 +0300 |
commit | c4842d4d0f744bf419a1b011fa3ce0ed98fcdc10 (patch) | |
tree | 3e67ddaa1882b3460dc685c87edd6d3a12c8d7c2 /drivers/gpu/drm/mcde/mcde_display.c | |
parent | f6fd1d70609ca691ee5dc0d2748b6eeaa57a9535 (diff) | |
download | linux-c4842d4d0f744bf419a1b011fa3ce0ed98fcdc10.tar.xz |
drm/mcde: Fix display pipeline restart
To make sure that the MCDE is in a reasonable state during
set-up, perform a reset by power cycling the block by dropping
the on-chip regulator reference after probe. The display
subsystem (DSS) has no dedicated reset line so dropping
the EPOD regulator is the only real way of resetting it.
We introduce code to enable and disable the regulator in
the display enable/disable callbacks.
We move the generic MCDE setup such as muxing of DPI
signals and masking of interrupts to the display
handling.
When we drop the power to the whole display subsystem, not
only MCDE but also the DSI links lose their state. Therefore
we move the DSI block reset and hardware initialization
code to the mcde_dsi_bridge_pre_enable() callback so this
happens every time we start up the bridge, as we may have
lost the power.
We move the final disablement of the interrupts and
clocks to the mcde_dsi_bridge_post_disable() callback
rather than have it in the mcde_dsi_bridge_disable()
callback, as some control messages may still be sent
over the DSI host after the bridge has been shut down.
This (together with a patch for the corresponding
panel) makes the Samsung GT-S7710 successfully disable
and re-enable its display, cutting all power while
disabled and re-initializing the hardware when coming
back up.
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Cc: newbytee@protonmail.com
Cc: Stephan Gerhold <stephan@gerhold.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20200808223122.1492124-3-linus.walleij@linaro.org
Diffstat (limited to 'drivers/gpu/drm/mcde/mcde_display.c')
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_display.c | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index cbc7c0c4955a..d608cc894e01 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -7,6 +7,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-buf.h> +#include <linux/regulator/consumer.h> #include <drm/drm_device.h> #include <drm/drm_fb_cma_helper.h> @@ -879,6 +880,14 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe, u32 formatter_frame; u32 pkt_div; u32 val; + int ret; + + /* This powers up the entire MCDE block and the DSI hardware */ + ret = regulator_enable(mcde->epod); + if (ret) { + dev_err(drm->dev, "can't re-enable EPOD regulator\n"); + return; + } dev_info(drm->dev, "enable MCDE, %d x %d format %s\n", mode->hdisplay, mode->vdisplay, @@ -889,6 +898,26 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe, return; } + /* Set up the main control, watermark level at 7 */ + val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; + /* 24 bits DPI: connect LSB Ch B to D[0:7] */ + val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT; + /* TV out: connect LSB Ch B to D[8:15] */ + val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT; + /* Don't care about this muxing */ + val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; + /* 24 bits DPI: connect MID Ch B to D[24:31] */ + val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT; + /* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */ + val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT; + /* Syncmux bits zero: DPI channel A and B on output pins A and B resp */ + writel(val, mcde->regs + MCDE_CONF0); + + /* Clear any pending interrupts */ + mcde_display_disable_irqs(mcde); + writel(0, mcde->regs + MCDE_IMSCERR); + writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR); + dev_info(drm->dev, "output in %s mode, format %dbpp\n", (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD", @@ -1005,6 +1034,11 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe, dev_dbg(mcde->dev, "started MCDE video FIFO flow\n"); } + /* Enable automatic clock gating */ + val = readl(mcde->regs + MCDE_CR); + val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; + writel(val, mcde->regs + MCDE_CR); + dev_info(drm->dev, "MCDE display is enabled\n"); } @@ -1014,6 +1048,7 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe) struct drm_device *drm = crtc->dev; struct mcde *mcde = to_mcde(drm); struct drm_pending_vblank_event *event; + int ret; drm_crtc_vblank_off(crtc); @@ -1029,6 +1064,12 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe) spin_unlock_irq(&crtc->dev->event_lock); } + ret = regulator_disable(mcde->epod); + if (ret) + dev_err(drm->dev, "can't disable EPOD regulator\n"); + /* Make sure we are powered down (before we may power up again) */ + usleep_range(50000, 70000); + dev_info(drm->dev, "MCDE display is disabled\n"); } |