summaryrefslogtreecommitdiff
path: root/drivers/gpu/ipu-v3/ipu-dc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/ipu-v3/ipu-dc.c')
-rw-r--r--drivers/gpu/ipu-v3/ipu-dc.c97
1 files changed, 73 insertions, 24 deletions
diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
index 9f1e5efa3acf..2326c752d89b 100644
--- a/drivers/gpu/ipu-v3/ipu-dc.c
+++ b/drivers/gpu/ipu-v3/ipu-dc.c
@@ -18,6 +18,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <video/imx-ipu-v3.h>
@@ -90,6 +91,7 @@ enum ipu_dc_map {
IPU_DC_MAP_RGB565,
IPU_DC_MAP_GBR24, /* TVEv2 */
IPU_DC_MAP_BGR666,
+ IPU_DC_MAP_LVDS666,
IPU_DC_MAP_BGR24,
};
@@ -109,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)
@@ -152,6 +157,8 @@ static int ipu_pixfmt_to_map(u32 fmt)
return IPU_DC_MAP_GBR24;
case V4L2_PIX_FMT_BGR666:
return IPU_DC_MAP_BGR666;
+ case v4l2_fourcc('L', 'V', 'D', '6'):
+ return IPU_DC_MAP_LVDS666;
case V4L2_PIX_FMT_BGR24:
return IPU_DC_MAP_BGR24;
default:
@@ -219,12 +226,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;
@@ -238,41 +249,55 @@ 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);
+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)
{
@@ -339,7 +364,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)
@@ -360,13 +385,31 @@ 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);
writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
priv->channels[5].base + DC_WR_CH_CONF);
- writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, priv->dc_reg + DC_GEN);
+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1,
+ priv->dc_reg + DC_GEN);
ipu->dc_priv = priv;
@@ -397,6 +440,12 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
+ /* lvds666 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666);
+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */
+
/* bgr24 */
ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */