Message ID | 1495486427-8213-1-git-send-email-festevam@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 --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);