diff options
Diffstat (limited to 'drivers/gpu/drm/mxsfb/mxsfb_drv.c')
-rw-r--r-- | drivers/gpu/drm/mxsfb/mxsfb_drv.c | 80 |
1 files changed, 64 insertions, 16 deletions
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 24bb09fdc5fd..183c3652100b 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -44,6 +44,27 @@ #include "mxsfb_drv.h" #include "mxsfb_regs.h" +/* + * There are non-atomic versions of clk_enable()/clk_disable() callbacks + * used in IMX8QM/IMX8QXP, so we can't manage axi clk in interrupt handlers + */ +#if defined(CONFIG_ARCH_FSL_IMX8QM) || defined(CONFIG_ARCH_FSL_IMX8QXP) +# define mxsfb_enable_axi_clk(mxsfb) +# define mxsfb_disable_axi_clk(mxsfb) +#else +static inline void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb) +{ + if (mxsfb->clk_axi) + clk_prepare_enable(mxsfb->clk_axi); +} + +static inline void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb) +{ + if (mxsfb->clk_axi) + clk_disable_unprepare(mxsfb->clk_axi); +} +#endif + /* The eLCDIF max possible CRTCs */ #define MAX_CRTCS 1 @@ -56,6 +77,9 @@ enum mxsfb_devtype { MXSFB_V4, }; +/* default output bus width */ +#define MXSFB_DEFAULT_BUS_WIDTH 24 + /* * When adding new formats, make sure to update the num_formats from * mxsfb_devdata below. @@ -336,9 +360,17 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, drm_crtc_vblank_on(&mxsfb->pipe.crtc); pm_runtime_get_sync(drm->dev); - drm_panel_prepare(mxsfb->panel); - mxsfb_crtc_enable(mxsfb); - drm_panel_enable(mxsfb->panel); + if (mxsfb->panel) { + drm_panel_prepare(mxsfb->panel); + mxsfb_crtc_enable(mxsfb); + drm_panel_enable(mxsfb->panel); + } + + if (mxsfb->bridge) { + drm_bridge_pre_enable(mxsfb->bridge); + mxsfb_crtc_enable(mxsfb); + drm_bridge_enable(mxsfb->bridge); + } } static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -348,9 +380,18 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_pending_vblank_event *event; - drm_panel_disable(mxsfb->panel); - mxsfb_crtc_disable(mxsfb); - drm_panel_unprepare(mxsfb->panel); + if (mxsfb->bridge) { + drm_bridge_disable(mxsfb->bridge); + mxsfb_crtc_disable(mxsfb); + drm_bridge_post_disable(mxsfb->bridge); + } + + if (mxsfb->panel) { + drm_panel_disable(mxsfb->panel); + mxsfb_crtc_disable(mxsfb); + drm_panel_unprepare(mxsfb->panel); + } + pm_runtime_put_sync(drm->dev); spin_lock_irq(&drm->event_lock); @@ -398,6 +439,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) struct mxsfb_drm_private *mxsfb; struct resource *res; u32 max_res[2] = {0, 0}; + u32 bus_width = MXSFB_DEFAULT_BUS_WIDTH; int ret; mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL); @@ -493,6 +535,10 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) } } + /* bus width is needed to set up correct bus format */ + of_property_read_u32(drm->dev->of_node, "bus-width", &bus_width); + mxsfb->bus_width = bus_width; + of_property_read_u32_array(drm->dev->of_node, "max-res", &max_res[0], 2); if (!max_res[0]) @@ -531,13 +577,15 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) drm_helper_hpd_irq_event(drm); pm_runtime_enable(drm->dev); + pm_runtime_get_sync(drm->dev); return 0; err_cma: drm_irq_uninstall(drm); err_irq: - drm_panel_detach(mxsfb->panel); + if (mxsfb->panel) + drm_panel_detach(mxsfb->panel); return ret; } @@ -566,6 +614,7 @@ static void mxsfb_unload(struct drm_device *drm) drm->dev_private = NULL; + pm_runtime_put_sync(drm->dev); pm_runtime_disable(drm->dev); } @@ -581,14 +630,13 @@ static int mxsfb_enable_vblank(struct drm_device *drm, unsigned int crtc) struct mxsfb_drm_private *mxsfb = drm->dev_private; int ret = 0; - ret = clk_prepare_enable(mxsfb->clk_axi); - if (ret) - return ret; + mxsfb_enable_axi_clk(mxsfb); /* Clear and enable VBLANK IRQ */ writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET); - clk_disable_unprepare(mxsfb->clk_axi); + + mxsfb_disable_axi_clk(mxsfb); return ret; } @@ -597,13 +645,13 @@ static void mxsfb_disable_vblank(struct drm_device *drm, unsigned int crtc) { struct mxsfb_drm_private *mxsfb = drm->dev_private; - if (clk_prepare_enable(mxsfb->clk_axi)) - return; + mxsfb_enable_axi_clk(mxsfb); /* Disable and clear VBLANK IRQ */ writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR); writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); - clk_disable_unprepare(mxsfb->clk_axi); + + mxsfb_disable_axi_clk(mxsfb); } static void mxsfb_irq_preinstall(struct drm_device *drm) @@ -617,7 +665,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) struct mxsfb_drm_private *mxsfb = drm->dev_private; u32 reg; - clk_prepare_enable(mxsfb->clk_axi); + mxsfb_enable_axi_clk(mxsfb); reg = readl(mxsfb->base + LCDC_CTRL1); @@ -626,7 +674,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); - clk_disable_unprepare(mxsfb->clk_axi); + mxsfb_disable_axi_clk(mxsfb); return IRQ_HANDLED; } |