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 <p.zabel@pengutronix.de> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
47348661c4
commit
1dee9a9e53
1 changed files with 50 additions and 21 deletions
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include "../imx-drm.h"
|
#include "../imx-drm.h"
|
||||||
|
@ -110,6 +111,9 @@ struct ipu_dc_priv {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
|
struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
|
||||||
struct mutex mutex;
|
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)
|
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);
|
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)
|
void ipu_dc_disable_channel(struct ipu_dc *dc)
|
||||||
{
|
{
|
||||||
struct ipu_dc_priv *priv = dc->priv;
|
struct ipu_dc_priv *priv = dc->priv;
|
||||||
|
int irq, ret;
|
||||||
u32 val;
|
u32 val;
|
||||||
int irq = 0, timeout = 50;
|
|
||||||
|
|
||||||
|
/* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */
|
||||||
if (dc->chno == 1)
|
if (dc->chno == 1)
|
||||||
irq = IPU_IRQ_DC_FC_1;
|
irq = priv->dc_irq;
|
||||||
else if (dc->chno == 5)
|
else if (dc->chno == 5)
|
||||||
irq = IPU_IRQ_DP_SF_END;
|
irq = priv->dp_irq;
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* should wait for the interrupt here */
|
init_completion(&priv->comp);
|
||||||
mdelay(50);
|
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 = readl(dc->base + DC_WR_CH_CONF);
|
||||||
val = 0x00000002;
|
val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
|
||||||
else
|
writel(val, dc->base + DC_WR_CH_CONF);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
|
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;
|
struct ipu_dc_priv *priv;
|
||||||
static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
|
static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
|
||||||
0x78, 0, 0x94, 0xb4};
|
0x78, 0, 0x94, 0xb4};
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
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->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) |
|
writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
|
||||||
DC_WR_CH_CONF_PROG_DI_ID,
|
DC_WR_CH_CONF_PROG_DI_ID,
|
||||||
priv->channels[1].base + DC_WR_CH_CONF);
|
priv->channels[1].base + DC_WR_CH_CONF);
|
||||||
|
|
Loading…
Add table
Reference in a new issue