diff mbox

[RESEND] drm: mxsfb_crtc: Reset the eLCDIF controller

Message ID 1495486427-8213-1-git-send-email-festevam@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Fabio Estevam May 22, 2017, 8:53 p.m. UTC
From: Fabio Estevam <fabio.estevam@nxp.com>

According to the eLCDIF initialization steps listed in the MX6SX
Reference Manual the eLCDIF block reset is mandatory. 

Without performing the eLCDIF reset the display shows garbage content
when the kernel boots.

In earlier tests this issue has not been observed because the bootloader
was previously showing a splash screen and the bootloader display driver
does properly implement the eLCDIF reset.

Add the eLCDIF reset to the driver, so that it can operate correctly
independently of the bootloader.

Tested on a imx6sx-sdb board.

Cc: <stable@vger.kernel.org> #4.11.x
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Reviewed-by: Marek Vasut <marex@denx.de>
---
Daniel,

Marek suggested that this goes via drm-misc.

Thanks

 drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 42 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Comments

Fabio Estevam May 28, 2017, 1:12 p.m. UTC | #1
On Mon, May 22, 2017 at 5:53 PM, Fabio Estevam <festevam@gmail.com> wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> According to the eLCDIF initialization steps listed in the MX6SX
> Reference Manual the eLCDIF block reset is mandatory.
>
> Without performing the eLCDIF reset the display shows garbage content
> when the kernel boots.
>
> In earlier tests this issue has not been observed because the bootloader
> was previously showing a splash screen and the bootloader display driver
> does properly implement the eLCDIF reset.
>
> Add the eLCDIF reset to the driver, so that it can operate correctly
> independently of the bootloader.
>
> Tested on a imx6sx-sdb board.
>
> Cc: <stable@vger.kernel.org> #4.11.x
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
> Reviewed-by: Marek Vasut <marex@denx.de>
> ---
> Daniel,
>
> Marek suggested that this goes via drm-misc.

Sean/Jani/Daniel,

Any chance this could go via drm-misc?

Without this change the mxsfb driver is unusable on some platforms.

Thanks


>
> Thanks
>
>  drivers/gpu/drm/mxsfb/mxsfb_crtc.c | 42 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
>
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
> index 1144e0c..0abe776 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
> @@ -35,6 +35,13 @@
>  #include "mxsfb_drv.h"
>  #include "mxsfb_regs.h"
>
> +#define MXS_SET_ADDR           0x4
> +#define MXS_CLR_ADDR           0x8
> +#define MODULE_CLKGATE         BIT(30)
> +#define MODULE_SFTRST          BIT(31)
> +/* 1 second delay should be plenty of time for block reset */
> +#define RESET_TIMEOUT          1000000
> +
>  static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
>  {
>         return (val & mxsfb->devdata->hs_wdth_mask) <<
> @@ -159,6 +166,36 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
>                 clk_disable_unprepare(mxsfb->clk_disp_axi);
>  }
>
> +/*
> + * Clear the bit and poll it cleared.  This is usually called with
> + * a reset address and mask being either SFTRST(bit 31) or CLKGATE
> + * (bit 30).
> + */
> +static int clear_poll_bit(void __iomem *addr, u32 mask)
> +{
> +       u32 reg;
> +
> +       writel(mask, addr + MXS_CLR_ADDR);
> +       return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
> +}
> +
> +static int mxsfb_reset_block(void __iomem *reset_addr)
> +{
> +       int ret;
> +
> +       ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
> +       if (ret)
> +               return ret;
> +
> +       writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
> +
> +       ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
> +       if (ret)
> +               return ret;
> +
> +       return clear_poll_bit(reset_addr, MODULE_CLKGATE);
> +}
> +
>  static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
>  {
>         struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
> @@ -173,6 +210,11 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
>          */
>         mxsfb_enable_axi_clk(mxsfb);
>
> +       /* Mandatory eLCDIF reset as per the Reference Manual */
> +       err = mxsfb_reset_block(mxsfb->base);
> +       if (err)
> +               return;
> +
>         /* Clear the FIFOs */
>         writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
>
> --
> 2.7.4
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
index 1144e0c..0abe776 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
@@ -35,6 +35,13 @@ 
 #include "mxsfb_drv.h"
 #include "mxsfb_regs.h"
 
+#define MXS_SET_ADDR		0x4
+#define MXS_CLR_ADDR		0x8
+#define MODULE_CLKGATE		BIT(30)
+#define MODULE_SFTRST		BIT(31)
+/* 1 second delay should be plenty of time for block reset */
+#define RESET_TIMEOUT		1000000
+
 static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
 {
 	return (val & mxsfb->devdata->hs_wdth_mask) <<
@@ -159,6 +166,36 @@  static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
 		clk_disable_unprepare(mxsfb->clk_disp_axi);
 }
 
+/*
+ * Clear the bit and poll it cleared.  This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+	u32 reg;
+
+	writel(mask, addr + MXS_CLR_ADDR);
+	return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
+}
+
+static int mxsfb_reset_block(void __iomem *reset_addr)
+{
+	int ret;
+
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (ret)
+		return ret;
+
+	writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
+
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (ret)
+		return ret;
+
+	return clear_poll_bit(reset_addr, MODULE_CLKGATE);
+}
+
 static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
 {
 	struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
@@ -173,6 +210,11 @@  static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
 	 */
 	mxsfb_enable_axi_clk(mxsfb);
 
+	/* Mandatory eLCDIF reset as per the Reference Manual */
+	err = mxsfb_reset_block(mxsfb->base);
+	if (err)
+		return;
+
 	/* Clear the FIFOs */
 	writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);