From 85de9d17c485c4196f74d45de2206d4802f8a3be Mon Sep 17 00:00:00 2001 From: Denis Carikli Date: Mon, 7 Apr 2014 14:44:43 +0200 Subject: [PATCH 1/9] imx-drm: match ipu_di_signal_cfg's clk_pol with its description. According to the datasheet, setting the di0_polarity_disp_clk field in the GENERAL di register sets the output clock polarity to active high. Signed-off-by: Denis Carikli Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/ipu-di.c | 2 +- drivers/staging/imx-drm/ipuv3-crtc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c index 82a9ebad697c..849b3e120ef0 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c @@ -595,7 +595,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) } } - if (!sig->clk_pol) + if (sig->clk_pol) di_gen |= DI_GEN_POLARITY_DISP_CLK; ipu_di_write(di, di_gen, DI_GENERAL); diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index c48f640db006..f2c9cd040043 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -158,7 +158,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, sig_cfg.Vsync_pol = 1; sig_cfg.enable_pol = 1; - sig_cfg.clk_pol = 1; + sig_cfg.clk_pol = 0; sig_cfg.width = mode->hdisplay; sig_cfg.height = mode->vdisplay; sig_cfg.pixel_fmt = out_pixel_fmt; From 861a50c17260be2583309ef448dc728a4729549b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:16 +0200 Subject: [PATCH 2/9] imx-drm: ipu-common: add ipu_map_irq to request non-IDMAC interrupts This allows to request the DC related interrupts. Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 1 + drivers/staging/imx-drm/ipu-v3/ipu-common.c | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h index c4d14ead5837..2966e425990e 100644 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h @@ -76,6 +76,7 @@ enum ipu_channel_irq { IPU_IRQ_EOS = 192, }; +int ipu_map_irq(struct ipu_soc *ipu, int irq); int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, enum ipu_channel_irq irq); diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index ca85d3d70ae3..058551795677 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -933,15 +933,22 @@ static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } +int ipu_map_irq(struct ipu_soc *ipu, int irq) +{ + int virq; + + virq = irq_linear_revmap(ipu->domain, irq); + if (!virq) + virq = irq_create_mapping(ipu->domain, irq); + + return virq; +} +EXPORT_SYMBOL_GPL(ipu_map_irq); + int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, enum ipu_channel_irq irq_type) { - int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num); - - if (!irq) - irq = irq_create_mapping(ipu->domain, irq_type + channel->num); - - return irq; + return ipu_map_irq(ipu, irq_type + channel->num); } EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); From 17075504688baf04567fc47c4a2a6ab8684b13e9 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:17 +0200 Subject: [PATCH 3/9] imx-drm: ipu-common: add helpers to check for a busy IDMAC channel and to busywait for an interrupt Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/ipu-common.c | 22 +++++++++++++++++++++ drivers/staging/imx-drm/ipu-v3/ipu-prv.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 058551795677..8fb4c207f3f1 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -697,6 +697,12 @@ int ipu_idmac_enable_channel(struct ipuv3_channel *channel) } EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); +bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) +{ + return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); +} +EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); + int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) { struct ipu_soc *ipu = channel->ipu; @@ -714,6 +720,22 @@ int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) } EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); +int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(ms); + ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); + while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_wait_interrupt); + int ipu_idmac_disable_channel(struct ipuv3_channel *channel) { struct ipu_soc *ipu = channel->ipu; diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h index 4df00501adc2..bfc1b3366488 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h +++ b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h @@ -185,6 +185,9 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu); int ipu_module_enable(struct ipu_soc *ipu, u32 mask); int ipu_module_disable(struct ipu_soc *ipu, u32 mask); +bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); +int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms); + int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, unsigned long base, u32 module, struct clk *ipu_clk); void ipu_di_exit(struct ipu_soc *ipu, int id); From 47348661c4e03480dc99a29c371b5fa930d1bb55 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:18 +0200 Subject: [PATCH 4/9] imx-drm: ipu-dmfc: wait for FIFOs to run empty before disabling Disabling the DMFC module while there is still data in the FIFOs could cause the "new frame before end of frame" error state when the DMFC is enabled again. Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 25 +++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c index 45213017fa4b..59f182b28fc1 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c @@ -28,7 +28,12 @@ #define DMFC_GENERAL1 0x0014 #define DMFC_GENERAL2 0x0018 #define DMFC_IC_CTRL 0x001c -#define DMFC_STAT 0x0020 +#define DMFC_WR_CHAN_ALT 0x0020 +#define DMFC_WR_CHAN_DEF_ALT 0x0024 +#define DMFC_DP_CHAN_ALT 0x0028 +#define DMFC_DP_CHAN_DEF_ALT 0x002c +#define DMFC_GENERAL1_ALT 0x0030 +#define DMFC_STAT 0x0034 #define DMFC_WR_CHAN_1_28 0 #define DMFC_WR_CHAN_2_41 8 @@ -133,6 +138,20 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) } EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); +static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { + if (time_after(jiffies, timeout)) { + dev_warn(priv->dev, + "Timeout waiting for DMFC FIFOs to clear\n"); + break; + } + cpu_relax(); + } +} + void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) { struct ipu_dmfc_priv *priv = dmfc->priv; @@ -141,8 +160,10 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) priv->use_count--; - if (!priv->use_count) + if (!priv->use_count) { + ipu_dmfc_wait_fifos(priv); ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); + } if (priv->use_count < 0) priv->use_count = 0; From 1dee9a9e53860a674abe5a4ee02949fa447ba2d5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:19 +0200 Subject: [PATCH 5/9] imx-drm: ipu-dc: wait for DC_FC_1 / DP_SF_END interrupt Wait for the DC Frame Complete or DP Sync Flow End interrupts before disabling DC channels. Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 71 +++++++++++++++++-------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c index d5de8bb5c803..280e494aee81 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "../imx-drm.h" @@ -110,6 +111,9 @@ struct ipu_dc_priv { struct device *dev; struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; struct mutex mutex; + struct completion comp; + int dc_irq; + int dp_irq; }; static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) @@ -239,38 +243,46 @@ void ipu_dc_enable_channel(struct ipu_dc *dc) } EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); +static irqreturn_t dc_irq_handler(int irq, void *dev_id) +{ + struct ipu_dc *dc = dev_id; + u32 reg; + + reg = readl(dc->base + DC_WR_CH_CONF); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + writel(reg, dc->base + DC_WR_CH_CONF); + + /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */ + + complete(&dc->priv->comp); + return IRQ_HANDLED; +} + void ipu_dc_disable_channel(struct ipu_dc *dc) { struct ipu_dc_priv *priv = dc->priv; + int irq, ret; u32 val; - int irq = 0, timeout = 50; + /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */ if (dc->chno == 1) - irq = IPU_IRQ_DC_FC_1; + irq = priv->dc_irq; else if (dc->chno == 5) - irq = IPU_IRQ_DP_SF_END; + irq = priv->dp_irq; else return; - /* should wait for the interrupt here */ - mdelay(50); + init_completion(&priv->comp); + enable_irq(irq); + ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50)); + disable_irq(irq); + if (ret <= 0) { + dev_warn(priv->dev, "DC stop timeout after 50 ms\n"); - if (dc->di == 0) - val = 0x00000002; - else - val = 0x00000020; - - /* Wait for DC triple buffer to empty */ - while ((readl(priv->dc_reg + DC_STAT) & val) != val) { - usleep_range(2000, 20000); - timeout -= 2; - if (timeout <= 0) - break; + val = readl(dc->base + DC_WR_CH_CONF); + val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + writel(val, dc->base + DC_WR_CH_CONF); } - - val = readl(dc->base + DC_WR_CH_CONF); - val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - writel(val, dc->base + DC_WR_CH_CONF); } EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); @@ -340,7 +352,7 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, struct ipu_dc_priv *priv; static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 0x78, 0, 0x94, 0xb4}; - int i; + int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -361,6 +373,23 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, priv->channels[i].base = priv->dc_reg + channel_offsets[i]; } + priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1); + if (!priv->dc_irq) + return -EINVAL; + ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL, + &priv->channels[1]); + if (ret < 0) + return ret; + disable_irq(priv->dc_irq); + priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END); + if (!priv->dp_irq) + return -EINVAL; + ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL, + &priv->channels[5]); + if (ret < 0) + return ret; + disable_irq(priv->dp_irq); + writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | DC_WR_CH_CONF_PROG_DI_ID, priv->channels[1].base + DC_WR_CH_CONF); From 285bbb018f0b2332ac7f76dd7e1bfb761bf5d85b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:20 +0200 Subject: [PATCH 6/9] imx-drm: ipu-dp: split disabling the DP foreground channel from disabling the DP module The former has to be done before disabling the DMFC, the latter has to be done afterwards. Otherwise the DMFC FIFOs never get cleared properly. Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 2 + drivers/staging/imx-drm/ipu-v3/ipu-dp.c | 74 +++++++++++++-------- drivers/staging/imx-drm/ipuv3-plane.c | 4 ++ 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h index 2966e425990e..8678ad18a3ef 100644 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h @@ -153,8 +153,10 @@ void ipu_dmfc_put(struct dmfc_channel *dmfc); struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow); void ipu_dp_put(struct ipu_dp *); +int ipu_dp_enable(struct ipu_soc *ipu); int ipu_dp_enable_channel(struct ipu_dp *dp); void ipu_dp_disable_channel(struct ipu_dp *dp); +void ipu_dp_disable(struct ipu_soc *ipu); int ipu_dp_setup_channel(struct ipu_dp *dp, enum ipu_color_space in, enum ipu_color_space out); int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos); diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c index 58f87c8d7c07..6980fa125517 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c @@ -215,10 +215,9 @@ int ipu_dp_setup_channel(struct ipu_dp *dp, } EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -int ipu_dp_enable_channel(struct ipu_dp *dp) +int ipu_dp_enable(struct ipu_soc *ipu) { - struct ipu_flow *flow = to_flow(dp); - struct ipu_dp_priv *priv = flow->priv; + struct ipu_dp_priv *priv = ipu->dp_priv; mutex_lock(&priv->mutex); @@ -227,15 +226,28 @@ int ipu_dp_enable_channel(struct ipu_dp *dp) priv->use_count++; - if (dp->foreground) { - u32 reg; + mutex_unlock(&priv->mutex); - reg = readl(flow->base + DP_COM_CONF); - reg |= DP_COM_CONF_FG_EN; - writel(reg, flow->base + DP_COM_CONF); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_dp_enable); - ipu_srm_dp_sync_update(priv->ipu); - } +int ipu_dp_enable_channel(struct ipu_dp *dp) +{ + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; + u32 reg; + + if (!dp->foreground) + return 0; + + mutex_lock(&priv->mutex); + + reg = readl(flow->base + DP_COM_CONF); + reg |= DP_COM_CONF_FG_EN; + writel(reg, flow->base + DP_COM_CONF); + + ipu_srm_dp_sync_update(priv->ipu); mutex_unlock(&priv->mutex); @@ -247,26 +259,36 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) { struct ipu_flow *flow = to_flow(dp); struct ipu_dp_priv *priv = flow->priv; + u32 reg, csc; + + if (!dp->foreground) + return; + + mutex_lock(&priv->mutex); + + reg = readl(flow->base + DP_COM_CONF); + csc = reg & DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_FG) + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + reg &= ~DP_COM_CONF_FG_EN; + writel(reg, flow->base + DP_COM_CONF); + + writel(0, flow->base + DP_FG_POS); + ipu_srm_dp_sync_update(priv->ipu); + + mutex_unlock(&priv->mutex); +} +EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); + +void ipu_dp_disable(struct ipu_soc *ipu) +{ + struct ipu_dp_priv *priv = ipu->dp_priv; mutex_lock(&priv->mutex); priv->use_count--; - if (dp->foreground) { - u32 reg, csc; - - reg = readl(flow->base + DP_COM_CONF); - csc = reg & DP_COM_CONF_CSC_DEF_MASK; - if (csc == DP_COM_CONF_CSC_DEF_FG) - reg &= ~DP_COM_CONF_CSC_DEF_MASK; - - reg &= ~DP_COM_CONF_FG_EN; - writel(reg, flow->base + DP_COM_CONF); - - writel(0, flow->base + DP_FG_POS); - ipu_srm_dp_sync_update(priv->ipu); - } - if (!priv->use_count) ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); @@ -275,7 +297,7 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) mutex_unlock(&priv->mutex); } -EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); +EXPORT_SYMBOL_GPL(ipu_dp_disable); struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) { diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c index 27a8d735dae0..5697e59ddf1d 100644 --- a/drivers/staging/imx-drm/ipuv3-plane.c +++ b/drivers/staging/imx-drm/ipuv3-plane.c @@ -239,6 +239,8 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane) void ipu_plane_enable(struct ipu_plane *ipu_plane) { + if (ipu_plane->dp) + ipu_dp_enable(ipu_plane->ipu); ipu_dmfc_enable_channel(ipu_plane->dmfc); ipu_idmac_enable_channel(ipu_plane->ipu_ch); if (ipu_plane->dp) @@ -257,6 +259,8 @@ void ipu_plane_disable(struct ipu_plane *ipu_plane) ipu_dp_disable_channel(ipu_plane->dp); ipu_idmac_disable_channel(ipu_plane->ipu_ch); ipu_dmfc_disable_channel(ipu_plane->dmfc); + if (ipu_plane->dp) + ipu_dp_disable(ipu_plane->ipu); } static void ipu_plane_dpms(struct ipu_plane *ipu_plane, int mode) From 216ddd608fb4f993d2f80ddbf1297abe10935a89 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:21 +0200 Subject: [PATCH 7/9] imx-drm: imx-dp: when disabling the DP foreground channel, wait until the DP background channel is finished before disabling the IDMAC channel Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/ipu-dp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c index 6980fa125517..d90f82a87d19 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c @@ -277,6 +277,9 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) writel(0, flow->base + DP_FG_POS); ipu_srm_dp_sync_update(priv->ipu); + if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC)) + ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50); + mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); From c115edb87f0666b924d8139b2fb0396b03c6ac5d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:22 +0200 Subject: [PATCH 8/9] imx-drm: ipuv3-crtc: change display enable/disable order Now that ipu_dc_disable_channel correctly waits for the channel to finish, we can reorder the enable/disable order to first stop the DC and DI and only then disable the IDMAC. Enabling is done the other way around: IDMAC first, then DC, then DI. This avoids an issue where sometimes the channel would not correctly start, leading to non-working LVDS displays. Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipuv3-crtc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index f2c9cd040043..ab667a191a36 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -63,9 +63,11 @@ static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) if (ipu_crtc->enabled) return; - ipu_di_enable(ipu_crtc->di); - ipu_dc_enable_channel(ipu_crtc->dc); + /* TODO: Enable DC module here, right now it is never disabled */ ipu_plane_enable(ipu_crtc->plane[0]); + /* Start DC channel and DI after IDMAC */ + ipu_dc_enable_channel(ipu_crtc->dc); + ipu_di_enable(ipu_crtc->di); ipu_crtc->enabled = 1; } @@ -75,9 +77,11 @@ static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) if (!ipu_crtc->enabled) return; - ipu_plane_disable(ipu_crtc->plane[0]); + /* Stop DC channel and DI before IDMAC */ ipu_dc_disable_channel(ipu_crtc->dc); ipu_di_disable(ipu_crtc->di); + ipu_plane_disable(ipu_crtc->plane[0]); + /* TODO: Disable DC module here */ ipu_crtc->enabled = 0; } From 1e6d486be4ea3f17c99c933703f53a6d566292a9 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 14 Apr 2014 23:53:23 +0200 Subject: [PATCH 9/9] imx-drm: ipu-dc: disable DC module when not in use Signed-off-by: Philipp Zabel Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 2 ++ drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 14 ++++++++++++-- drivers/staging/imx-drm/ipuv3-crtc.c | 8 ++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h index 8678ad18a3ef..c2c6fab05eaa 100644 --- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h @@ -115,8 +115,10 @@ struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel); void ipu_dc_put(struct ipu_dc *dc); int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, u32 pixel_fmt, u32 width); +void ipu_dc_enable(struct ipu_soc *ipu); void ipu_dc_enable_channel(struct ipu_dc *dc); void ipu_dc_disable_channel(struct ipu_dc *dc); +void ipu_dc_disable(struct ipu_soc *ipu); /* * IPU Display Interface (di) functions diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c index 280e494aee81..93b2709f81e1 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c @@ -224,12 +224,16 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, writel(0x0, dc->base + DC_WR_CH_ADDR); writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); - ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); - return 0; } EXPORT_SYMBOL_GPL(ipu_dc_init_sync); +void ipu_dc_enable(struct ipu_soc *ipu) +{ + ipu_module_enable(ipu, IPU_CONF_DC_EN); +} +EXPORT_SYMBOL_GPL(ipu_dc_enable); + void ipu_dc_enable_channel(struct ipu_dc *dc) { int di; @@ -286,6 +290,12 @@ void ipu_dc_disable_channel(struct ipu_dc *dc) } EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); +void ipu_dc_disable(struct ipu_soc *ipu) +{ + ipu_module_disable(ipu, IPU_CONF_DC_EN); +} +EXPORT_SYMBOL_GPL(ipu_dc_disable); + static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, int byte_num, int offset, int mask) { diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index ab667a191a36..47bec5e17358 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -60,10 +60,12 @@ struct ipu_crtc { static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) { + struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); + if (ipu_crtc->enabled) return; - /* TODO: Enable DC module here, right now it is never disabled */ + ipu_dc_enable(ipu); ipu_plane_enable(ipu_crtc->plane[0]); /* Start DC channel and DI after IDMAC */ ipu_dc_enable_channel(ipu_crtc->dc); @@ -74,6 +76,8 @@ static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) { + struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); + if (!ipu_crtc->enabled) return; @@ -81,7 +85,7 @@ static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) ipu_dc_disable_channel(ipu_crtc->dc); ipu_di_disable(ipu_crtc->di); ipu_plane_disable(ipu_crtc->plane[0]); - /* TODO: Disable DC module here */ + ipu_dc_disable(ipu); ipu_crtc->enabled = 0; }