summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Suvorov <oleksandr.suvorov@toradex.com>2019-08-16 09:48:29 +0300
committerOleksandr Suvorov <oleksandr.suvorov@toradex.com>2019-08-16 10:32:10 +0300
commit35272b2f48285111d463a0f8226a5472c50e6426 (patch)
tree64012a7ed6bf6dc748cabe98fd3cd1985ae9e3e3
parent485f9863807ac8d90d9ea3907089925eeb98becd (diff)
drm/mxsfb: add support of non-atomic clk_enable/disable
i.MX8QM/i.MX8QXP clock enabling/disabling callbacks are non-atomic, so we can't use them in interrupt/atomic context. Signed-off-by: Oleksandr Suvorov <oleksandr.suvorov@toradex.com>
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_drv.c38
1 files changed, 31 insertions, 7 deletions
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index 507e5516a918..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
@@ -609,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;
}
@@ -625,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)
@@ -645,6 +665,8 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
struct mxsfb_drm_private *mxsfb = drm->dev_private;
u32 reg;
+ mxsfb_enable_axi_clk(mxsfb);
+
reg = readl(mxsfb->base + LCDC_CTRL1);
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
@@ -652,6 +674,8 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
+ mxsfb_disable_axi_clk(mxsfb);
+
return IRQ_HANDLED;
}