All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers
@ 2016-07-07 23:03 Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
                   ` (17 more replies)
  0 siblings, 18 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

These updates to IPUv3 are needed for media staging drivers
for i.MX5/6 video capture and mem2mem.

Steve Longerbeam (15):
  gpu: ipu-v3: Add Video Deinterlacer unit
  gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  gpu: ipu-v3: Add ipu_get_num()
  gpu: ipu-v3: Add IDMA channel linking support
  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  gpu: ipu-v3: Add VDI input IDMAC channels
  gpu: ipu-v3: Add ipu_csi_set_src()
  gpu: ipu-v3: Add ipu_ic_set_src()
  gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  gpu: ipu-v3: Fix IRT usage
  gpu: ipu-ic: Add complete image conversion support with tiling
  gpu: ipu-ic: allow multiple handles to ic
  gpu: ipu-v3: rename CSI client device

Suresh Dhandapani (1):
  gpu: ipu-v3: Fix CSI0 blur in NTSC format

 drivers/gpu/ipu-v3/Makefile     |    2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  155 +++-
 drivers/gpu/ipu-v3/ipu-cpmem.c  |   13 +
 drivers/gpu/ipu-v3/ipu-csi.c    |   36 +-
 drivers/gpu/ipu-v3/ipu-ic.c     | 1769 ++++++++++++++++++++++++++++++++++++++-
 drivers/gpu/ipu-v3/ipu-prv.h    |    7 +
 drivers/gpu/ipu-v3/ipu-vdi.c    |  266 ++++++
 include/video/imx-ipu-v3.h      |   96 ++-
 8 files changed, 2283 insertions(+), 61 deletions(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 119+ messages in thread

* [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-11  0:02   ` Paul Gortmaker
  2016-07-07 23:03 ` [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds the Video Deinterlacer (VDIC) unit.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/Makefile     |   2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  11 ++
 drivers/gpu/ipu-v3/ipu-prv.h    |   6 +
 drivers/gpu/ipu-v3/ipu-vdi.c    | 266 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  27 ++++
 5 files changed, 311 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
index 107ec23..aeba9dc 100644
--- a/drivers/gpu/ipu-v3/Makefile
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
 imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
-		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
+		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 99dcacf..30dc115 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -833,6 +833,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
 		goto err_ic;
 	}
 
+	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
+			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
+			   IPU_CONF_IC_INPUT);
+	if (ret) {
+		unit = "vdi";
+		goto err_vdi;
+	}
+
 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
 			  IPU_CONF_DI0_EN, ipu_clk);
 	if (ret) {
@@ -887,6 +895,8 @@ err_dc:
 err_di_1:
 	ipu_di_exit(ipu, 0);
 err_di_0:
+	ipu_vdi_exit(ipu);
+err_vdi:
 	ipu_ic_exit(ipu);
 err_ic:
 	ipu_csi_exit(ipu, 1);
@@ -971,6 +981,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
 	ipu_dc_exit(ipu);
 	ipu_di_exit(ipu, 1);
 	ipu_di_exit(ipu, 0);
+	ipu_vdi_exit(ipu);
 	ipu_ic_exit(ipu);
 	ipu_csi_exit(ipu, 1);
 	ipu_csi_exit(ipu, 0);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index bfb1e8a..845f64c 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -138,6 +138,7 @@ struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
 struct ipu_ic_priv;
+struct ipu_vdi;
 struct ipu_smfc_priv;
 
 struct ipu_devtype;
@@ -169,6 +170,7 @@ struct ipu_soc {
 	struct ipu_di		*di_priv[2];
 	struct ipu_csi		*csi_priv[2];
 	struct ipu_ic_priv	*ic_priv;
+	struct ipu_vdi          *vdi_priv;
 	struct ipu_smfc_priv	*smfc_priv;
 };
 
@@ -199,6 +201,10 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 		unsigned long base, unsigned long tpmem_base);
 void ipu_ic_exit(struct ipu_soc *ipu);
 
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module);
+void ipu_vdi_exit(struct ipu_soc *ipu);
+
 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);
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
new file mode 100644
index 0000000..1303bcc
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-vdi.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2012 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <uapi/linux/v4l2-mediabus.h>
+
+#include "ipu-prv.h"
+
+struct ipu_vdi {
+	void __iomem *base;
+	u32 module;
+	spinlock_t lock;
+	int use_count;
+	struct ipu_soc *ipu;
+};
+
+
+/* VDI Register Offsets */
+#define VDI_FSIZE 0x0000
+#define VDI_C     0x0004
+
+/* VDI Register Fields */
+#define VDI_C_CH_420             (0 << 1)
+#define VDI_C_CH_422             (1 << 1)
+#define VDI_C_MOT_SEL_MASK       (0x3 << 2)
+#define VDI_C_MOT_SEL_FULL       (2 << 2)
+#define VDI_C_MOT_SEL_LOW        (1 << 2)
+#define VDI_C_MOT_SEL_MED        (0 << 2)
+#define VDI_C_BURST_SIZE1_4      (3 << 4)
+#define VDI_C_BURST_SIZE2_4      (3 << 8)
+#define VDI_C_BURST_SIZE3_4      (3 << 12)
+#define VDI_C_BURST_SIZE_MASK    0xF
+#define VDI_C_BURST_SIZE1_OFFSET 4
+#define VDI_C_BURST_SIZE2_OFFSET 8
+#define VDI_C_BURST_SIZE3_OFFSET 12
+#define VDI_C_VWM1_SET_1         (0 << 16)
+#define VDI_C_VWM1_SET_2         (1 << 16)
+#define VDI_C_VWM1_CLR_2         (1 << 19)
+#define VDI_C_VWM3_SET_1         (0 << 22)
+#define VDI_C_VWM3_SET_2         (1 << 22)
+#define VDI_C_VWM3_CLR_2         (1 << 25)
+#define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
+#define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
+
+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
+{
+	return readl(vdi->base + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
+				 unsigned int offset)
+{
+	writel(value, vdi->base + offset);
+}
+
+static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	if (top_field_0)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+static void __ipu_vdi_set_motion(struct ipu_vdi *vdi,
+				 enum ipu_motion_sel motion_sel)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+
+	reg &= ~VDI_C_MOT_SEL_MASK;
+
+	switch (motion_sel) {
+	case MED_MOTION:
+		reg |= VDI_C_MOT_SEL_MED;
+		break;
+	case HIGH_MOTION:
+		reg |= VDI_C_MOT_SEL_FULL;
+		break;
+	default:
+		reg |= VDI_C_MOT_SEL_LOW;
+		break;
+	}
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres,
+		   u32 field, enum ipu_motion_sel motion_sel)
+{
+	unsigned long flags;
+	u32 pixel_fmt, reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ((yres - 1) << 16) | (xres - 1);
+	ipu_vdi_write(vdi, reg, VDI_FSIZE);
+
+	/*
+	 * Full motion, only vertical filter is used.
+	 * Burst size is 4 accesses
+	 */
+	if (code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+	    code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+	    code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+	    code == MEDIA_BUS_FMT_YUYV8_1X16)
+		pixel_fmt = VDI_C_CH_422;
+	else
+		pixel_fmt = VDI_C_CH_420;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	reg |= pixel_fmt;
+	reg |= VDI_C_BURST_SIZE2_4;
+	reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
+	reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	if (field == V4L2_FIELD_INTERLACED_TB)
+		__ipu_vdi_set_top_field_man(vdi, false);
+	else if (field == V4L2_FIELD_INTERLACED_BT)
+		__ipu_vdi_set_top_field_man(vdi, true);
+
+	__ipu_vdi_set_motion(vdi, motion_sel);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_setup);
+
+void ipu_vdi_unsetup(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+	ipu_vdi_write(vdi, 0, VDI_FSIZE);
+	ipu_vdi_write(vdi, 0, VDI_C);
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
+
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 mask_reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+	if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
+
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
+{
+	ipu_set_vdi_src_mux(vdi->ipu, csi);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_src);
+
+int ipu_vdi_enable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (!vdi->use_count)
+		ipu_module_enable(vdi->ipu, vdi->module);
+
+	vdi->use_count++;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_enable);
+
+int ipu_vdi_disable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	vdi->use_count--;
+
+	if (!vdi->use_count)
+		ipu_module_disable(vdi->ipu, vdi->module);
+
+	if (vdi->use_count < 0)
+		vdi->use_count = 0;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_disable);
+
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
+{
+	return ipu->vdi_priv;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_get);
+
+void ipu_vdi_put(struct ipu_vdi *vdi)
+{
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_put);
+
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module)
+{
+	struct ipu_vdi *vdi;
+
+	vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
+	if (!vdi)
+		return -ENOMEM;
+
+	ipu->vdi_priv = vdi;
+
+	spin_lock_init(&vdi->lock);
+	vdi->module = module;
+	vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
+	if (!vdi->base)
+		return -ENOMEM;
+
+	dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
+	vdi->ipu = ipu;
+
+	return 0;
+}
+
+void ipu_vdi_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 3a2a794..22662a1 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -80,6 +80,16 @@ enum ipu_color_space {
 	IPUV3_COLORSPACE_UNKNOWN,
 };
 
+/*
+ * Enumeration of VDI MOTION select
+ */
+enum ipu_motion_sel {
+	MOTION_NONE = 0,
+	LOW_MOTION,
+	MED_MOTION,
+	HIGH_MOTION,
+};
+
 struct ipuv3_channel;
 
 enum ipu_channel_irq {
@@ -320,6 +330,23 @@ void ipu_ic_put(struct ipu_ic *ic);
 void ipu_ic_dump(struct ipu_ic *ic);
 
 /*
+ * IPU Video De-Interlacer (vdi) functions
+ */
+struct ipu_vdi;
+void ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0);
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
+void ipu_vdi_setup(struct ipu_vdi *vdi,
+		   u32 code, int xres, int yres, u32 field,
+		   enum ipu_motion_sel motion_sel);
+void ipu_vdi_unsetup(struct ipu_vdi *vdi);
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi);
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi);
+int ipu_vdi_enable(struct ipu_vdi *vdi);
+int ipu_vdi_disable(struct ipu_vdi *vdi);
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
+void ipu_vdi_put(struct ipu_vdi *vdi);
+
+/*
  * IPU Sensor Multiple FIFO Controller (SMFC) functions
  */
 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-08 17:34     ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 03/16] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 6494a4d..a36c35e 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
 
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
+{
+	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
+	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
+
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 22662a1..904fd12 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -194,6 +194,7 @@ void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
 void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
 void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 03/16] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 04/16] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds ipu_cpmem_get_burstsize().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 6 ++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 7 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index a36c35e..fcb7dc8 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -275,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
 
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
+{
+	return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
+
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 904fd12..60540ead 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -197,6 +197,7 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
 void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
 void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
 void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 04/16] gpu: ipu-v3: Add ipu_get_num()
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (2 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 03/16] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-15 12:45   ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds of-alias id to ipu_soc and retrieve with ipu_get_num().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 8 ++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    | 1 +
 include/video/imx-ipu-v3.h      | 1 +
 3 files changed, 10 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 30dc115..49af121 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
 	writel(value, ipu->cm_reg + offset);
 }
 
+int ipu_get_num(struct ipu_soc *ipu)
+{
+	return ipu->id;
+}
+EXPORT_SYMBOL_GPL(ipu_get_num);
+
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
 {
 	u32 val;
@@ -1220,6 +1226,7 @@ static int ipu_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
 			of_match_device(imx_ipu_dt_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
 	struct ipu_soc *ipu;
 	struct resource *res;
 	unsigned long ipu_base;
@@ -1248,6 +1255,7 @@ static int ipu_probe(struct platform_device *pdev)
 		ipu->channel[i].ipu = ipu;
 	ipu->devtype = devtype;
 	ipu->ipu_type = devtype->type;
+	ipu->id = of_alias_get_id(np, "ipu");
 
 	spin_lock_init(&ipu->lock);
 	mutex_init(&ipu->channel_lock);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 845f64c..02057d8 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -153,6 +153,7 @@ struct ipu_soc {
 	void __iomem		*cm_reg;
 	void __iomem		*idmac_reg;
 
+	int			id;
 	int			usecount;
 
 	struct clk		*clk;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 60540ead..b174f8a 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -148,6 +148,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 /*
  * IPU Common functions
  */
+int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
 void ipu_dump(struct ipu_soc *ipu);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (3 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 04/16] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-08 17:34     ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds functions to link and unlink IDMAC source channels to sink
channels.

So far the following links are supported:

IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP

More links can be added to the idmac_link_info[] array.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 112 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |   3 ++
 2 files changed, 115 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 49af121..6d1676e 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,118 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+
+/* IDMAC Channel Linking */
+
+struct idmac_link_reg_info {
+	int chno;
+	u32 reg;
+	int shift;
+	int bits;
+	u32 sel;
+};
+
+struct idmac_link_info {
+	struct idmac_link_reg_info src;
+	struct idmac_link_reg_info sink;
+};
+
+static const struct idmac_link_info idmac_link_info[] = {
+	{
+		.src  = { 20, IPU_FS_PROC_FLOW1,  0, 4, 7 },
+		.sink = { 45, IPU_FS_PROC_FLOW2,  0, 4, 1 },
+	}, {
+		.src =  { 21, IPU_FS_PROC_FLOW1,  8, 4, 8 },
+		.sink = { 46, IPU_FS_PROC_FLOW2,  4, 4, 1 },
+	}, {
+		.src =  { 22, IPU_FS_PROC_FLOW1, 16, 4, 5 },
+		.sink = { 47, IPU_FS_PROC_FLOW2, 12, 4, 3 },
+	},
+};
+
+static const struct idmac_link_info *find_idmac_link_info(
+	struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(idmac_link_info); i++) {
+		if (src->num == idmac_link_info[i].src.chno &&
+		    sink->num == idmac_link_info[i].sink.chno)
+			return &idmac_link_info[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Links an IDMAC source channel to a sink channel.
+ */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	src_reg |= (link->src.sel << link->src.shift);
+
+	sink_reg &= ~sink_mask;
+	sink_reg |= (link->sink.sel << link->sink.shift);
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/*
+ * Unlinks IDMAC source and sink channels.
+ */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	sink_reg &= ~sink_mask;
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
 struct ipu_devtype {
 	const char *name;
 	unsigned long cm_ofs;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index b174f8a..0a39c64 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -128,6 +128,7 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_ROT_VF_MEM		49
 #define IPUV3_CHANNEL_ROT_PP_MEM		50
 #define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA		51
+#define IPUV3_NUM_CHANNELS			64
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
@@ -171,6 +172,8 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink);
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink);
 
 /*
  * IPU Channel Parameter Memory (cpmem) functions
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (4 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-15 12:48     ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds ipu_set_vdi_src_mux() that selects the VDIC input
(from CSI or memory).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  1 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 6d1676e..374100e 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+/*
+ * Set the source for the VDIC. Selects either from CSI[01] or memory.
+ */
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+	val &= ~(0x3 << 28);
+	if (csi)
+		val |= (0x01 << 28);
+	ipu_cm_write(ipu, val, IPU_FS_PROC_FLOW1);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_vdi_src_mux);
+
 
 /* IDMAC Channel Linking */
 
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 0a39c64..586979e 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -152,6 +152,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi);
 void ipu_dump(struct ipu_soc *ipu);
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (5 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-15 12:45     ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds the VDIC field input IDMAC channels. These channels
transfer fields F(n-1), F(n), and F(N+1) from memory to
the VDIC (channels 8, 9, 10 respectively).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 include/video/imx-ipu-v3.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 586979e..2302fc5 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -107,6 +107,9 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_CSI2			 2
 #define IPUV3_CHANNEL_CSI3			 3
 #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
+#define IPUV3_CHANNEL_MEM_VDI_P			 8
+#define IPUV3_CHANNEL_MEM_VDI			 9
+#define IPUV3_CHANNEL_MEM_VDI_N			10
 #define IPUV3_CHANNEL_MEM_IC_PP			11
 #define IPUV3_CHANNEL_MEM_IC_PRP_VF		12
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src()
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (6 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-15 12:49   ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds ipu_csi_set_src() which is just a wrapper around
ipu_set_csi_src_mux().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 8 ++++++++
 include/video/imx-ipu-v3.h   | 1 +
 2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 06631ac..336dc06 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -609,6 +609,14 @@ int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 }
 EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
 
+int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2)
+{
+	ipu_set_csi_src_mux(csi->ipu, csi->id, select_mipi_csi2);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_src);
+
 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
 {
 	unsigned long flags;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 2302fc5..57b487d 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -301,6 +301,7 @@ int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
 			      struct v4l2_mbus_framefmt *mbus_fmt);
 int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 			  u32 max_ratio, u32 id);
+int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2);
 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest);
 int ipu_csi_enable(struct ipu_csi *csi);
 int ipu_csi_disable(struct ipu_csi *csi);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src()
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (7 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-15 12:45   ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 10/16] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Adds ipu_ic_set_src() which is just aa wrapper around
ipu_set_ic_src_mux().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 10 ++++++++++
 include/video/imx-ipu-v3.h  |  1 +
 2 files changed, 11 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1dcb96c..f306a9c 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -629,6 +629,16 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	ipu_set_ic_src_mux(priv->ipu, csi_id, vdi);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_set_src);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 57b487d..8f77ddb 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -334,6 +334,7 @@ void ipu_ic_task_disable(struct ipu_ic *ic);
 int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
+int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 10/16] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (8 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Set the sensor full frame based on whether the passed in mbus_fmt
is 720x480 (NTSC) or 720x576 (PAL).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 336dc06..07c7091 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -365,10 +365,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 {
 	struct ipu_csi_bus_config cfg;
 	unsigned long flags;
-	u32 data = 0;
+	u32 width, height, data = 0;
 
 	fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
 
+	/* set default sensor frame width and height */
+	width = mbus_fmt->width;
+	height = mbus_fmt->height;
+
 	/* Set the CSI_SENS_CONF register remaining fields */
 	data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
 		cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
@@ -386,11 +390,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 	ipu_csi_write(csi, data, CSI_SENS_CONF);
 
-	/* Setup sensor frame size */
-	ipu_csi_write(csi,
-		      (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
-		      CSI_SENS_FRM_SIZE);
-
 	/* Set CCIR registers */
 
 	switch (cfg.clk_mode) {
@@ -408,11 +407,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
 			 * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
 			 */
+			height = 625; /* framelines for PAL */
+
 			ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
-
 		} else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
 			/*
 			 * NTSC case
@@ -422,6 +422,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
 			 * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
 			 */
+			height = 525; /* framelines for NTSC */
+
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
@@ -447,6 +449,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 		break;
 	}
 
+	/* Setup sensor frame size */
+	ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
+		      CSI_SENS_FRM_SIZE);
+
 	dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
 		ipu_csi_read(csi, CSI_SENS_CONF));
 	dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (9 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 10/16] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-08 17:38     ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

The CSI data format was being programmed incorrectly for the
1x16 media bus formats. The CSI data format for 16-bit must
be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 07c7091..0eac28c 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
 		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 		break;
 	case MEDIA_BUS_FMT_UYVY8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-		cfg->mipi_dt = MIPI_DT_YUV422;
-		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
-		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 		cfg->mipi_dt = MIPI_DT_YUV422;
 		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
 		break;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (10 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-08 17:34     ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 13/16] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Suresh Dhandapani

From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>

This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
0x40596 to 0x405A6. The change is related to the Start of field 1
first blanking line command bit[5-3] for NTSC format only. This
change is dependent with ADV chip where the NEWAVMODE is set to 0
in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
codes generated to suit analog devices encoders".

Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 0eac28c..ec81958 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
-			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
+			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
 		} else {
 			dev_err(csi->ipu->dev,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 13/16] gpu: ipu-v3: Fix IRT usage
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (11 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 14/16] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

There can be multiple IC tasks using the IRT, so the IRT needs
a separate use counter. Create a private ipu_irt_enable() to
enable the IRT module when any IC task requires rotation, and
ipu_irt_disable() when a task no longer needs the IRT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index f306a9c..5329bfe 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -160,6 +160,7 @@ struct ipu_ic_priv {
 	spinlock_t lock;
 	struct ipu_soc *ipu;
 	int use_count;
+	int irt_use_count;
 	struct ipu_ic task[IC_NUM_TASKS];
 };
 
@@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
 
 	ipu_ic_write(ic, ic_conf, IC_CONF);
 
-	ic->rotation = ic->graphics = false;
-
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
@@ -639,22 +638,44 @@ int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_ic_set_src);
 
+static void ipu_irt_enable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (!priv->irt_use_count)
+		ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN);
+
+	priv->irt_use_count++;
+}
+
+static void ipu_irt_disable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	priv->irt_use_count--;
+
+	if (!priv->irt_use_count)
+		ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN);
+
+	if (priv->irt_use_count < 0)
+		priv->irt_use_count = 0;
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	if (ic->rotation)
-		module |= IPU_CONF_ROT_EN;
-
 	if (!priv->use_count)
-		ipu_module_enable(priv->ipu, module);
+		ipu_module_enable(priv->ipu, IPU_CONF_IC_EN);
 
 	priv->use_count++;
 
+	if (ic->rotation)
+		ipu_irt_enable(ic);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -665,18 +686,22 @@ int ipu_ic_disable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
 	priv->use_count--;
 
 	if (!priv->use_count)
-		ipu_module_disable(priv->ipu, module);
+		ipu_module_disable(priv->ipu, IPU_CONF_IC_EN);
 
 	if (priv->use_count < 0)
 		priv->use_count = 0;
 
+	if (ic->rotation)
+		ipu_irt_disable(ic);
+
+	ic->rotation = ic->graphics = false;
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 14/16] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (12 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 13/16] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 15/16] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

This patch implements complete image conversion support to ipu-ic,
with tiling to support scaling to and from images up to 4096x4096.
Image rotation is also supported.

The internal API is subsystem agnostic (no V4L2 dependency except
for the use of V4L2 fourcc pixel formats).

Callers prepare for image conversion by calling
ipu_image_convert_prepare(), which initializes the parameters of
the conversion. The caller passes in the ipu_ic task to use for
the conversion, the input and output image formats, a rotation mode,
and a completion callback and completion context pointer:

struct image_converter_ctx *
ipu_image_convert_prepare(struct ipu_ic *ic,
                          struct ipu_image *in, struct ipu_image *out,
                          enum ipu_rotate_mode rot_mode,
                          image_converter_cb_t complete,
                          void *complete_context);

The caller is given a new conversion context that must be passed to
the further APIs:

struct image_converter_run *
ipu_image_convert_run(struct image_converter_ctx *ctx,
                      dma_addr_t in_phys, dma_addr_t out_phys);

This queues a new image conversion request to a run queue, and
starts the conversion immediately if the run queue is empty. Only
the physaddr's of the input and output image buffers are needed,
since the conversion context was created previously with
ipu_image_convert_prepare(). Returns a new run object pointer. When
the conversion completes, the run pointer is returned to the
completion callback.

void image_convert_abort(struct image_converter_ctx *ctx);

This will abort any active or pending conversions for this context.
Any currently active or pending runs belonging to this context are
returned via the completion callback with an error status.

void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);

Unprepares the conversion context. Any active or pending runs will
be aborted by calling image_convert_abort().
---
 drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |   57 +-
 2 files changed, 1736 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 5329bfe..f6a1125 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -17,6 +17,8 @@
 #include <linux/bitrev.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
 #include "ipu-prv.h"
 
 /* IC Register Offsets */
@@ -82,6 +84,40 @@
 #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
 #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
 
+/*
+ * The IC Resizer has a restriction that the output frame from the
+ * resizer must be 1024 or less in both width (pixels) and height
+ * (lines).
+ *
+ * The image conversion support attempts to split up a conversion when
+ * the desired output (converted) frame resolution exceeds the IC resizer
+ * limit of 1024 in either dimension.
+ *
+ * If either dimension of the output frame exceeds the limit, the
+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum
+ * of 4*4 or 16 tiles. A conversion is then carried out for each
+ * tile (but taking care to pass the full frame stride length to
+ * the DMA channel's parameter memory!). IDMA double-buffering is used
+ * to convert each tile back-to-back when possible (see note below
+ * when double_buffering boolean is set).
+ *
+ * Note that the input frame must be split up into the same number
+ * of tiles as the output frame.
+ */
+#define MAX_STRIPES_W    4
+#define MAX_STRIPES_H    4
+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
+
+#define MIN_W     128
+#define MIN_H     128
+#define MAX_W     4096
+#define MAX_H     4096
+
+enum image_convert_type {
+	IMAGE_CONVERT_IN = 0,
+	IMAGE_CONVERT_OUT,
+};
+
 struct ic_task_regoffs {
 	u32 rsc;
 	u32 tpmem_csc[2];
@@ -96,6 +132,16 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	int in;
+	int out;
+	int rot_in;
+	int rot_out;
+	int vdi_in_p;
+	int vdi_in;
+	int vdi_in_n;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
 	},
 };
 
+static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
+	[IC_TASK_ENCODER] = {
+		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
+		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
+	},
+	[IC_TASK_VIEWFINDER] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_P,
+		.vdi_in = IPUV3_CHANNEL_MEM_VDI,
+		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_N,
+	},
+	[IC_TASK_POST_PROCESSOR] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PP,
+		.out = IPUV3_CHANNEL_IC_PP_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
+		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
+	},
+};
+
+struct ipu_ic_dma_buf {
+	void          *virt;
+	dma_addr_t    phys;
+	unsigned long len;
+};
+
+/* dimensions of one tile */
+struct ipu_ic_tile {
+	unsigned int width;
+	unsigned int height;
+	/* size and strides are in bytes */
+	unsigned int size;
+	unsigned int stride;
+	unsigned int rot_stride;
+};
+
+struct ipu_ic_tile_off {
+	/* start Y or packed offset of this tile */
+	u32     offset;
+	/* offset from start to tile in U plane, for planar formats */
+	u32     u_off;
+	/* offset from start to tile in V plane, for planar formats */
+	u32     v_off;
+};
+
+struct ipu_ic_pixfmt {
+	char	*name;
+	u32	fourcc;        /* V4L2 fourcc */
+	int     bpp;           /* total bpp */
+	int     y_depth;       /* depth of Y plane for planar formats */
+	int     uv_width_dec;  /* decimation in width for U/V planes */
+	int     uv_height_dec; /* decimation in height for U/V planes */
+	bool    uv_swapped;    /* U and V planes are swapped */
+	bool    uv_packed;     /* partial planar (U and V in same plane) */
+};
+
+struct ipu_ic_image {
+	struct ipu_image base;
+	enum image_convert_type type;
+
+	const struct ipu_ic_pixfmt *fmt;
+	unsigned int stride;
+
+	/* # of rows (horizontal stripes) if dest height is > 1024 */
+	unsigned int num_rows;
+	/* # of columns (vertical stripes) if dest width is > 1024 */
+	unsigned int num_cols;
+
+	struct ipu_ic_tile tile;
+	struct ipu_ic_tile_off tile_off[MAX_TILES];
+};
+
+struct image_converter_ctx;
+struct image_converter;
 struct ipu_ic_priv;
+struct ipu_ic;
+
+struct image_converter_run {
+	struct image_converter_ctx *ctx;
+
+	dma_addr_t in_phys;
+	dma_addr_t out_phys;
+
+	int status;
+
+	struct list_head list;
+};
+
+struct image_converter_ctx {
+	struct image_converter *cvt;
+
+	image_converter_cb_t complete;
+	void *complete_context;
+
+	/* Source/destination image data and rotation mode */
+	struct ipu_ic_image in;
+	struct ipu_ic_image out;
+	enum ipu_rotate_mode rot_mode;
+
+	/* intermediate buffer for rotation */
+	struct ipu_ic_dma_buf rot_intermediate[2];
+
+	/* current buffer number for double buffering */
+	int cur_buf_num;
+
+	bool aborting;
+	struct completion aborted;
+
+	/* can we use double-buffering for this conversion operation? */
+	bool double_buffering;
+	/* num_rows * num_cols */
+	unsigned int num_tiles;
+	/* next tile to process */
+	unsigned int next_tile;
+	/* where to place converted tile in dest image */
+	unsigned int out_tile_map[MAX_TILES];
+
+	struct list_head list;
+};
+
+struct image_converter {
+	struct ipu_ic *ic;
+
+	struct ipuv3_channel *in_chan;
+	struct ipuv3_channel *out_chan;
+	struct ipuv3_channel *rotation_in_chan;
+	struct ipuv3_channel *rotation_out_chan;
+
+	/* the IPU end-of-frame irqs */
+	int out_eof_irq;
+	int rot_out_eof_irq;
+
+	spinlock_t irqlock;
+
+	/* list of convert contexts */
+	struct list_head ctx_list;
+	/* queue of conversion runs */
+	struct list_head pending_q;
+	/* queue of completed runs */
+	struct list_head done_q;
+
+	/* the current conversion run */
+	struct image_converter_run *current_run;
+};
 
 struct ipu_ic {
 	enum ipu_ic_task task;
 	const struct ic_task_regoffs *reg;
 	const struct ic_task_bitfields *bit;
+	const struct ic_task_channels *ch;
 
 	enum ipu_color_space in_cs, g_in_cs;
 	enum ipu_color_space out_cs;
@@ -151,6 +344,8 @@ struct ipu_ic {
 	bool rotation;
 	bool in_use;
 
+	struct image_converter cvt;
+
 	struct ipu_ic_priv *priv;
 };
 
@@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
 	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
 
-	if (rot >= IPU_ROTATE_90_RIGHT)
+	if (ipu_rot_mode_is_irt(rot))
 		ic->rotation = true;
 
 unlock:
@@ -661,6 +856,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
 		priv->irt_use_count = 0;
 }
 
+/*
+ * Complete image conversion support follows
+ */
+
+static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.bpp    = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+	}, {
+		.name	= "4:2:0 planar, YVU",
+		.fourcc	= V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_swapped = true,
+	}, {
+		.name   = "4:2:0 partial planar, NV12",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_packed = true,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name   = "4:2:2 partial planar, NV16",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+		.uv_packed = true,
+	},
+};
+
+static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
+{
+	const struct ipu_ic_pixfmt *ret = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
+		if (ipu_ic_formats[i].fourcc == fourcc) {
+			ret = &ipu_ic_formats[i];
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
+			       struct ipu_ic_image *ic_image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev,
+		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
+		ctx,
+		ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
+		ic_image->base.pix.width, ic_image->base.pix.height,
+		ic_image->num_cols, ic_image->num_rows,
+		ic_image->tile.width, ic_image->tile.height,
+		ic_image->fmt->fourcc & 0xff,
+		(ic_image->fmt->fourcc >> 8) & 0xff,
+		(ic_image->fmt->fourcc >> 16) & 0xff,
+		(ic_image->fmt->fourcc >> 24) & 0xff);
+}
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
+{
+	const struct ipu_ic_pixfmt *fmt;
+
+	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
+		return -EINVAL;
+
+	/* Format found */
+	fmt = &ipu_ic_formats[index];
+	*desc = fmt->name;
+	*fourcc = fmt->fourcc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
+
+static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf)
+{
+	if (buf->virt)
+		dma_free_coherent(priv->ipu->dev,
+				  buf->len, buf->virt, buf->phys);
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf,
+				int size)
+{
+	unsigned long newlen = PAGE_ALIGN(size);
+
+	if (buf->virt) {
+		if (buf->len == newlen)
+			return 0;
+		ipu_ic_free_dma_buf(priv, buf);
+	}
+
+	buf->len = newlen;
+	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline int ipu_ic_num_stripes(int dim)
+{
+	if (dim <= 1024)
+		return 1;
+	else if (dim <= 2048)
+		return 2;
+	else
+		return 4;
+}
+
+static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
+					struct ipu_ic_image *image)
+{
+	struct ipu_ic_tile *tile = &image->tile;
+
+	tile->height = image->base.pix.height / image->num_rows;
+	tile->width = image->base.pix.width / image->num_cols;
+	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
+
+	if (image->fmt->y_depth) {
+		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
+	} else {
+		tile->stride = (image->fmt->bpp * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
+	}
+}
+
+/*
+ * Use the rotation transformation to find the tile coordinates
+ * (row, col) of a tile in the destination frame that corresponds
+ * to the given tile coordinates of a source frame. The destination
+ * coordinate is then converted to a tile index.
+ */
+static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
+				       int src_row, int src_col)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	int cos, sin, dst_row, dst_col;
+
+	/* with no rotation it's a 1:1 mapping */
+	if (ctx->rot_mode == IPU_ROTATE_NONE)
+		return src_row * s_image->num_cols + src_col;
+
+	if (ctx->rot_mode & IPU_ROT_BIT_90) {
+		cos = 0;
+		sin = 1;
+	} else {
+		cos = 1;
+		sin = 0;
+	}
+
+	/*
+	 * before doing the transform, first we have to translate
+	 * source row,col for an origin in the center of s_image
+	 */
+	src_row *= 2;
+	src_col *= 2;
+	src_row -= s_image->num_rows - 1;
+	src_col -= s_image->num_cols - 1;
+
+	/* do the rotation transform */
+	dst_col = src_col * cos - src_row * sin;
+	dst_row = src_col * sin + src_row * cos;
+
+	/* apply flip */
+	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
+		dst_col = -dst_col;
+	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
+		dst_row = -dst_row;
+
+	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
+		ctx, src_col, src_row, dst_col, dst_row);
+
+	/*
+	 * finally translate dest row,col using an origin in upper
+	 * left of d_image
+	 */
+	dst_row += d_image->num_rows - 1;
+	dst_col += d_image->num_cols - 1;
+	dst_row /= 2;
+	dst_col /= 2;
+
+	return dst_row * d_image->num_cols + dst_col;
+}
+
+/*
+ * Fill the out_tile_map[] with transformed destination tile indeces.
+ */
+static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
+{
+	struct ipu_ic_image *s_image = &ctx->in;
+	unsigned int row, col, tile = 0;
+
+	for (row = 0; row < s_image->num_rows; row++) {
+		for (col = 0; col < s_image->num_cols; col++) {
+			ctx->out_tile_map[tile] =
+				ipu_ic_transform_tile_index(ctx, row, col);
+			tile++;
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 H, w, h, y_depth, y_stride, uv_stride;
+	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
+	u32 y_row_off, y_col_off, y_off;
+	u32 y_size, uv_size;
+
+	/* setup some convenience vars */
+	H = image->base.pix.height;
+	w = image->tile.width;
+	h = image->tile.height;
+
+	y_depth = fmt->y_depth;
+	y_stride = image->stride;
+	uv_stride = y_stride / fmt->uv_width_dec;
+	if (fmt->uv_packed)
+		uv_stride *= 2;
+
+	y_size = H * y_stride;
+	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
+
+	for (row = 0; row < image->num_rows; row++) {
+		y_row_off = row * h * y_stride;
+		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
+
+		for (col = 0; col < image->num_cols; col++) {
+			y_col_off = (col * w * y_depth) >> 3;
+			uv_col_off = y_col_off / fmt->uv_width_dec;
+			if (fmt->uv_packed)
+				uv_col_off *= 2;
+
+			y_off = y_row_off + y_col_off;
+			uv_off = uv_row_off + uv_col_off;
+
+			u_off = y_size - y_off + uv_off;
+			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
+			if (fmt->uv_swapped) {
+				tmp = u_off;
+				u_off = v_off;
+				v_off = tmp;
+			}
+
+			image->tile_off[tile].offset = y_off;
+			image->tile_off[tile].u_off = u_off;
+			image->tile_off[tile++].v_off = v_off;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
+				ctx, image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				y_off, u_off, v_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 w, h, bpp, stride;
+	u32 row_off, col_off;
+
+	/* setup some convenience vars */
+	w = image->tile.width;
+	h = image->tile.height;
+	stride = image->stride;
+	bpp = fmt->bpp;
+
+	for (row = 0; row < image->num_rows; row++) {
+		row_off = row * h * stride;
+
+		for (col = 0; col < image->num_cols; col++) {
+			col_off = (col * w * bpp) >> 3;
+
+			image->tile_off[tile].offset = row_off + col_off;
+			image->tile_off[tile].u_off = 0;
+			image->tile_off[tile++].v_off = 0;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
+				image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				row_off + col_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
+				     struct ipu_ic_image *image)
+{
+	memset(image->tile_off, 0, sizeof(image->tile_off));
+
+	if (image->fmt->y_depth)
+		ipu_ic_calc_tile_offsets_planar(ctx, image);
+	else
+		ipu_ic_calc_tile_offsets_packed(ctx, image);
+}
+
+/*
+ * return the number of runs in given queue (pending_q or done_q)
+ * for this context. hold irqlock when calling.
+ */
+static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
+				struct list_head *q)
+{
+	struct image_converter_run *run;
+	int count = 0;
+
+	list_for_each_entry(run, q, list) {
+		if (run->ctx == ctx)
+			count++;
+	}
+
+	return count;
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_convert_stop(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
+		__func__, ctx, run);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(cvt->ic);
+	ipu_idmac_disable_channel(cvt->in_chan);
+	ipu_idmac_disable_channel(cvt->out_chan);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_disable_channel(cvt->rotation_in_chan);
+		ipu_idmac_disable_channel(cvt->rotation_out_chan);
+		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
+	}
+
+	ipu_ic_disable(cvt->ic);
+}
+
+/* hold irqlock when calling */
+static void init_idmac_channel(struct image_converter_ctx *ctx,
+			       struct ipuv3_channel *channel,
+			       struct ipu_ic_image *image,
+			       enum ipu_rotate_mode rot_mode,
+			       bool rot_swap_width_height)
+{
+	struct image_converter *cvt = ctx->cvt;
+	unsigned int burst_size;
+	u32 width, height, stride;
+	dma_addr_t addr0, addr1 = 0;
+	struct ipu_image tile_image;
+	unsigned int tile_idx[2];
+
+	if (image->type == IMAGE_CONVERT_OUT) {
+		tile_idx[0] = ctx->out_tile_map[0];
+		tile_idx[1] = ctx->out_tile_map[1];
+	} else {
+		tile_idx[0] = 0;
+		tile_idx[1] = 1;
+	}
+
+	if (rot_swap_width_height) {
+		width = image->tile.height;
+		height = image->tile.width;
+		stride = image->tile.rot_stride;
+		addr0 = ctx->rot_intermediate[0].phys;
+		if (ctx->double_buffering)
+			addr1 = ctx->rot_intermediate[1].phys;
+	} else {
+		width = image->tile.width;
+		height = image->tile.height;
+		stride = image->stride;
+		addr0 = image->base.phys0 +
+			image->tile_off[tile_idx[0]].offset;
+		if (ctx->double_buffering)
+			addr1 = image->base.phys0 +
+				image->tile_off[tile_idx[1]].offset;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&tile_image, 0, sizeof(tile_image));
+	tile_image.pix.width = tile_image.rect.width = width;
+	tile_image.pix.height = tile_image.rect.height = height;
+	tile_image.pix.bytesperline = stride;
+	tile_image.pix.pixelformat =  image->fmt->fourcc;
+	tile_image.phys0 = addr0;
+	tile_image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &tile_image);
+
+	if (image->fmt->y_depth && !rot_swap_width_height)
+		ipu_cpmem_set_uv_offset(channel,
+					image->tile_off[tile_idx[0]].u_off,
+					image->tile_off[tile_idx[0]].v_off);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (channel == cvt->rotation_in_chan ||
+	    channel == cvt->rotation_out_chan) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else
+		burst_size = (width % 16) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
+			      burst_size, rot_mode);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_convert_start(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	enum ipu_color_space src_cs, dest_cs;
+	unsigned int dest_width, dest_height;
+	int ret;
+
+	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
+		__func__, ctx, run);
+
+	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
+	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* swap width/height for resizer */
+		dest_width = d_image->tile.height;
+		dest_height = d_image->tile.width;
+	} else {
+		dest_width = d_image->tile.width;
+		dest_height = d_image->tile.height;
+	}
+
+	/* setup the IC resizer and CSC */
+	ret = ipu_ic_task_init(cvt->ic,
+			       s_image->tile.width,
+			       s_image->tile.height,
+			       dest_width,
+			       dest_height,
+			       src_cs, dest_cs);
+	if (ret) {
+		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
+		return ret;
+	}
+
+	/* init the source MEM-->IC PP IDMAC channel */
+	init_idmac_channel(ctx, cvt->in_chan, s_image,
+			   IPU_ROTATE_NONE, false);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* init the IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   IPU_ROTATE_NONE, true);
+
+		/* init the MEM-->IC PP ROT IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
+				   ctx->rot_mode, true);
+
+		/* init the destination IC PP ROT-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
+				   IPU_ROTATE_NONE, false);
+
+		/* now link IC PP-->MEM to MEM-->IC PP ROT */
+		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
+	} else {
+		/* init the destination IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   ctx->rot_mode, false);
+	}
+
+	/* enable the IC */
+	ipu_ic_enable(cvt->ic);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(cvt->in_chan, 0);
+	ipu_idmac_select_buffer(cvt->out_chan, 0);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode))
+		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
+	if (ctx->double_buffering) {
+		ipu_idmac_select_buffer(cvt->in_chan, 1);
+		ipu_idmac_select_buffer(cvt->out_chan, 1);
+		if (ipu_rot_mode_is_irt(ctx->rot_mode))
+			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
+	}
+
+	/* enable the channels! */
+	ipu_idmac_enable_channel(cvt->in_chan);
+	ipu_idmac_enable_channel(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_enable_channel(cvt->rotation_in_chan);
+		ipu_idmac_enable_channel(cvt->rotation_out_chan);
+	}
+
+	ipu_ic_task_enable(cvt->ic);
+
+	ipu_cpmem_dump(cvt->in_chan);
+	ipu_cpmem_dump(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_cpmem_dump(cvt->rotation_in_chan);
+		ipu_cpmem_dump(cvt->rotation_out_chan);
+	}
+
+	ipu_dump(priv->ipu);
+
+	return 0;
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_run(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+
+	ctx->in.base.phys0 = run->in_phys;
+	ctx->out.base.phys0 = run->out_phys;
+
+	ctx->cur_buf_num = 0;
+	ctx->next_tile = 1;
+
+	/* remove run from pending_q and set as current */
+	list_del(&run->list);
+	cvt->current_run = run;
+
+	return ipu_ic_convert_start(run);
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_run_next(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		/* skip contexts that are aborting */
+		if (run->ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: skipping aborting ctx %p run %p\n",
+				 __func__, run->ctx, run);
+			continue;
+		}
+
+		ret = ipu_ic_run(run);
+		if (!ret)
+			break;
+
+		/*
+		 * something went wrong with start, add the run
+		 * to done q and continue to the next run in the
+		 * pending q.
+		 */
+		run->status = ret;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+	}
+}
+
+static void ipu_ic_empty_done_q(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	while (!list_empty(&cvt->done_q)) {
+		run = list_entry(cvt->done_q.next,
+				 struct image_converter_run,
+				 list);
+
+		list_del(&run->list);
+
+		dev_dbg(priv->ipu->dev,
+			"%s: completing ctx %p run %p with %d\n",
+			__func__, run->ctx, run, run->status);
+
+		/* call the completion callback and free the run */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		run->ctx->complete(run->ctx->complete_context, run,
+				   run->status);
+		kfree(run);
+		spin_lock_irqsave(&cvt->irqlock, flags);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+}
+
+/*
+ * the bottom half thread clears out the done_q, calling the
+ * completion handler for each.
+ */
+static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
+{
+	struct image_converter *cvt = dev_id;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+
+	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
+
+	ipu_ic_empty_done_q(cvt);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/*
+	 * the done_q is cleared out, signal any contexts
+	 * that are aborting that abort can complete.
+	 */
+	list_for_each_entry(ctx, &cvt->ctx_list, list) {
+		if (ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: signaling abort for ctx %p\n",
+				 __func__, ctx);
+			complete(&ctx->aborted);
+		}
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
+	return IRQ_HANDLED;
+}
+
+/* hold irqlock when calling */
+static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_tile_off *src_off, *dst_off;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	struct ipuv3_channel *outch;
+	unsigned int dst_idx;
+
+	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
+		cvt->rotation_out_chan : cvt->out_chan;
+
+	/*
+	 * It is difficult to stop the channel DMA before the channels
+	 * enter the paused state. Without double-buffering the channels
+	 * are always in a paused state when the EOF irq occurs, so it
+	 * is safe to stop the channels now. For double-buffering we
+	 * just ignore the abort until the operation completes, when it
+	 * is safe to shut down.
+	 */
+	if (ctx->aborting && !ctx->double_buffering) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		goto done;
+	}
+
+	if (ctx->next_tile == ctx->num_tiles) {
+		/*
+		 * the conversion is complete
+		 */
+		ipu_ic_convert_stop(run);
+		run->status = 0;
+		goto done;
+	}
+
+	/*
+	 * not done, place the next tile buffers.
+	 */
+	if (!ctx->double_buffering) {
+
+		src_off = &s_image->tile_off[ctx->next_tile];
+		dst_idx = ctx->out_tile_map[ctx->next_tile];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, 0,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, 0,
+				     d_image->base.phys0 + dst_off->offset);
+		if (s_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(cvt->in_chan,
+						src_off->u_off,
+						src_off->v_off);
+		if (d_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(outch,
+						dst_off->u_off,
+						dst_off->v_off);
+
+		ipu_idmac_select_buffer(cvt->in_chan, 0);
+		ipu_idmac_select_buffer(outch, 0);
+
+	} else if (ctx->next_tile < ctx->num_tiles - 1) {
+
+		src_off = &s_image->tile_off[ctx->next_tile + 1];
+		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
+				     d_image->base.phys0 + dst_off->offset);
+
+		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
+		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
+
+		ctx->cur_buf_num ^= 1;
+	}
+
+	ctx->next_tile++;
+	return IRQ_HANDLED;
+done:
+	list_add_tail(&run->list, &cvt->done_q);
+	cvt->current_run = NULL;
+	ipu_ic_run_next(cvt);
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this is a rotation operation, just ignore */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this was NOT a rotation operation, shouldn't happen */
+		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+/*
+ * try to force the completion of runs for this ctx. Called when
+ * abort wait times out in ipu_image_convert_abort().
+ */
+static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	run = cvt->current_run;
+	if (run && run->ctx == ctx) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+		ipu_ic_run_next(cvt);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	ipu_ic_empty_done_q(cvt);
+}
+
+static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
+{
+	if (cvt->out_eof_irq >= 0)
+		free_irq(cvt->out_eof_irq, cvt);
+	if (cvt->rot_out_eof_irq >= 0)
+		free_irq(cvt->rot_out_eof_irq, cvt);
+
+	if (!IS_ERR_OR_NULL(cvt->in_chan))
+		ipu_idmac_put(cvt->in_chan);
+	if (!IS_ERR_OR_NULL(cvt->out_chan))
+		ipu_idmac_put(cvt->out_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
+		ipu_idmac_put(cvt->rotation_in_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
+		ipu_idmac_put(cvt->rotation_out_chan);
+
+	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan =
+		cvt->rotation_out_chan = NULL;
+	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
+}
+
+static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
+{
+	const struct ic_task_channels *chan = cvt->ic->ch;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	int ret;
+
+	/* get IDMAC channels */
+	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
+	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
+	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
+		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
+	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
+	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
+		dev_err(priv->ipu->dev,
+			"could not acquire idmac rotation channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* acquire the EOF interrupts */
+	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						cvt->out_chan,
+						IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->out_eof_irq,
+				   ipu_ic_norotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			 cvt->out_eof_irq);
+		cvt->out_eof_irq = -1;
+		goto err;
+	}
+
+	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						     cvt->rotation_out_chan,
+						     IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->rot_out_eof_irq,
+				   ipu_ic_rotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			cvt->rot_out_eof_irq);
+		cvt->rot_out_eof_irq = -1;
+		goto err;
+	}
+
+	return 0;
+err:
+	ipu_ic_release_ipu_resources(cvt);
+	return ret;
+}
+
+static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
+			     struct ipu_ic_image *ic_image,
+			     struct ipu_image *image,
+			     enum image_convert_type type)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	ic_image->base = *image;
+	ic_image->type = type;
+
+	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
+	if (!ic_image->fmt) {
+		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
+			type == IMAGE_CONVERT_OUT ? "Output" : "Input");
+		return -EINVAL;
+	}
+
+	if (ic_image->fmt->y_depth)
+		ic_image->stride = (ic_image->fmt->y_depth *
+				    ic_image->base.pix.width) >> 3;
+	else
+		ic_image->stride  = ic_image->base.pix.bytesperline;
+
+	ipu_ic_calc_tile_dimensions(ctx, ic_image);
+	ipu_ic_calc_tile_offsets(ctx, ic_image);
+
+	return 0;
+}
+
+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+				unsigned int max, unsigned int align)
+{
+	/* Bits that must be zero to be aligned */
+	unsigned int mask = ~((1 << align) - 1);
+
+	/* Clamp to aligned min and max */
+	x = clamp(x, (min + ~mask) & mask, max & mask);
+
+	/* Round to nearest aligned value */
+	if (align)
+		x = (x + (1 << (align - 1))) & mask;
+
+	return x;
+}
+
+/*
+ * We have to adjust the tile width such that the tile physaddrs and
+ * U and V plane offsets are multiples of 8 bytes as required by
+ * the IPU DMA Controller. For the planar formats, this corresponds
+ * to a pixel alignment of 16 (but use a more formal equation since
+ * the variables are available). For all the packed formats, 8 is
+ * good enough.
+ */
+static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
+{
+	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
+}
+
+/*
+ * For tile height alignment, we have to ensure that the output tile
+ * heights are multiples of 8 lines if the IRT is required by the
+ * given rotation mode (the IRT performs rotations on 8x8 blocks
+ * at a time). If the IRT is not used, or for input image tiles,
+ * 2 lines are good enough.
+ */
+static inline u32 tile_height_align(enum image_convert_type type,
+				    enum ipu_rotate_mode rot_mode)
+{
+	return (type == IMAGE_CONVERT_OUT &&
+		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
+}
+
+/* Adjusts input/output images to IPU restrictions */
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	const struct ipu_ic_pixfmt *infmt, *outfmt;
+	unsigned int num_in_rows, num_in_cols;
+	unsigned int num_out_rows, num_out_cols;
+	u32 w_align, h_align;
+
+	infmt = ipu_ic_get_format(in->pix.pixelformat);
+	outfmt = ipu_ic_get_format(out->pix.pixelformat);
+
+	/* set some defaults if needed */
+	if (!infmt) {
+		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+	if (!outfmt) {
+		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+
+	if (!in->pix.width || !in->pix.height) {
+		in->pix.width = 640;
+		in->pix.height = 480;
+	}
+	if (!out->pix.width || !out->pix.height) {
+		out->pix.width = 640;
+		out->pix.height = 480;
+	}
+
+	/* image converter does not handle fields */
+	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.width / 4);
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.height / 4);
+	} else {
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.width / 4);
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.height / 4);
+	}
+
+	/* get tiling rows/cols from output format */
+	num_out_rows = ipu_ic_num_stripes(out->pix.height);
+	num_out_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		num_in_rows = num_out_cols;
+		num_in_cols = num_out_rows;
+	} else {
+		num_in_rows = num_out_rows;
+		num_in_cols = num_out_cols;
+	}
+
+	/* align input width/height */
+	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
+			num_in_rows);
+	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
+	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
+
+	/* align output width/height */
+	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
+			num_out_rows);
+	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
+	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
+
+	/* set input/output strides and image sizes */
+	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
+	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
+	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
+	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
+
+/*
+ * this is used by ipu_image_convert_prepare() to verify set input and
+ * output images are valid before starting the conversion. Clients can
+ * also call it before calling ipu_image_convert_prepare().
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	struct ipu_image testin, testout;
+	int ret;
+
+	testin = *in;
+	testout = *out;
+
+	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
+	if (ret)
+		return ret;
+
+	if (testin.pix.width != in->pix.width ||
+	    testin.pix.height != in->pix.height ||
+	    testout.pix.width != out->pix.width ||
+	    testout.pix.height != out->pix.height)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
+
+/*
+ * Call ipu_image_convert_prepare() to prepare for the conversion of
+ * given images and rotation mode. Returns a new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct image_converter *cvt = &ic->cvt;
+	struct ipu_ic_image *s_image, *d_image;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+	bool get_res;
+	int ret;
+
+	if (!ic || !in || !out || !complete)
+		return ERR_PTR(-EINVAL);
+
+	/* verify the in/out images before continuing */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret) {
+		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
+			__func__);
+		return ERR_PTR(ret);
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
+
+	ctx->cvt = cvt;
+	init_completion(&ctx->aborted);
+
+	s_image = &ctx->in;
+	d_image = &ctx->out;
+
+	/* set tiling and rotation */
+	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
+	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		s_image->num_rows = d_image->num_cols;
+		s_image->num_cols = d_image->num_rows;
+	} else {
+		s_image->num_rows = d_image->num_rows;
+		s_image->num_cols = d_image->num_cols;
+	}
+
+	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
+	ctx->rot_mode = rot_mode;
+
+	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
+	if (ret)
+		goto out_free;
+	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
+	if (ret)
+		goto out_free;
+
+	ipu_ic_calc_out_tile_map(ctx);
+
+	ipu_ic_dump_format(ctx, s_image);
+	ipu_ic_dump_format(ctx, d_image);
+
+	ctx->complete = complete;
+	ctx->complete_context = complete_context;
+
+	/*
+	 * Can we use double-buffering for this operation? If there is
+	 * only one tile (the whole image can be converted in a single
+	 * operation) there's no point in using double-buffering. Also,
+	 * the IPU's IDMAC channels allow only a single U and V plane
+	 * offset shared between both buffers, but these offsets change
+	 * for every tile, and therefore would have to be updated for
+	 * each buffer which is not possible. So double-buffering is
+	 * impossible when either the source or destination images are
+	 * a planar format (YUV420, YUV422P, etc.).
+	 */
+	ctx->double_buffering = (ctx->num_tiles > 1 &&
+				 !s_image->fmt->y_depth &&
+				 !d_image->fmt->y_depth);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
+					   d_image->tile.size);
+		if (ret)
+			goto out_free;
+		if (ctx->double_buffering) {
+			ret = ipu_ic_alloc_dma_buf(priv,
+						   &ctx->rot_intermediate[1],
+						   d_image->tile.size);
+			if (ret)
+				goto out_free_dmabuf0;
+		}
+	}
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	get_res = list_empty(&cvt->ctx_list);
+
+	list_add_tail(&ctx->list, &cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (get_res) {
+		ret = ipu_ic_get_ipu_resources(cvt);
+		if (ret)
+			goto out_free_dmabuf1;
+	}
+
+	return ctx;
+
+out_free_dmabuf1:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	spin_lock_irqsave(&cvt->irqlock, flags);
+	list_del(&ctx->list);
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+out_free_dmabuf0:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+out_free:
+	kfree(ctx);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+/*
+ * Carry out a single image conversion. Only the physaddr's of the input
+ * and output image buffers are needed. The conversion context must have
+ * been created previously with ipu_image_convert_prepare(). Returns the
+ * new run object.
+ */
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+	int ret = 0;
+
+	run = kzalloc(sizeof(*run), GFP_KERNEL);
+	if (!run)
+		return ERR_PTR(-ENOMEM);
+
+	run->ctx = ctx;
+	run->in_phys = in_phys;
+	run->out_phys = out_phys;
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
+		ctx, run);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	if (ctx->aborting) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	list_add_tail(&run->list, &cvt->pending_q);
+
+	if (!cvt->current_run) {
+		ret = ipu_ic_run(run);
+		if (ret)
+			cvt->current_run = NULL;
+	}
+unlock:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (ret) {
+		kfree(run);
+		run = ERR_PTR(ret);
+	}
+
+	return run;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+/* Abort any active or pending conversions for this context */
+void ipu_image_convert_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *active_run, *tmp;
+	unsigned long flags;
+	int run_count, ret;
+	bool need_abort;
+
+	reinit_completion(&ctx->aborted);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* move all remaining pending runs in this context to done_q */
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		if (run->ctx != ctx)
+			continue;
+		run->status = -EIO;
+		list_move_tail(&run->list, &cvt->done_q);
+	}
+
+	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
+	active_run = (cvt->current_run && cvt->current_run->ctx == ctx) ?
+		cvt->current_run : NULL;
+
+	need_abort = (run_count || active_run);
+
+	ctx->aborting = need_abort;
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (!need_abort) {
+		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
+			__func__, ctx);
+		return;
+	}
+
+	dev_dbg(priv->ipu->dev,
+		 "%s: wait for completion: %d runs, active run %p\n",
+		 __func__, run_count, active_run);
+
+	ret = wait_for_completion_timeout(&ctx->aborted,
+					  msecs_to_jiffies(10000));
+	if (ret == 0) {
+		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
+		ipu_ic_force_abort(ctx);
+	}
+
+	ctx->aborting = false;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
+
+/* Unprepare image conversion context */
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	unsigned long flags;
+	bool put_res;
+
+	/* make sure no runs are hanging around */
+	ipu_image_convert_abort(ctx);
+
+	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	list_del(&ctx->list);
+
+	put_res = list_empty(&cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (put_res)
+		ipu_ic_release_ipu_resources(cvt);
+
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+
+	kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
+
+/*
+ * "Canned" asynchronous single image conversion. On successful return
+ * caller must call ipu_image_convert_unprepare() after conversion completes.
+ * Returns the new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context)
+{
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+
+	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
+					complete, complete_context);
+	if (IS_ERR(ctx))
+		return ctx;
+
+	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
+	if (IS_ERR(run)) {
+		ipu_image_convert_unprepare(ctx);
+		return ERR_PTR(PTR_ERR(run));
+	}
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert);
+
+/* "Canned" synchronous single image conversion */
+static void image_convert_sync_complete(void *data,
+					struct image_converter_run *run,
+					int err)
+{
+	struct completion *comp = data;
+
+	complete(comp);
+}
+
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode)
+{
+	struct image_converter_ctx *ctx;
+	struct completion comp;
+	int ret;
+
+	init_completion(&comp);
+
+	ctx = ipu_image_convert(ic, in, out, rot_mode,
+				image_convert_sync_complete, &comp);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
+	ret = (ret == 0) ? -ETIMEDOUT : 0;
+
+	ipu_image_convert_unprepare(ctx);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -759,6 +2428,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	ipu->ic_priv = priv;
 
 	spin_lock_init(&priv->lock);
+
 	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
 	if (!priv->base)
 		return -ENOMEM;
@@ -771,10 +2441,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
-		priv->task[i].task = i;
-		priv->task[i].priv = priv;
-		priv->task[i].reg = &ic_task_reg[i];
-		priv->task[i].bit = &ic_task_bit[i];
+		struct ipu_ic *ic = &priv->task[i];
+		struct image_converter *cvt = &ic->cvt;
+
+		ic->task = i;
+		ic->priv = priv;
+		ic->reg = &ic_task_reg[i];
+		ic->bit = &ic_task_bit[i];
+		ic->ch = &ic_task_ch[i];
+
+		cvt->ic = ic;
+		spin_lock_init(&cvt->irqlock);
+		INIT_LIST_HEAD(&cvt->ctx_list);
+		INIT_LIST_HEAD(&cvt->pending_q);
+		INIT_LIST_HEAD(&cvt->done_q);
+		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
 	}
 
 	return 0;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 8f77ddb..5938a69 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -63,17 +63,25 @@ enum ipu_csi_dest {
 /*
  * Enumeration of IPU rotation modes
  */
+#define IPU_ROT_BIT_VFLIP (1 << 0)
+#define IPU_ROT_BIT_HFLIP (1 << 1)
+#define IPU_ROT_BIT_90    (1 << 2)
+
 enum ipu_rotate_mode {
 	IPU_ROTATE_NONE = 0,
-	IPU_ROTATE_VERT_FLIP,
-	IPU_ROTATE_HORIZ_FLIP,
-	IPU_ROTATE_180,
-	IPU_ROTATE_90_RIGHT,
-	IPU_ROTATE_90_RIGHT_VFLIP,
-	IPU_ROTATE_90_RIGHT_HFLIP,
-	IPU_ROTATE_90_LEFT,
+	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
+	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
+	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
+	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
+	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
+			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
 };
 
+/* 90-degree rotations require the IRT unit */
+#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
+
 enum ipu_color_space {
 	IPUV3_COLORSPACE_RGB,
 	IPUV3_COLORSPACE_YUV,
@@ -320,6 +328,7 @@ enum ipu_ic_task {
 };
 
 struct ipu_ic;
+
 int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
@@ -335,6 +344,40 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
 int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
+
+struct image_converter_ctx;
+struct image_converter_run;
+
+typedef void (*image_converter_cb_t)(void *ctx,
+				     struct image_converter_run *run,
+				     int err);
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context);
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys);
+void ipu_image_convert_abort(struct image_converter_ctx *ctx);
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context);
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode);
+
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 15/16] gpu: ipu-ic: allow multiple handles to ic
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (13 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 14/16] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 16/16] gpu: ipu-v3: rename CSI client device Steve Longerbeam
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

The image converter kernel API supports conversion contexts and
job queues, so we should allow more than one handle to the IC, so
that multiple users can add jobs to the queue.

Note however that users that control the IC manually (that do not
use the image converter APIs but setup the IC task by hand via calls
to ipu_ic_task_enable(), ipu_ic_enable(), etc.) must still be careful not
to share the IC handle with other threads. At this point, the only user
that still controls the IC manually is the i.mx capture driver. In that
case the capture driver only allows one open context to get a handle
to the IC at a time, so we should be ok there.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index f6a1125..51e34a1 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -342,7 +342,6 @@ struct ipu_ic {
 	enum ipu_color_space out_cs;
 	bool graphics;
 	bool rotation;
-	bool in_use;
 
 	struct image_converter cvt;
 
@@ -2380,38 +2379,16 @@ EXPORT_SYMBOL_GPL(ipu_ic_disable);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
 {
 	struct ipu_ic_priv *priv = ipu->ic_priv;
-	unsigned long flags;
-	struct ipu_ic *ic, *ret;
 
 	if (task >= IC_NUM_TASKS)
 		return ERR_PTR(-EINVAL);
 
-	ic = &priv->task[task];
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	if (ic->in_use) {
-		ret = ERR_PTR(-EBUSY);
-		goto unlock;
-	}
-
-	ic->in_use = true;
-	ret = ic;
-
-unlock:
-	spin_unlock_irqrestore(&priv->lock, flags);
-	return ret;
+	return &priv->task[task];
 }
 EXPORT_SYMBOL_GPL(ipu_ic_get);
 
 void ipu_ic_put(struct ipu_ic *ic)
 {
-	struct ipu_ic_priv *priv = ic->priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ic->in_use = false;
-	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_put);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH 16/16] gpu: ipu-v3: rename CSI client device
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
                   ` (14 preceding siblings ...)
  2016-07-07 23:03 ` [PATCH 15/16] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
@ 2016-07-07 23:03 ` Steve Longerbeam
  2016-07-08 17:34   ` Philipp Zabel
  2016-07-20  1:10   ` Steve Longerbeam
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-07 23:03 UTC (permalink / raw)
  To: p.zabel; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Rename the CSI client device in the client_reg[] table to
"imx-ipuv3-csi".

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 374100e..bd6771b 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1153,14 +1153,14 @@ static struct ipu_platform_reg client_reg[] = {
 			.dma[0] = IPUV3_CHANNEL_CSI0,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.csi = 1,
 			.dma[0] = IPUV3_CHANNEL_CSI1,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.di = 0,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* Re: [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
@ 2016-07-08 17:34   ` Philipp Zabel
  2016-07-07 23:03 ` [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
                     ` (16 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Hi Steve,

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> These updates to IPUv3 are needed for media staging drivers
> for i.MX5/6 video capture and mem2mem.

Thank you for the update, I'll have a proper look at this next week.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers
@ 2016-07-08 17:34   ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, linux-kernel, dri-devel

Hi Steve,

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> These updates to IPUv3 are needed for media staging drivers
> for i.MX5/6 video capture and mem2mem.

Thank you for the update, I'll have a proper look at this next week.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-07 23:03 ` [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
@ 2016-07-08 17:34     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Suresh Dhandapani

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> 
> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
> 0x40596 to 0x405A6. The change is related to the Start of field 1
> first blanking line command bit[5-3] for NTSC format only. This
> change is dependent with ADV chip where the NEWAVMODE is set to 0
> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
> codes generated to suit analog devices encoders".
> 
> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> ---
>  drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> index 0eac28c..ec81958 100644
> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>  
>  			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>  					  CSI_CCIR_CODE_1);
> -			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
> +			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>  			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>  		} else {
>  			dev_err(csi->ipu->dev,

This looks like a very hardware specific hack? I'll at least have to
test if that also works with other analog decoders.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-08 17:34     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-kernel, dri-devel, Suresh Dhandapani

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> 
> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
> 0x40596 to 0x405A6. The change is related to the Start of field 1
> first blanking line command bit[5-3] for NTSC format only. This
> change is dependent with ADV chip where the NEWAVMODE is set to 0
> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
> codes generated to suit analog devices encoders".
> 
> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> ---
>  drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> index 0eac28c..ec81958 100644
> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>  
>  			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>  					  CSI_CCIR_CODE_1);
> -			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
> +			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>  			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>  		} else {
>  			dev_err(csi->ipu->dev,

This looks like a very hardware specific hack? I'll at least have to
test if that also works with other analog decoders.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-07-07 23:03 ` [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
@ 2016-07-08 17:34     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
>  include/video/imx-ipu-v3.h     | 1 +
>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
> index 6494a4d..a36c35e 100644
> --- a/drivers/gpu/ipu-v3/ipu-cpmem.c
> +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
> @@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
>  }
>  EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
>  
> +void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
> +{
> +	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
> +	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
> +}
> +EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
> +

I'd prefer to avoid too much duplication in the API. Is there a reason
you can't use ipu_cpmem_set_yuv_planar_full?

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
@ 2016-07-08 17:34     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, linux-kernel, dri-devel

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
>  include/video/imx-ipu-v3.h     | 1 +
>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
> index 6494a4d..a36c35e 100644
> --- a/drivers/gpu/ipu-v3/ipu-cpmem.c
> +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
> @@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
>  }
>  EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
>  
> +void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
> +{
> +	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
> +	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
> +}
> +EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
> +

I'd prefer to avoid too much duplication in the API. Is there a reason
you can't use ipu_cpmem_set_yuv_planar_full?

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-07 23:03 ` [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
@ 2016-07-08 17:34     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds functions to link and unlink IDMAC source channels to sink
> channels.
> 
> So far the following links are supported:
> 
> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
> 
> More links can be added to the idmac_link_info[] array.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-common.c | 112 ++++++++++++++++++++++++++++++++++++++++
>  include/video/imx-ipu-v3.h      |   3 ++
>  2 files changed, 115 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
> index 49af121..6d1676e 100644
> --- a/drivers/gpu/ipu-v3/ipu-common.c
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> @@ -730,6 +730,118 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
>  }
>  EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
>  
> +
> +/* IDMAC Channel Linking */
> +
> +struct idmac_link_reg_info {
> +	int chno;
> +	u32 reg;
> +	int shift;
> +	int bits;
> +	u32 sel;
> +};
> +
> +struct idmac_link_info {
> +	struct idmac_link_reg_info src;
> +	struct idmac_link_reg_info sink;
> +};
> +
> +static const struct idmac_link_info idmac_link_info[] = {
> +	{
> +		.src  = { 20, IPU_FS_PROC_FLOW1,  0, 4, 7 },
> +		.sink = { 45, IPU_FS_PROC_FLOW2,  0, 4, 1 },

Please use the defines here:
	IPUV3_CHANNEL_IC_PRP_ENC_MEM
	IPUV3_CHANNEL_MEM_ROT_ENC

> +	}, {
> +		.src =  { 21, IPU_FS_PROC_FLOW1,  8, 4, 8 },
> +		.sink = { 46, IPU_FS_PROC_FLOW2,  4, 4, 1 },

and so on.

> +	}, {
> +		.src =  { 22, IPU_FS_PROC_FLOW1, 16, 4, 5 },
> +		.sink = { 47, IPU_FS_PROC_FLOW2, 12, 4, 3 },
> +	},
> +};
[...]

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-08 17:34     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:34 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, linux-kernel, dri-devel

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds functions to link and unlink IDMAC source channels to sink
> channels.
> 
> So far the following links are supported:
> 
> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
> 
> More links can be added to the idmac_link_info[] array.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-common.c | 112 ++++++++++++++++++++++++++++++++++++++++
>  include/video/imx-ipu-v3.h      |   3 ++
>  2 files changed, 115 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
> index 49af121..6d1676e 100644
> --- a/drivers/gpu/ipu-v3/ipu-common.c
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> @@ -730,6 +730,118 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
>  }
>  EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
>  
> +
> +/* IDMAC Channel Linking */
> +
> +struct idmac_link_reg_info {
> +	int chno;
> +	u32 reg;
> +	int shift;
> +	int bits;
> +	u32 sel;
> +};
> +
> +struct idmac_link_info {
> +	struct idmac_link_reg_info src;
> +	struct idmac_link_reg_info sink;
> +};
> +
> +static const struct idmac_link_info idmac_link_info[] = {
> +	{
> +		.src  = { 20, IPU_FS_PROC_FLOW1,  0, 4, 7 },
> +		.sink = { 45, IPU_FS_PROC_FLOW2,  0, 4, 1 },

Please use the defines here:
	IPUV3_CHANNEL_IC_PRP_ENC_MEM
	IPUV3_CHANNEL_MEM_ROT_ENC

> +	}, {
> +		.src =  { 21, IPU_FS_PROC_FLOW1,  8, 4, 8 },
> +		.sink = { 46, IPU_FS_PROC_FLOW2,  4, 4, 1 },

and so on.

> +	}, {
> +		.src =  { 22, IPU_FS_PROC_FLOW1, 16, 4, 5 },
> +		.sink = { 47, IPU_FS_PROC_FLOW2, 12, 4, 3 },
> +	},
> +};
[...]

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  2016-07-07 23:03 ` [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
@ 2016-07-08 17:38     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:38 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> The CSI data format was being programmed incorrectly for the
> 1x16 media bus formats. The CSI data format for 16-bit must
> be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

I think this is not the case for BT.1120, but right now it that isn't
supported yet anyway.

> Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
>  1 file changed, 1 insertion(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> index 07c7091..0eac28c 100644
> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> @@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
>  		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
>  		break;
>  	case MEDIA_BUS_FMT_UYVY8_1X16:
> -		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
> -		cfg->mipi_dt = MIPI_DT_YUV422;
> -		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
> -		break;
>  	case MEDIA_BUS_FMT_YUYV8_1X16:
> -		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
> +		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
>  		cfg->mipi_dt = MIPI_DT_YUV422;
>  		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
>  		break;

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
@ 2016-07-08 17:38     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-08 17:38 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, linux-kernel, dri-devel

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> The CSI data format was being programmed incorrectly for the
> 1x16 media bus formats. The CSI data format for 16-bit must
> be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

I think this is not the case for BT.1120, but right now it that isn't
supported yet anyway.

> Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
>  1 file changed, 1 insertion(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> index 07c7091..0eac28c 100644
> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> @@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
>  		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
>  		break;
>  	case MEDIA_BUS_FMT_UYVY8_1X16:
> -		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
> -		cfg->mipi_dt = MIPI_DT_YUV422;
> -		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
> -		break;
>  	case MEDIA_BUS_FMT_YUYV8_1X16:
> -		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
> +		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
>  		cfg->mipi_dt = MIPI_DT_YUV422;
>  		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
>  		break;

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-08 17:34     ` Philipp Zabel
@ 2016-07-10 16:33       ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-10 16:33 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: dri-devel, linux-kernel, Suresh Dhandapani



On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>
>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>> first blanking line command bit[5-3] for NTSC format only. This
>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>> codes generated to suit analog devices encoders".
>>
>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>> ---
>>   drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
>> index 0eac28c..ec81958 100644
>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>   
>>   			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>   					  CSI_CCIR_CODE_1);
>> -			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>> +			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>   			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>   		} else {
>>   			dev_err(csi->ipu->dev,
> This looks like a very hardware specific hack? I'll at least have to
> test if that also works with other analog decoders.

Hi Philipp,

Yes it's a hack, but it has always been a hack (hardcoded values). And the
reason is simple, nobody AFAIK (including me) understands how to program
these CSI_CCIR_CODE registers, the description in the reference manual is
complete gibberish.

The reason we made this change is that, in discussions with Analog Devices,
they recommended setting NEWAVMODE, which changes the positions of
the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
a full day of reverse engineering (Suresh correct me if I am wrong) to hit
on the correct values in these registers to regain stable video after 
switching
the ADV7180 to NEWAVMODE.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-10 16:33       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-10 16:33 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: dri-devel, linux-kernel, Suresh Dhandapani



On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>
>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>> first blanking line command bit[5-3] for NTSC format only. This
>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>> codes generated to suit analog devices encoders".
>>
>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>> ---
>>   drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
>> index 0eac28c..ec81958 100644
>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>   
>>   			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>   					  CSI_CCIR_CODE_1);
>> -			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>> +			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>   			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>   		} else {
>>   			dev_err(csi->ipu->dev,
> This looks like a very hardware specific hack? I'll at least have to
> test if that also works with other analog decoders.

Hi Philipp,

Yes it's a hack, but it has always been a hack (hardcoded values). And the
reason is simple, nobody AFAIK (including me) understands how to program
these CSI_CCIR_CODE registers, the description in the reference manual is
complete gibberish.

The reason we made this change is that, in discussions with Analog Devices,
they recommended setting NEWAVMODE, which changes the positions of
the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
a full day of reverse engineering (Suresh correct me if I am wrong) to hit
on the correct values in these registers to regain stable video after 
switching
the ADV7180 to NEWAVMODE.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-07 23:03 ` [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
@ 2016-07-11  0:02   ` Paul Gortmaker
  2016-07-15 22:35     ` Steve Longerbeam
  0 siblings, 1 reply; 119+ messages in thread
From: Paul Gortmaker @ 2016-07-11  0:02 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: p.zabel, dri-devel, LKML, Steve Longerbeam

On Thu, Jul 7, 2016 at 7:03 PM, Steve Longerbeam <slongerbeam@gmail.com> wrote:
> Adds the Video Deinterlacer (VDIC) unit.
>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/Makefile     |   2 +-
>  drivers/gpu/ipu-v3/ipu-common.c |  11 ++
>  drivers/gpu/ipu-v3/ipu-prv.h    |   6 +
>  drivers/gpu/ipu-v3/ipu-vdi.c    | 266 ++++++++++++++++++++++++++++++++++++++++
>  include/video/imx-ipu-v3.h      |  27 ++++
>  5 files changed, 311 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c
>
> diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
> index 107ec23..aeba9dc 100644
> --- a/drivers/gpu/ipu-v3/Makefile
> +++ b/drivers/gpu/ipu-v3/Makefile
> @@ -1,4 +1,4 @@
>  obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
>
>  imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
> -               ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
> +               ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o ipu-vdi.o
> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
> index 99dcacf..30dc115 100644
> --- a/drivers/gpu/ipu-v3/ipu-common.c
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> @@ -833,6 +833,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
>                 goto err_ic;
>         }
>
> +       ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
> +                          IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
> +                          IPU_CONF_IC_INPUT);
> +       if (ret) {
> +               unit = "vdi";
> +               goto err_vdi;
> +       }
> +
>         ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
>                           IPU_CONF_DI0_EN, ipu_clk);
>         if (ret) {
> @@ -887,6 +895,8 @@ err_dc:
>  err_di_1:
>         ipu_di_exit(ipu, 0);
>  err_di_0:
> +       ipu_vdi_exit(ipu);
> +err_vdi:
>         ipu_ic_exit(ipu);
>  err_ic:
>         ipu_csi_exit(ipu, 1);
> @@ -971,6 +981,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
>         ipu_dc_exit(ipu);
>         ipu_di_exit(ipu, 1);
>         ipu_di_exit(ipu, 0);
> +       ipu_vdi_exit(ipu);
>         ipu_ic_exit(ipu);
>         ipu_csi_exit(ipu, 1);
>         ipu_csi_exit(ipu, 0);
> diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
> index bfb1e8a..845f64c 100644
> --- a/drivers/gpu/ipu-v3/ipu-prv.h
> +++ b/drivers/gpu/ipu-v3/ipu-prv.h
> @@ -138,6 +138,7 @@ struct ipu_dc_priv;
>  struct ipu_dmfc_priv;
>  struct ipu_di;
>  struct ipu_ic_priv;
> +struct ipu_vdi;
>  struct ipu_smfc_priv;
>
>  struct ipu_devtype;
> @@ -169,6 +170,7 @@ struct ipu_soc {
>         struct ipu_di           *di_priv[2];
>         struct ipu_csi          *csi_priv[2];
>         struct ipu_ic_priv      *ic_priv;
> +       struct ipu_vdi          *vdi_priv;
>         struct ipu_smfc_priv    *smfc_priv;
>  };
>
> @@ -199,6 +201,10 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
>                 unsigned long base, unsigned long tpmem_base);
>  void ipu_ic_exit(struct ipu_soc *ipu);
>
> +int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
> +                unsigned long base, u32 module);
> +void ipu_vdi_exit(struct ipu_soc *ipu);
> +
>  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);
> diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
> new file mode 100644
> index 0000000..1303bcc
> --- /dev/null
> +++ b/drivers/gpu/ipu-v3/ipu-vdi.c
> @@ -0,0 +1,266 @@
> +/*
> + * Copyright (C) 2012 Mentor Graphics Inc.
> + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> + * for more details.
> + */
> +#include <linux/export.h>
> +#include <linux/module.h>

You have a u32 field in a struct called "modules" but aside from that, I do not
see anything in this code requiring module.h -- did I miss something?

You might want export.h for EXPORT_SYMBOL though.

Paul.
--

> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <uapi/linux/v4l2-mediabus.h>
> +
> +#include "ipu-prv.h"
> +

[...]

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-07-08 17:34     ` Philipp Zabel
@ 2016-07-13 22:54       ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-13 22:54 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel

Hi Philipp,

On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>  drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
>>  include/video/imx-ipu-v3.h     | 1 +
>>  2 files changed, 8 insertions(+)
>>
>> diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
>> index 6494a4d..a36c35e 100644
>> --- a/drivers/gpu/ipu-v3/ipu-cpmem.c
>> +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
>> @@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
>>  }
>>  EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
>>  
>> +void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
>> +{
>> +	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
>> +	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
>> +}
>> +EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
>> +
> I'd prefer to avoid too much duplication in the API. Is there a reason
> you can't use ipu_cpmem_set_yuv_planar_full?

I could, but it would be cumbersome. ipu_cpmem_set_uv_offset() is being
called when kicking off a new tile conversion, and the stride length would
have to be calculated all over again if I were to use ipu_cpmem_set_yuv_planar_ful().
When kicking off a new tile conversion, the stride doesn't change, only the
tile's buffer address, and U/V offsets for planar, need to be updated.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
@ 2016-07-13 22:54       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-13 22:54 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel

Hi Philipp,

On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>  drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
>>  include/video/imx-ipu-v3.h     | 1 +
>>  2 files changed, 8 insertions(+)
>>
>> diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
>> index 6494a4d..a36c35e 100644
>> --- a/drivers/gpu/ipu-v3/ipu-cpmem.c
>> +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
>> @@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
>>  }
>>  EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
>>  
>> +void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
>> +{
>> +	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
>> +	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
>> +}
>> +EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
>> +
> I'd prefer to avoid too much duplication in the API. Is there a reason
> you can't use ipu_cpmem_set_yuv_planar_full?

I could, but it would be cumbersome. ipu_cpmem_set_uv_offset() is being
called when kicking off a new tile conversion, and the stride length would
have to be calculated all over again if I were to use ipu_cpmem_set_yuv_planar_ful().
When kicking off a new tile conversion, the stride doesn't change, only the
tile's buffer address, and U/V offsets for planar, need to be updated.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-08 17:34     ` Philipp Zabel
@ 2016-07-13 22:55       ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-13 22:55 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel

Hi Philipp,

On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>
> Please use the defines here:
> 	IPUV3_CHANNEL_IC_PRP_ENC_MEM
> 	IPUV3_CHANNEL_MEM_ROT_ENC
>

Right, I will fix in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-13 22:55       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-13 22:55 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel

Hi Philipp,

On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>
> Please use the defines here:
> 	IPUV3_CHANNEL_IC_PRP_ENC_MEM
> 	IPUV3_CHANNEL_MEM_ROT_ENC
>

Right, I will fix in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-10 16:33       ` Steve Longerbeam
@ 2016-07-13 23:02         ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-13 23:02 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Suresh Dhandapani

On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
>
>
> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>
>>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>>> first blanking line command bit[5-3] for NTSC format only. This
>>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>>> codes generated to suit analog devices encoders".
>>>
>>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>> ---
>>>   drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
>>> index 0eac28c..ec81958 100644
>>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>>                 ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>>                         CSI_CCIR_CODE_1);
>>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>>               ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>>           } else {
>>>               dev_err(csi->ipu->dev,
>> This looks like a very hardware specific hack? I'll at least have to
>> test if that also works with other analog decoders.
>
> Hi Philipp,
>
> Yes it's a hack, but it has always been a hack (hardcoded values). And the
> reason is simple, nobody AFAIK (including me) understands how to program
> these CSI_CCIR_CODE registers, the description in the reference manual is
> complete gibberish.

Hi Philipp, Ian over at linux-media helped me to understand these registers a
little better, although there are still mysteries given the poor documentation.
You should have been copied on that linux-media thread.

>
> The reason we made this change is that, in discussions with Analog Devices,
> they recommended setting NEWAVMODE, which changes the positions of
> the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
> a full day of reverse engineering (Suresh correct me if I am wrong) to hit
> on the correct values in these registers to regain stable video after switching
> the ADV7180 to NEWAVMODE.

So this NEWAVMODE is somehow breaking from the BT.656 standard, which
necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
the ADV7180 will break other capture backends that are expecting standard
BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
this patch in the next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-13 23:02         ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-13 23:02 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Suresh Dhandapani

On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
>
>
> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>
>>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>>> first blanking line command bit[5-3] for NTSC format only. This
>>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>>> codes generated to suit analog devices encoders".
>>>
>>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>> ---
>>>   drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
>>> index 0eac28c..ec81958 100644
>>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>>                 ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>>                         CSI_CCIR_CODE_1);
>>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>>               ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>>           } else {
>>>               dev_err(csi->ipu->dev,
>> This looks like a very hardware specific hack? I'll at least have to
>> test if that also works with other analog decoders.
>
> Hi Philipp,
>
> Yes it's a hack, but it has always been a hack (hardcoded values). And the
> reason is simple, nobody AFAIK (including me) understands how to program
> these CSI_CCIR_CODE registers, the description in the reference manual is
> complete gibberish.

Hi Philipp, Ian over at linux-media helped me to understand these registers a
little better, although there are still mysteries given the poor documentation.
You should have been copied on that linux-media thread.

>
> The reason we made this change is that, in discussions with Analog Devices,
> they recommended setting NEWAVMODE, which changes the positions of
> the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
> a full day of reverse engineering (Suresh correct me if I am wrong) to hit
> on the correct values in these registers to regain stable video after switching
> the ADV7180 to NEWAVMODE.

So this NEWAVMODE is somehow breaking from the BT.656 standard, which
necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
the ADV7180 will break other capture backends that are expecting standard
BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
this patch in the next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-07-13 22:54       ` Steve Longerbeam
  (?)
@ 2016-07-15 12:45       ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:45 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, dri-devel, linux-kernel

Am Mittwoch, den 13.07.2016, 15:54 -0700 schrieb Steve Longerbeam:
> Hi Philipp,
> 
> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> > Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> >> Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.
> >>
> >> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> >> ---
> >>  drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
> >>  include/video/imx-ipu-v3.h     | 1 +
> >>  2 files changed, 8 insertions(+)
> >>
> >> diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
> >> index 6494a4d..a36c35e 100644
> >> --- a/drivers/gpu/ipu-v3/ipu-cpmem.c
> >> +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
> >> @@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
> >>  }
> >>  EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
> >>  
> >> +void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
> >> +{
> >> +	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
> >> +	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
> >> +
> > I'd prefer to avoid too much duplication in the API. Is there a reason
> > you can't use ipu_cpmem_set_yuv_planar_full?
> 
> I could, but it would be cumbersome. ipu_cpmem_set_uv_offset() is being
> called when kicking off a new tile conversion, and the stride length would
> have to be calculated all over again if I were to use ipu_cpmem_set_yuv_planar_ful().
> When kicking off a new tile conversion, the stride doesn't change, only the
> tile's buffer address, and U/V offsets for planar, need to be updated.

Ok. We'll have to change tile width and height as well, but the stride
indeed is constant.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels
  2016-07-07 23:03 ` [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
@ 2016-07-15 12:45     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:45 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds the VDIC field input IDMAC channels. These channels
> transfer fields F(n-1), F(n), and F(N+1) from memory to
> the VDIC (channels 8, 9, 10 respectively).
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  include/video/imx-ipu-v3.h | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
> index 586979e..2302fc5 100644
> --- a/include/video/imx-ipu-v3.h
> +++ b/include/video/imx-ipu-v3.h
> @@ -107,6 +107,9 @@ enum ipu_channel_irq {
>  #define IPUV3_CHANNEL_CSI2			 2
>  #define IPUV3_CHANNEL_CSI3			 3
>  #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
> +#define IPUV3_CHANNEL_MEM_VDI_P			 8
> +#define IPUV3_CHANNEL_MEM_VDI			 9
> +#define IPUV3_CHANNEL_MEM_VDI_N			10

I would prefer the names to be just a bit more descriptive:

#define IPUV3_CHANNEL_MEM_VDI_PREV		 8
#define IPUV3_CHANNEL_MEM_VDI_CUR		 9
#define IPUV3_CHANNEL_MEM_VDI_NEXT		10

The reference manual states that the purposes of these channels are
"Previous field", "Current field", and "Next field" input to the VDIC,
respectively.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels
@ 2016-07-15 12:45     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:45 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, linux-kernel, dri-devel

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds the VDIC field input IDMAC channels. These channels
> transfer fields F(n-1), F(n), and F(N+1) from memory to
> the VDIC (channels 8, 9, 10 respectively).
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  include/video/imx-ipu-v3.h | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
> index 586979e..2302fc5 100644
> --- a/include/video/imx-ipu-v3.h
> +++ b/include/video/imx-ipu-v3.h
> @@ -107,6 +107,9 @@ enum ipu_channel_irq {
>  #define IPUV3_CHANNEL_CSI2			 2
>  #define IPUV3_CHANNEL_CSI3			 3
>  #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
> +#define IPUV3_CHANNEL_MEM_VDI_P			 8
> +#define IPUV3_CHANNEL_MEM_VDI			 9
> +#define IPUV3_CHANNEL_MEM_VDI_N			10

I would prefer the names to be just a bit more descriptive:

#define IPUV3_CHANNEL_MEM_VDI_PREV		 8
#define IPUV3_CHANNEL_MEM_VDI_CUR		 9
#define IPUV3_CHANNEL_MEM_VDI_NEXT		10

The reference manual states that the purposes of these channels are
"Previous field", "Current field", and "Next field" input to the VDIC,
respectively.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 04/16] gpu: ipu-v3: Add ipu_get_num()
  2016-07-07 23:03 ` [PATCH 04/16] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
@ 2016-07-15 12:45   ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:45 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds of-alias id to ipu_soc and retrieve with ipu_get_num().
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

This looks good to me, the necessary DT aliases are already in mainline
since commit 41beef39cdc8 ("ARM: dts: imx6qdl: add IPU aliases").

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src()
  2016-07-07 23:03 ` [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
@ 2016-07-15 12:45   ` Philipp Zabel
  2016-07-15 21:57       ` Steve Longerbeam
  0 siblings, 1 reply; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:45 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds ipu_ic_set_src() which is just aa wrapper around
> ipu_set_ic_src_mux().
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-ic.c | 10 ++++++++++
>  include/video/imx-ipu-v3.h  |  1 +
>  2 files changed, 11 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index 1dcb96c..f306a9c 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -629,6 +629,16 @@ unlock:
>  }
>  EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
>  
> +int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
> +{
> +	struct ipu_ic_priv *priv = ic->priv;
> +
> +	ipu_set_ic_src_mux(priv->ipu, csi_id, vdi);

	ipu_set_ic_src_mux(ic->priv->ipu, csi_id, vdi);
would be shorter.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  2016-07-07 23:03 ` [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
@ 2016-07-15 12:48     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:48 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds ipu_set_vdi_src_mux() that selects the VDIC input
> (from CSI or memory).
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
>  include/video/imx-ipu-v3.h      |  1 +
>  2 files changed, 21 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
> index 6d1676e..374100e 100644
> --- a/drivers/gpu/ipu-v3/ipu-common.c
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> @@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
>  }
>  EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
>  
> +/*
> + * Set the source for the VDIC. Selects either from CSI[01] or memory.
> + */
> +void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
> +{
> +	unsigned long flags;
> +	u32 val;
> +
> +	spin_lock_irqsave(&ipu->lock, flags);
> +
> +	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
> +	val &= ~(0x3 << 28);
> +	if (csi)
> +		val |= (0x01 << 28);

This doesn't set a bus mux, or does it? As I understand it, this
configures the FSU to trigger the VDIC conversion from the CSI direct
channel sync signals (IC channel CB7). As such I believe this belongs
together with the other channel linking code, maybe even in ipu-fsu.c.

Also, some #defines would be nice, for example:

#define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
#define FS_VDI_SRC_SEL_OFFSET		28
#define FS_VDI_SRC_SEL_CSI_DIRECT	0x1
#define FS_VDI_SRC_SEL_VDOA		0x2

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
@ 2016-07-15 12:48     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:48 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: Steve Longerbeam, linux-kernel, dri-devel

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds ipu_set_vdi_src_mux() that selects the VDIC input
> (from CSI or memory).
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
>  include/video/imx-ipu-v3.h      |  1 +
>  2 files changed, 21 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
> index 6d1676e..374100e 100644
> --- a/drivers/gpu/ipu-v3/ipu-common.c
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> @@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
>  }
>  EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
>  
> +/*
> + * Set the source for the VDIC. Selects either from CSI[01] or memory.
> + */
> +void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
> +{
> +	unsigned long flags;
> +	u32 val;
> +
> +	spin_lock_irqsave(&ipu->lock, flags);
> +
> +	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
> +	val &= ~(0x3 << 28);
> +	if (csi)
> +		val |= (0x01 << 28);

This doesn't set a bus mux, or does it? As I understand it, this
configures the FSU to trigger the VDIC conversion from the CSI direct
channel sync signals (IC channel CB7). As such I believe this belongs
together with the other channel linking code, maybe even in ipu-fsu.c.

Also, some #defines would be nice, for example:

#define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
#define FS_VDI_SRC_SEL_OFFSET		28
#define FS_VDI_SRC_SEL_CSI_DIRECT	0x1
#define FS_VDI_SRC_SEL_VDOA		0x2

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src()
  2016-07-07 23:03 ` [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
@ 2016-07-15 12:49   ` Philipp Zabel
  2016-07-15 21:45       ` Steve Longerbeam
  0 siblings, 1 reply; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:49 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Steve Longerbeam

Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> Adds ipu_csi_set_src() which is just a wrapper around
> ipu_set_csi_src_mux().
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-csi.c | 8 ++++++++
>  include/video/imx-ipu-v3.h   | 1 +
>  2 files changed, 9 insertions(+)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> index 06631ac..336dc06 100644
> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> @@ -609,6 +609,14 @@ int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
>  }
>  EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
>  
> +int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2)

vc is unused.

> +{
> +	ipu_set_csi_src_mux(csi->ipu, csi->id, select_mipi_csi2);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ipu_csi_set_src);
> +

Couldn't you just replace ipu_set_csi_src_mux?

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-13 23:02         ` Steve Longerbeam
@ 2016-07-15 12:58           ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:58 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Suresh Dhandapani

Am Mittwoch, den 13.07.2016, 16:02 -0700 schrieb Steve Longerbeam:
> On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
> >
> >
> > On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> >> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> >>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> >>>
> >>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
> >>> 0x40596 to 0x405A6. The change is related to the Start of field 1
> >>> first blanking line command bit[5-3] for NTSC format only. This
> >>> change is dependent with ADV chip where the NEWAVMODE is set to 0
> >>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
> >>> codes generated to suit analog devices encoders".
> >>>
> >>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> >>> ---
> >>>   drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
> >>>   1 file changed, 1 insertion(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> >>> index 0eac28c..ec81958 100644
> >>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> >>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> >>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
> >>>                 ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
> >>>                         CSI_CCIR_CODE_1);
> >>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
> >>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
> >>>               ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
> >>>           } else {
> >>>               dev_err(csi->ipu->dev,
> >> This looks like a very hardware specific hack? I'll at least have to
> >> test if that also works with other analog decoders.
> >
> > Hi Philipp,
> >
> > Yes it's a hack, but it has always been a hack (hardcoded values). And the
> > reason is simple, nobody AFAIK (including me) understands how to program
> > these CSI_CCIR_CODE registers, the description in the reference manual is
> > complete gibberish.
> 
> Hi Philipp, Ian over at linux-media helped me to understand these registers a
> little better, although there are still mysteries given the poor documentation.
> You should have been copied on that linux-media thread.
> 
> >
> > The reason we made this change is that, in discussions with Analog Devices,
> > they recommended setting NEWAVMODE, which changes the positions of
> > the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
> > a full day of reverse engineering (Suresh correct me if I am wrong) to hit
> > on the correct values in these registers to regain stable video after switching
> > the ADV7180 to NEWAVMODE.
> 
> So this NEWAVMODE is somehow breaking from the BT.656 standard, which
> necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
> the ADV7180 will break other capture backends that are expecting standard
> BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
> this patch in the next version.

Ok. To use that mode, first a new v4l2 mbus type and corresponding DT
bindings would have to be added.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-15 12:58           ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-15 12:58 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-kernel, dri-devel, Suresh Dhandapani

Am Mittwoch, den 13.07.2016, 16:02 -0700 schrieb Steve Longerbeam:
> On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
> >
> >
> > On 07/08/2016 10:34 AM, Philipp Zabel wrote:
> >> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
> >>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> >>>
> >>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
> >>> 0x40596 to 0x405A6. The change is related to the Start of field 1
> >>> first blanking line command bit[5-3] for NTSC format only. This
> >>> change is dependent with ADV chip where the NEWAVMODE is set to 0
> >>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
> >>> codes generated to suit analog devices encoders".
> >>>
> >>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
> >>> ---
> >>>   drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
> >>>   1 file changed, 1 insertion(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
> >>> index 0eac28c..ec81958 100644
> >>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
> >>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
> >>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
> >>>                 ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
> >>>                         CSI_CCIR_CODE_1);
> >>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
> >>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
> >>>               ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
> >>>           } else {
> >>>               dev_err(csi->ipu->dev,
> >> This looks like a very hardware specific hack? I'll at least have to
> >> test if that also works with other analog decoders.
> >
> > Hi Philipp,
> >
> > Yes it's a hack, but it has always been a hack (hardcoded values). And the
> > reason is simple, nobody AFAIK (including me) understands how to program
> > these CSI_CCIR_CODE registers, the description in the reference manual is
> > complete gibberish.
> 
> Hi Philipp, Ian over at linux-media helped me to understand these registers a
> little better, although there are still mysteries given the poor documentation.
> You should have been copied on that linux-media thread.
> 
> >
> > The reason we made this change is that, in discussions with Analog Devices,
> > they recommended setting NEWAVMODE, which changes the positions of
> > the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
> > a full day of reverse engineering (Suresh correct me if I am wrong) to hit
> > on the correct values in these registers to regain stable video after switching
> > the ADV7180 to NEWAVMODE.
> 
> So this NEWAVMODE is somehow breaking from the BT.656 standard, which
> necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
> the ADV7180 will break other capture backends that are expecting standard
> BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
> this patch in the next version.

Ok. To use that mode, first a new v4l2 mbus type and corresponding DT
bindings would have to be added.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  2016-07-15 12:48     ` Philipp Zabel
@ 2016-07-15 21:33       ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:33 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:48 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> Adds ipu_set_vdi_src_mux() that selects the VDIC input
>> (from CSI or memory).
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>   drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
>>   include/video/imx-ipu-v3.h      |  1 +
>>   2 files changed, 21 insertions(+)
>>
>> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
>> index 6d1676e..374100e 100644
>> --- a/drivers/gpu/ipu-v3/ipu-common.c
>> +++ b/drivers/gpu/ipu-v3/ipu-common.c
>> @@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
>>   }
>>   EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
>>   
>> +/*
>> + * Set the source for the VDIC. Selects either from CSI[01] or memory.
>> + */
>> +void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
>> +{
>> +	unsigned long flags;
>> +	u32 val;
>> +
>> +	spin_lock_irqsave(&ipu->lock, flags);
>> +
>> +	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
>> +	val &= ~(0x3 << 28);
>> +	if (csi)
>> +		val |= (0x01 << 28);
> This doesn't set a bus mux, or does it? As I understand it, this
> configures the FSU to trigger the VDIC conversion from the CSI direct
> channel sync signals (IC channel CB7). As such I believe this belongs
> together with the other channel linking code, maybe even in ipu-fsu.c.

It can't be folded into ipu_idmac_link(), since that links idmac channels.

I don't know if this should be characterized as a mux either.
But it does seem to have a similar function to setting the IC
source (ipu_set_ic_src_mux()), which configures the FSU to
trigger the IC conversion to start from CSI or VDIC sync signals.

But it is a bit of a mystery why the VDIC source bits were placed in
IPU_FS_PROC_FLOW1 instead of IPU_CONF.


>
> Also, some #defines would be nice, for example:
>
> #define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
> #define FS_VDI_SRC_SEL_OFFSET		28
> #define FS_VDI_SRC_SEL_CSI_DIRECT	0x1
> #define FS_VDI_SRC_SEL_VDOA		0x2

Ok I will do that in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
@ 2016-07-15 21:33       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:33 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:48 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> Adds ipu_set_vdi_src_mux() that selects the VDIC input
>> (from CSI or memory).
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>   drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
>>   include/video/imx-ipu-v3.h      |  1 +
>>   2 files changed, 21 insertions(+)
>>
>> diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
>> index 6d1676e..374100e 100644
>> --- a/drivers/gpu/ipu-v3/ipu-common.c
>> +++ b/drivers/gpu/ipu-v3/ipu-common.c
>> @@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
>>   }
>>   EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
>>   
>> +/*
>> + * Set the source for the VDIC. Selects either from CSI[01] or memory.
>> + */
>> +void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
>> +{
>> +	unsigned long flags;
>> +	u32 val;
>> +
>> +	spin_lock_irqsave(&ipu->lock, flags);
>> +
>> +	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
>> +	val &= ~(0x3 << 28);
>> +	if (csi)
>> +		val |= (0x01 << 28);
> This doesn't set a bus mux, or does it? As I understand it, this
> configures the FSU to trigger the VDIC conversion from the CSI direct
> channel sync signals (IC channel CB7). As such I believe this belongs
> together with the other channel linking code, maybe even in ipu-fsu.c.

It can't be folded into ipu_idmac_link(), since that links idmac channels.

I don't know if this should be characterized as a mux either.
But it does seem to have a similar function to setting the IC
source (ipu_set_ic_src_mux()), which configures the FSU to
trigger the IC conversion to start from CSI or VDIC sync signals.

But it is a bit of a mystery why the VDIC source bits were placed in
IPU_FS_PROC_FLOW1 instead of IPU_CONF.


>
> Also, some #defines would be nice, for example:
>
> #define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
> #define FS_VDI_SRC_SEL_OFFSET		28
> #define FS_VDI_SRC_SEL_CSI_DIRECT	0x1
> #define FS_VDI_SRC_SEL_VDOA		0x2

Ok I will do that in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels
  2016-07-15 12:45     ` Philipp Zabel
@ 2016-07-15 21:34       ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:34 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:45 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> Adds the VDIC field input IDMAC channels. These channels
>> transfer fields F(n-1), F(n), and F(N+1) from memory to
>> the VDIC (channels 8, 9, 10 respectively).
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>   include/video/imx-ipu-v3.h | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
>> index 586979e..2302fc5 100644
>> --- a/include/video/imx-ipu-v3.h
>> +++ b/include/video/imx-ipu-v3.h
>> @@ -107,6 +107,9 @@ enum ipu_channel_irq {
>>   #define IPUV3_CHANNEL_CSI2			 2
>>   #define IPUV3_CHANNEL_CSI3			 3
>>   #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
>> +#define IPUV3_CHANNEL_MEM_VDI_P			 8
>> +#define IPUV3_CHANNEL_MEM_VDI			 9
>> +#define IPUV3_CHANNEL_MEM_VDI_N			10
> I would prefer the names to be just a bit more descriptive:
>
> #define IPUV3_CHANNEL_MEM_VDI_PREV		 8
> #define IPUV3_CHANNEL_MEM_VDI_CUR		 9
> #define IPUV3_CHANNEL_MEM_VDI_NEXT		10
>
> The reference manual states that the purposes of these channels are
> "Previous field", "Current field", and "Next field" input to the VDIC,
> respectively.

right. Ok I will make that change in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels
@ 2016-07-15 21:34       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:34 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:45 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> Adds the VDIC field input IDMAC channels. These channels
>> transfer fields F(n-1), F(n), and F(N+1) from memory to
>> the VDIC (channels 8, 9, 10 respectively).
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>   include/video/imx-ipu-v3.h | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
>> index 586979e..2302fc5 100644
>> --- a/include/video/imx-ipu-v3.h
>> +++ b/include/video/imx-ipu-v3.h
>> @@ -107,6 +107,9 @@ enum ipu_channel_irq {
>>   #define IPUV3_CHANNEL_CSI2			 2
>>   #define IPUV3_CHANNEL_CSI3			 3
>>   #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
>> +#define IPUV3_CHANNEL_MEM_VDI_P			 8
>> +#define IPUV3_CHANNEL_MEM_VDI			 9
>> +#define IPUV3_CHANNEL_MEM_VDI_N			10
> I would prefer the names to be just a bit more descriptive:
>
> #define IPUV3_CHANNEL_MEM_VDI_PREV		 8
> #define IPUV3_CHANNEL_MEM_VDI_CUR		 9
> #define IPUV3_CHANNEL_MEM_VDI_NEXT		10
>
> The reference manual states that the purposes of these channels are
> "Previous field", "Current field", and "Next field" input to the VDIC,
> respectively.

right. Ok I will make that change in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src()
  2016-07-15 12:49   ` Philipp Zabel
@ 2016-07-15 21:45       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:45 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:49 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>
>> +{
>> +	ipu_set_csi_src_mux(csi->ipu, csi->id, select_mipi_csi2);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(ipu_csi_set_src);
>> +
> Couldn't you just replace ipu_set_csi_src_mux?

Yeah. I guess I created this as a matter of code convenience,
but not worth exporting another function. I will remove this patch
in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src()
@ 2016-07-15 21:45       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:45 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:49 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>
>> +{
>> +	ipu_set_csi_src_mux(csi->ipu, csi->id, select_mipi_csi2);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(ipu_csi_set_src);
>> +
> Couldn't you just replace ipu_set_csi_src_mux?

Yeah. I guess I created this as a matter of code convenience,
but not worth exporting another function. I will remove this patch
in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src()
  2016-07-15 12:45   ` Philipp Zabel
@ 2016-07-15 21:57       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:57 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:45 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> +int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
>> +{
>> +	struct ipu_ic_priv *priv = ic->priv;
>> +
>> +	ipu_set_ic_src_mux(priv->ipu, csi_id, vdi);
> 	ipu_set_ic_src_mux(ic->priv->ipu, csi_id, vdi);
> would be shorter.

Again this function isn't needed any longer. I will remove this
patch in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src()
@ 2016-07-15 21:57       ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 21:57 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam; +Cc: dri-devel, linux-kernel



On 07/15/2016 05:45 AM, Philipp Zabel wrote:
> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>> +int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
>> +{
>> +	struct ipu_ic_priv *priv = ic->priv;
>> +
>> +	ipu_set_ic_src_mux(priv->ipu, csi_id, vdi);
> 	ipu_set_ic_src_mux(ic->priv->ipu, csi_id, vdi);
> would be shorter.

Again this function isn't needed any longer. I will remove this
patch in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-11  0:02   ` Paul Gortmaker
@ 2016-07-15 22:35     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 22:35 UTC (permalink / raw)
  To: Paul Gortmaker; +Cc: p.zabel, dri-devel, LKML, Steve Longerbeam



On 07/10/2016 05:02 PM, Paul Gortmaker wrote:
>
> +#include <linux/export.h>
> +#include <linux/module.h>
> You have a u32 field in a struct called "modules" but aside from that, I do not
> see anything in this code requiring module.h -- did I miss something?
>
> You might want export.h for EXPORT_SYMBOL though.
>
>

Hi Paul, yes module.h wasn't needed. Fixed in next version.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-15 12:58           ` Philipp Zabel
@ 2016-07-15 23:09             ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 23:09 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Suresh Dhandapani, Hans Verkuil



On 07/15/2016 05:58 AM, Philipp Zabel wrote:
> Am Mittwoch, den 13.07.2016, 16:02 -0700 schrieb Steve Longerbeam:
>> On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
>>>
>>> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>>>> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>>>>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>>
>>>>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>>>>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>>>>> first blanking line command bit[5-3] for NTSC format only. This
>>>>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>>>>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>>>>> codes generated to suit analog devices encoders".
>>>>>
>>>>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>> ---
>>>>>    drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>> index 0eac28c..ec81958 100644
>>>>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>>>>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>>>>                  ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>>>>                          CSI_CCIR_CODE_1);
>>>>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>>>>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>>>>                ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>>>>            } else {
>>>>>                dev_err(csi->ipu->dev,
>>>> This looks like a very hardware specific hack? I'll at least have to
>>>> test if that also works with other analog decoders.
>>> Hi Philipp,
>>>
>>> Yes it's a hack, but it has always been a hack (hardcoded values). And the
>>> reason is simple, nobody AFAIK (including me) understands how to program
>>> these CSI_CCIR_CODE registers, the description in the reference manual is
>>> complete gibberish.
>> Hi Philipp, Ian over at linux-media helped me to understand these registers a
>> little better, although there are still mysteries given the poor documentation.
>> You should have been copied on that linux-media thread.
>>
>>> The reason we made this change is that, in discussions with Analog Devices,
>>> they recommended setting NEWAVMODE, which changes the positions of
>>> the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
>>> a full day of reverse engineering (Suresh correct me if I am wrong) to hit
>>> on the correct values in these registers to regain stable video after switching
>>> the ADV7180 to NEWAVMODE.
>> So this NEWAVMODE is somehow breaking from the BT.656 standard, which
>> necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
>> the ADV7180 will break other capture backends that are expecting standard
>> BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
>> this patch in the next version.
> Ok. To use that mode, first a new v4l2 mbus type and corresponding DT
> bindings would have to be added.

Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
and then add a new "newavmode" boolean DT binding parsed by
v4l2_of_parse_endpoint()?

I don't know if that would make sense given that this NEWAVMODE is a kind
of hack of the BT.656 standard, only used by Analog Devices 
encoders/decoders.

Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
written for
them), so maybe it would make sense to do this.

Adding Hans.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-15 23:09             ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-15 23:09 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Suresh Dhandapani, Hans Verkuil



On 07/15/2016 05:58 AM, Philipp Zabel wrote:
> Am Mittwoch, den 13.07.2016, 16:02 -0700 schrieb Steve Longerbeam:
>> On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
>>>
>>> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>>>> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>>>>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>>
>>>>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>>>>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>>>>> first blanking line command bit[5-3] for NTSC format only. This
>>>>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>>>>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>>>>> codes generated to suit analog devices encoders".
>>>>>
>>>>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>> ---
>>>>>    drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>> index 0eac28c..ec81958 100644
>>>>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>>>>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>>>>                  ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>>>>                          CSI_CCIR_CODE_1);
>>>>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>>>>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>>>>                ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>>>>            } else {
>>>>>                dev_err(csi->ipu->dev,
>>>> This looks like a very hardware specific hack? I'll at least have to
>>>> test if that also works with other analog decoders.
>>> Hi Philipp,
>>>
>>> Yes it's a hack, but it has always been a hack (hardcoded values). And the
>>> reason is simple, nobody AFAIK (including me) understands how to program
>>> these CSI_CCIR_CODE registers, the description in the reference manual is
>>> complete gibberish.
>> Hi Philipp, Ian over at linux-media helped me to understand these registers a
>> little better, although there are still mysteries given the poor documentation.
>> You should have been copied on that linux-media thread.
>>
>>> The reason we made this change is that, in discussions with Analog Devices,
>>> they recommended setting NEWAVMODE, which changes the positions of
>>> the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh at least
>>> a full day of reverse engineering (Suresh correct me if I am wrong) to hit
>>> on the correct values in these registers to regain stable video after switching
>>> the ADV7180 to NEWAVMODE.
>> So this NEWAVMODE is somehow breaking from the BT.656 standard, which
>> necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
>> the ADV7180 will break other capture backends that are expecting standard
>> BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
>> this patch in the next version.
> Ok. To use that mode, first a new v4l2 mbus type and corresponding DT
> bindings would have to be added.

Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
and then add a new "newavmode" boolean DT binding parsed by
v4l2_of_parse_endpoint()?

I don't know if that would make sense given that this NEWAVMODE is a kind
of hack of the BT.656 standard, only used by Analog Devices 
encoders/decoders.

Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
written for
them), so maybe it would make sense to do this.

Adding Hans.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-15 23:09             ` Steve Longerbeam
@ 2016-07-16 20:24               ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-16 20:24 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Hans Verkuil



On 07/15/2016 04:09 PM, Steve Longerbeam wrote:
>
>
> On 07/15/2016 05:58 AM, Philipp Zabel wrote:
>> Am Mittwoch, den 13.07.2016, 16:02 -0700 schrieb Steve Longerbeam:
>>> On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
>>>>
>>>> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>>>>> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>>>>>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>>>
>>>>>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>>>>>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>>>>>> first blanking line command bit[5-3] for NTSC format only. This
>>>>>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>>>>>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>>>>>> codes generated to suit analog devices encoders".
>>>>>>
>>>>>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>>> ---
>>>>>>    drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>>>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c 
>>>>>> b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>>> index 0eac28c..ec81958 100644
>>>>>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>>>>>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>>>>>                  ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>>>>>                          CSI_CCIR_CODE_1);
>>>>>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>>>>>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>>>>>                ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>>>>>            } else {
>>>>>>                dev_err(csi->ipu->dev,
>>>>> This looks like a very hardware specific hack? I'll at least have to
>>>>> test if that also works with other analog decoders.
>>>> Hi Philipp,
>>>>
>>>> Yes it's a hack, but it has always been a hack (hardcoded values). 
>>>> And the
>>>> reason is simple, nobody AFAIK (including me) understands how to 
>>>> program
>>>> these CSI_CCIR_CODE registers, the description in the reference 
>>>> manual is
>>>> complete gibberish.
>>> Hi Philipp, Ian over at linux-media helped me to understand these 
>>> registers a
>>> little better, although there are still mysteries given the poor 
>>> documentation.
>>> You should have been copied on that linux-media thread.
>>>
>>>> The reason we made this change is that, in discussions with Analog 
>>>> Devices,
>>>> they recommended setting NEWAVMODE, which changes the positions of
>>>> the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh 
>>>> at least
>>>> a full day of reverse engineering (Suresh correct me if I am wrong) 
>>>> to hit
>>>> on the correct values in these registers to regain stable video 
>>>> after switching
>>>> the ADV7180 to NEWAVMODE.
>>> So this NEWAVMODE is somehow breaking from the BT.656 standard, which
>>> necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
>>> the ADV7180 will break other capture backends that are expecting 
>>> standard
>>> BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
>>> this patch in the next version.
>> Ok. To use that mode, first a new v4l2 mbus type and corresponding DT
>> bindings would have to be added.
>
> Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
> and then add a new "newavmode" boolean DT binding parsed by
> v4l2_of_parse_endpoint()?
>
> I don't know if that would make sense given that this NEWAVMODE is a kind
> of hack of the BT.656 standard, only used by Analog Devices 
> encoders/decoders.
>
> Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
> written for
> them), so maybe it would make sense to do this.
>

I don't think a "newavmode" boolean property would necessitate a whole new
mbus type, but perhaps just a new parallel bus flag. I will propose a 
patch at
linux-media that adds this.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-16 20:24               ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-16 20:24 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Hans Verkuil



On 07/15/2016 04:09 PM, Steve Longerbeam wrote:
>
>
> On 07/15/2016 05:58 AM, Philipp Zabel wrote:
>> Am Mittwoch, den 13.07.2016, 16:02 -0700 schrieb Steve Longerbeam:
>>> On 07/10/2016 09:33 AM, Steve Longerbeam wrote:
>>>>
>>>> On 07/08/2016 10:34 AM, Philipp Zabel wrote:
>>>>> Am Donnerstag, den 07.07.2016, 16:03 -0700 schrieb Steve Longerbeam:
>>>>>> From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>>>
>>>>>> This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
>>>>>> 0x40596 to 0x405A6. The change is related to the Start of field 1
>>>>>> first blanking line command bit[5-3] for NTSC format only. This
>>>>>> change is dependent with ADV chip where the NEWAVMODE is set to 0
>>>>>> in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
>>>>>> codes generated to suit analog devices encoders".
>>>>>>
>>>>>> Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
>>>>>> ---
>>>>>>    drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
>>>>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/ipu-v3/ipu-csi.c 
>>>>>> b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>>> index 0eac28c..ec81958 100644
>>>>>> --- a/drivers/gpu/ipu-v3/ipu-csi.c
>>>>>> +++ b/drivers/gpu/ipu-v3/ipu-csi.c
>>>>>> @@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
>>>>>>                  ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
>>>>>>                          CSI_CCIR_CODE_1);
>>>>>> -            ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
>>>>>> +            ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
>>>>>>                ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
>>>>>>            } else {
>>>>>>                dev_err(csi->ipu->dev,
>>>>> This looks like a very hardware specific hack? I'll at least have to
>>>>> test if that also works with other analog decoders.
>>>> Hi Philipp,
>>>>
>>>> Yes it's a hack, but it has always been a hack (hardcoded values). 
>>>> And the
>>>> reason is simple, nobody AFAIK (including me) understands how to 
>>>> program
>>>> these CSI_CCIR_CODE registers, the description in the reference 
>>>> manual is
>>>> complete gibberish.
>>> Hi Philipp, Ian over at linux-media helped me to understand these 
>>> registers a
>>> little better, although there are still mysteries given the poor 
>>> documentation.
>>> You should have been copied on that linux-media thread.
>>>
>>>> The reason we made this change is that, in discussions with Analog 
>>>> Devices,
>>>> they recommended setting NEWAVMODE, which changes the positions of
>>>> the AV codes sent by the ADV7180 on the bt.656 bus. It took Suresh 
>>>> at least
>>>> a full day of reverse engineering (Suresh correct me if I am wrong) 
>>>> to hit
>>>> on the correct values in these registers to regain stable video 
>>>> after switching
>>>> the ADV7180 to NEWAVMODE.
>>> So this NEWAVMODE is somehow breaking from the BT.656 standard, which
>>> necessitated the change to CSI_CCIR_CODE_2. So NEWAVMODE if enabled in
>>> the ADV7180 will break other capture backends that are expecting 
>>> standard
>>> BT.656 SAV/EAV codes. So NEWAVMODE should not be used and I will remove
>>> this patch in the next version.
>> Ok. To use that mode, first a new v4l2 mbus type and corresponding DT
>> bindings would have to be added.
>
> Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
> and then add a new "newavmode" boolean DT binding parsed by
> v4l2_of_parse_endpoint()?
>
> I don't know if that would make sense given that this NEWAVMODE is a kind
> of hack of the BT.656 standard, only used by Analog Devices 
> encoders/decoders.
>
> Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
> written for
> them), so maybe it would make sense to do this.
>

I don't think a "newavmode" boolean property would necessitate a whole new
mbus type, but perhaps just a new parallel bus flag. I will propose a 
patch at
linux-media that adds this.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-16 20:24               ` Steve Longerbeam
@ 2016-07-19 13:32                 ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-19 13:32 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: dri-devel, linux-kernel, Hans Verkuil

Am Samstag, den 16.07.2016, 13:24 -0700 schrieb Steve Longerbeam:
[...]
> > Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
> > and then add a new "newavmode" boolean DT binding parsed by
> > v4l2_of_parse_endpoint()?
> >
> > I don't know if that would make sense given that this NEWAVMODE is a kind
> > of hack of the BT.656 standard, only used by Analog Devices 
> > encoders/decoders.
> >
> > Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
> > written for
> > them), so maybe it would make sense to do this.
> 
> I don't think a "newavmode" boolean property would necessitate a whole new
> mbus type, but perhaps just a new parallel bus flag. I will propose a 
> patch at linux-media that adds this.

Sounds good to me, the V4L2_MBUS_BT656 documentation comment in
include/media/v4l2-mediabus.h should be extended to include the new
non-standard mode then.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-19 13:32                 ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-19 13:32 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-kernel, dri-devel

Am Samstag, den 16.07.2016, 13:24 -0700 schrieb Steve Longerbeam:
[...]
> > Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
> > and then add a new "newavmode" boolean DT binding parsed by
> > v4l2_of_parse_endpoint()?
> >
> > I don't know if that would make sense given that this NEWAVMODE is a kind
> > of hack of the BT.656 standard, only used by Analog Devices 
> > encoders/decoders.
> >
> > Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
> > written for
> > them), so maybe it would make sense to do this.
> 
> I don't think a "newavmode" boolean property would necessitate a whole new
> mbus type, but perhaps just a new parallel bus flag. I will propose a 
> patch at linux-media that adds this.

Sounds good to me, the V4L2_MBUS_BT656 documentation comment in
include/media/v4l2-mediabus.h should be extended to include the new
non-standard mode then.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-19 13:32                 ` Philipp Zabel
@ 2016-07-20  0:07                   ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  0:07 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Hans Verkuil

On 07/19/2016 06:32 AM, Philipp Zabel wrote:
> Am Samstag, den 16.07.2016, 13:24 -0700 schrieb Steve Longerbeam:
> [...]
>>> Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
>>> and then add a new "newavmode" boolean DT binding parsed by
>>> v4l2_of_parse_endpoint()?
>>>
>>> I don't know if that would make sense given that this NEWAVMODE is a kind
>>> of hack of the BT.656 standard, only used by Analog Devices 
>>> encoders/decoders.
>>>
>>> Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
>>> written for
>>> them), so maybe it would make sense to do this.
>> I don't think a "newavmode" boolean property would necessitate a whole new
>> mbus type, but perhaps just a new parallel bus flag. I will propose a 
>> patch at linux-media that adds this.
> Sounds good to me, the V4L2_MBUS_BT656 documentation comment in
> include/media/v4l2-mediabus.h should be extended to include the new
> non-standard mode then.

Hi Philipp, Ok I just sent that off to linux-media. If approved there,
we can use the new flag V4L2_MBUS_NEWAVMODE to make the
adjustment to CSI_CCIR_CODE_2.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format
@ 2016-07-20  0:07                   ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  0:07 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: dri-devel, linux-kernel, Hans Verkuil

On 07/19/2016 06:32 AM, Philipp Zabel wrote:
> Am Samstag, den 16.07.2016, 13:24 -0700 schrieb Steve Longerbeam:
> [...]
>>> Hmm, do you mean define something like a V4L2_MBUS_BT656_NEWAVMODE,
>>> and then add a new "newavmode" boolean DT binding parsed by
>>> v4l2_of_parse_endpoint()?
>>>
>>> I don't know if that would make sense given that this NEWAVMODE is a kind
>>> of hack of the BT.656 standard, only used by Analog Devices 
>>> encoders/decoders.
>>>
>>> Although there a _lot_ of AD encoder/decoder chips (and subdev drivers 
>>> written for
>>> them), so maybe it would make sense to do this.
>> I don't think a "newavmode" boolean property would necessitate a whole new
>> mbus type, but perhaps just a new parallel bus flag. I will propose a 
>> patch at linux-media that adds this.
> Sounds good to me, the V4L2_MBUS_BT656 documentation comment in
> include/media/v4l2-mediabus.h should be extended to include the new
> non-standard mode then.

Hi Philipp, Ok I just sent that off to linux-media. If approved there,
we can use the new flag V4L2_MBUS_NEWAVMODE to make the
adjustment to CSI_CCIR_CODE_2.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2
  2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
@ 2016-07-20  1:10   ` Steve Longerbeam
  2016-07-07 23:03 ` [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
                     ` (16 subsequent siblings)
  17 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:10 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

These updates to IPUv3 are needed for media staging drivers
for i.MX5/6 video capture and mem2mem.

Steve Longerbeam (13):
  gpu: ipu-v3: Add Video Deinterlacer unit
  gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  gpu: ipu-v3: Add ipu_get_num()
  gpu: ipu-v3: Add IDMA channel linking support
  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  gpu: ipu-v3: Add VDI input IDMAC channels
  gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  gpu: ipu-v3: Fix IRT usage
  gpu: ipu-ic: Add complete image conversion support with tiling
  gpu: ipu-ic: allow multiple handles to ic
  gpu: ipu-v3: rename CSI client device

 drivers/gpu/ipu-v3/Makefile     |    2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  161 +++-
 drivers/gpu/ipu-v3/ipu-cpmem.c  |   13 +
 drivers/gpu/ipu-v3/ipu-csi.c    |   26 +-
 drivers/gpu/ipu-v3/ipu-ic.c     | 1756 ++++++++++++++++++++++++++++++++++++++-
 drivers/gpu/ipu-v3/ipu-prv.h    |   13 +
 drivers/gpu/ipu-v3/ipu-vdi.c    |  262 ++++++
 include/video/imx-ipu-v3.h      |   94 ++-
 8 files changed, 2267 insertions(+), 60 deletions(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 119+ messages in thread

* [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2
@ 2016-07-20  1:10   ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:10 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

These updates to IPUv3 are needed for media staging drivers
for i.MX5/6 video capture and mem2mem.

Steve Longerbeam (13):
  gpu: ipu-v3: Add Video Deinterlacer unit
  gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  gpu: ipu-v3: Add ipu_get_num()
  gpu: ipu-v3: Add IDMA channel linking support
  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  gpu: ipu-v3: Add VDI input IDMAC channels
  gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  gpu: ipu-v3: Fix IRT usage
  gpu: ipu-ic: Add complete image conversion support with tiling
  gpu: ipu-ic: allow multiple handles to ic
  gpu: ipu-v3: rename CSI client device

 drivers/gpu/ipu-v3/Makefile     |    2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  161 +++-
 drivers/gpu/ipu-v3/ipu-cpmem.c  |   13 +
 drivers/gpu/ipu-v3/ipu-csi.c    |   26 +-
 drivers/gpu/ipu-v3/ipu-ic.c     | 1756 ++++++++++++++++++++++++++++++++++++++-
 drivers/gpu/ipu-v3/ipu-prv.h    |   13 +
 drivers/gpu/ipu-v3/ipu-vdi.c    |  262 ++++++
 include/video/imx-ipu-v3.h      |   94 ++-
 8 files changed, 2267 insertions(+), 60 deletions(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 119+ messages in thread

* [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:10     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:10 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds the Video Deinterlacer (VDIC) unit.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- removed include of module.h
- corrected V4L2 field type checks
- cleaned up use_count decrement in ipu_vdi_disable()
---
 drivers/gpu/ipu-v3/Makefile     |   2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  11 ++
 drivers/gpu/ipu-v3/ipu-prv.h    |   6 +
 drivers/gpu/ipu-v3/ipu-vdi.c    | 262 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  27 +++++
 5 files changed, 307 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
index 107ec23..aeba9dc 100644
--- a/drivers/gpu/ipu-v3/Makefile
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
 imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
-		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
+		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 99dcacf..30dc115 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -833,6 +833,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
 		goto err_ic;
 	}
 
+	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
+			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
+			   IPU_CONF_IC_INPUT);
+	if (ret) {
+		unit = "vdi";
+		goto err_vdi;
+	}
+
 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
 			  IPU_CONF_DI0_EN, ipu_clk);
 	if (ret) {
@@ -887,6 +895,8 @@ err_dc:
 err_di_1:
 	ipu_di_exit(ipu, 0);
 err_di_0:
+	ipu_vdi_exit(ipu);
+err_vdi:
 	ipu_ic_exit(ipu);
 err_ic:
 	ipu_csi_exit(ipu, 1);
@@ -971,6 +981,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
 	ipu_dc_exit(ipu);
 	ipu_di_exit(ipu, 1);
 	ipu_di_exit(ipu, 0);
+	ipu_vdi_exit(ipu);
 	ipu_ic_exit(ipu);
 	ipu_csi_exit(ipu, 1);
 	ipu_csi_exit(ipu, 0);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index bfb1e8a..845f64c 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -138,6 +138,7 @@ struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
 struct ipu_ic_priv;
+struct ipu_vdi;
 struct ipu_smfc_priv;
 
 struct ipu_devtype;
@@ -169,6 +170,7 @@ struct ipu_soc {
 	struct ipu_di		*di_priv[2];
 	struct ipu_csi		*csi_priv[2];
 	struct ipu_ic_priv	*ic_priv;
+	struct ipu_vdi          *vdi_priv;
 	struct ipu_smfc_priv	*smfc_priv;
 };
 
@@ -199,6 +201,10 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 		unsigned long base, unsigned long tpmem_base);
 void ipu_ic_exit(struct ipu_soc *ipu);
 
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module);
+void ipu_vdi_exit(struct ipu_soc *ipu);
+
 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);
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
new file mode 100644
index 0000000..fe4d90a
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-vdi.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <uapi/linux/v4l2-mediabus.h>
+
+#include "ipu-prv.h"
+
+struct ipu_vdi {
+	void __iomem *base;
+	u32 module;
+	spinlock_t lock;
+	int use_count;
+	struct ipu_soc *ipu;
+};
+
+
+/* VDI Register Offsets */
+#define VDI_FSIZE 0x0000
+#define VDI_C     0x0004
+
+/* VDI Register Fields */
+#define VDI_C_CH_420             (0 << 1)
+#define VDI_C_CH_422             (1 << 1)
+#define VDI_C_MOT_SEL_MASK       (0x3 << 2)
+#define VDI_C_MOT_SEL_FULL       (2 << 2)
+#define VDI_C_MOT_SEL_LOW        (1 << 2)
+#define VDI_C_MOT_SEL_MED        (0 << 2)
+#define VDI_C_BURST_SIZE1_4      (3 << 4)
+#define VDI_C_BURST_SIZE2_4      (3 << 8)
+#define VDI_C_BURST_SIZE3_4      (3 << 12)
+#define VDI_C_BURST_SIZE_MASK    0xF
+#define VDI_C_BURST_SIZE1_OFFSET 4
+#define VDI_C_BURST_SIZE2_OFFSET 8
+#define VDI_C_BURST_SIZE3_OFFSET 12
+#define VDI_C_VWM1_SET_1         (0 << 16)
+#define VDI_C_VWM1_SET_2         (1 << 16)
+#define VDI_C_VWM1_CLR_2         (1 << 19)
+#define VDI_C_VWM3_SET_1         (0 << 22)
+#define VDI_C_VWM3_SET_2         (1 << 22)
+#define VDI_C_VWM3_CLR_2         (1 << 25)
+#define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
+#define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
+
+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
+{
+	return readl(vdi->base + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
+				 unsigned int offset)
+{
+	writel(value, vdi->base + offset);
+}
+
+static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	if (top_field_0)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+static void __ipu_vdi_set_motion(struct ipu_vdi *vdi,
+				 enum ipu_motion_sel motion_sel)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+
+	reg &= ~VDI_C_MOT_SEL_MASK;
+
+	switch (motion_sel) {
+	case MED_MOTION:
+		reg |= VDI_C_MOT_SEL_MED;
+		break;
+	case HIGH_MOTION:
+		reg |= VDI_C_MOT_SEL_FULL;
+		break;
+	default:
+		reg |= VDI_C_MOT_SEL_LOW;
+		break;
+	}
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres,
+		   u32 field, enum ipu_motion_sel motion_sel)
+{
+	unsigned long flags;
+	u32 pixel_fmt, reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ((yres - 1) << 16) | (xres - 1);
+	ipu_vdi_write(vdi, reg, VDI_FSIZE);
+
+	/*
+	 * Full motion, only vertical filter is used.
+	 * Burst size is 4 accesses
+	 */
+	if (code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+	    code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+	    code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+	    code == MEDIA_BUS_FMT_YUYV8_1X16)
+		pixel_fmt = VDI_C_CH_422;
+	else
+		pixel_fmt = VDI_C_CH_420;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	reg |= pixel_fmt;
+	reg |= VDI_C_BURST_SIZE2_4;
+	reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
+	reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	if (field == V4L2_FIELD_SEQ_TB)
+		__ipu_vdi_set_top_field_man(vdi, false);
+	else if (field == V4L2_FIELD_SEQ_BT)
+		__ipu_vdi_set_top_field_man(vdi, true);
+
+	__ipu_vdi_set_motion(vdi, motion_sel);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_setup);
+
+void ipu_vdi_unsetup(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+	ipu_vdi_write(vdi, 0, VDI_FSIZE);
+	ipu_vdi_write(vdi, 0, VDI_C);
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
+
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 mask_reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+	if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
+
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
+{
+	ipu_set_vdi_src_mux(vdi->ipu, csi);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_src);
+
+int ipu_vdi_enable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (!vdi->use_count)
+		ipu_module_enable(vdi->ipu, vdi->module);
+
+	vdi->use_count++;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_enable);
+
+int ipu_vdi_disable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (vdi->use_count) {
+		if (!--vdi->use_count)
+			ipu_module_disable(vdi->ipu, vdi->module);
+	}
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_disable);
+
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
+{
+	return ipu->vdi_priv;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_get);
+
+void ipu_vdi_put(struct ipu_vdi *vdi)
+{
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_put);
+
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module)
+{
+	struct ipu_vdi *vdi;
+
+	vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
+	if (!vdi)
+		return -ENOMEM;
+
+	ipu->vdi_priv = vdi;
+
+	spin_lock_init(&vdi->lock);
+	vdi->module = module;
+	vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
+	if (!vdi->base)
+		return -ENOMEM;
+
+	dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
+	vdi->ipu = ipu;
+
+	return 0;
+}
+
+void ipu_vdi_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 7adeaae..a7a28db 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -80,6 +80,16 @@ enum ipu_color_space {
 	IPUV3_COLORSPACE_UNKNOWN,
 };
 
+/*
+ * Enumeration of VDI MOTION select
+ */
+enum ipu_motion_sel {
+	MOTION_NONE = 0,
+	LOW_MOTION,
+	MED_MOTION,
+	HIGH_MOTION,
+};
+
 struct ipuv3_channel;
 
 enum ipu_channel_irq {
@@ -317,6 +327,23 @@ void ipu_ic_put(struct ipu_ic *ic);
 void ipu_ic_dump(struct ipu_ic *ic);
 
 /*
+ * IPU Video De-Interlacer (vdi) functions
+ */
+struct ipu_vdi;
+void ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0);
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
+void ipu_vdi_setup(struct ipu_vdi *vdi,
+		   u32 code, int xres, int yres, u32 field,
+		   enum ipu_motion_sel motion_sel);
+void ipu_vdi_unsetup(struct ipu_vdi *vdi);
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi);
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi);
+int ipu_vdi_enable(struct ipu_vdi *vdi);
+int ipu_vdi_disable(struct ipu_vdi *vdi);
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
+void ipu_vdi_put(struct ipu_vdi *vdi);
+
+/*
  * IPU Sensor Multiple FIFO Controller (SMFC) functions
  */
 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
@ 2016-07-20  1:10     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:10 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds the Video Deinterlacer (VDIC) unit.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- removed include of module.h
- corrected V4L2 field type checks
- cleaned up use_count decrement in ipu_vdi_disable()
---
 drivers/gpu/ipu-v3/Makefile     |   2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  11 ++
 drivers/gpu/ipu-v3/ipu-prv.h    |   6 +
 drivers/gpu/ipu-v3/ipu-vdi.c    | 262 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  27 +++++
 5 files changed, 307 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
index 107ec23..aeba9dc 100644
--- a/drivers/gpu/ipu-v3/Makefile
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
 imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
-		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
+		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 99dcacf..30dc115 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -833,6 +833,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
 		goto err_ic;
 	}
 
+	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
+			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
+			   IPU_CONF_IC_INPUT);
+	if (ret) {
+		unit = "vdi";
+		goto err_vdi;
+	}
+
 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
 			  IPU_CONF_DI0_EN, ipu_clk);
 	if (ret) {
@@ -887,6 +895,8 @@ err_dc:
 err_di_1:
 	ipu_di_exit(ipu, 0);
 err_di_0:
+	ipu_vdi_exit(ipu);
+err_vdi:
 	ipu_ic_exit(ipu);
 err_ic:
 	ipu_csi_exit(ipu, 1);
@@ -971,6 +981,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
 	ipu_dc_exit(ipu);
 	ipu_di_exit(ipu, 1);
 	ipu_di_exit(ipu, 0);
+	ipu_vdi_exit(ipu);
 	ipu_ic_exit(ipu);
 	ipu_csi_exit(ipu, 1);
 	ipu_csi_exit(ipu, 0);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index bfb1e8a..845f64c 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -138,6 +138,7 @@ struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
 struct ipu_ic_priv;
+struct ipu_vdi;
 struct ipu_smfc_priv;
 
 struct ipu_devtype;
@@ -169,6 +170,7 @@ struct ipu_soc {
 	struct ipu_di		*di_priv[2];
 	struct ipu_csi		*csi_priv[2];
 	struct ipu_ic_priv	*ic_priv;
+	struct ipu_vdi          *vdi_priv;
 	struct ipu_smfc_priv	*smfc_priv;
 };
 
@@ -199,6 +201,10 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 		unsigned long base, unsigned long tpmem_base);
 void ipu_ic_exit(struct ipu_soc *ipu);
 
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module);
+void ipu_vdi_exit(struct ipu_soc *ipu);
+
 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);
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
new file mode 100644
index 0000000..fe4d90a
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-vdi.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <uapi/linux/v4l2-mediabus.h>
+
+#include "ipu-prv.h"
+
+struct ipu_vdi {
+	void __iomem *base;
+	u32 module;
+	spinlock_t lock;
+	int use_count;
+	struct ipu_soc *ipu;
+};
+
+
+/* VDI Register Offsets */
+#define VDI_FSIZE 0x0000
+#define VDI_C     0x0004
+
+/* VDI Register Fields */
+#define VDI_C_CH_420             (0 << 1)
+#define VDI_C_CH_422             (1 << 1)
+#define VDI_C_MOT_SEL_MASK       (0x3 << 2)
+#define VDI_C_MOT_SEL_FULL       (2 << 2)
+#define VDI_C_MOT_SEL_LOW        (1 << 2)
+#define VDI_C_MOT_SEL_MED        (0 << 2)
+#define VDI_C_BURST_SIZE1_4      (3 << 4)
+#define VDI_C_BURST_SIZE2_4      (3 << 8)
+#define VDI_C_BURST_SIZE3_4      (3 << 12)
+#define VDI_C_BURST_SIZE_MASK    0xF
+#define VDI_C_BURST_SIZE1_OFFSET 4
+#define VDI_C_BURST_SIZE2_OFFSET 8
+#define VDI_C_BURST_SIZE3_OFFSET 12
+#define VDI_C_VWM1_SET_1         (0 << 16)
+#define VDI_C_VWM1_SET_2         (1 << 16)
+#define VDI_C_VWM1_CLR_2         (1 << 19)
+#define VDI_C_VWM3_SET_1         (0 << 22)
+#define VDI_C_VWM3_SET_2         (1 << 22)
+#define VDI_C_VWM3_CLR_2         (1 << 25)
+#define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
+#define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
+
+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
+{
+	return readl(vdi->base + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
+				 unsigned int offset)
+{
+	writel(value, vdi->base + offset);
+}
+
+static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	if (top_field_0)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+static void __ipu_vdi_set_motion(struct ipu_vdi *vdi,
+				 enum ipu_motion_sel motion_sel)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+
+	reg &= ~VDI_C_MOT_SEL_MASK;
+
+	switch (motion_sel) {
+	case MED_MOTION:
+		reg |= VDI_C_MOT_SEL_MED;
+		break;
+	case HIGH_MOTION:
+		reg |= VDI_C_MOT_SEL_FULL;
+		break;
+	default:
+		reg |= VDI_C_MOT_SEL_LOW;
+		break;
+	}
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres,
+		   u32 field, enum ipu_motion_sel motion_sel)
+{
+	unsigned long flags;
+	u32 pixel_fmt, reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ((yres - 1) << 16) | (xres - 1);
+	ipu_vdi_write(vdi, reg, VDI_FSIZE);
+
+	/*
+	 * Full motion, only vertical filter is used.
+	 * Burst size is 4 accesses
+	 */
+	if (code = MEDIA_BUS_FMT_UYVY8_2X8 ||
+	    code = MEDIA_BUS_FMT_UYVY8_1X16 ||
+	    code = MEDIA_BUS_FMT_YUYV8_2X8 ||
+	    code = MEDIA_BUS_FMT_YUYV8_1X16)
+		pixel_fmt = VDI_C_CH_422;
+	else
+		pixel_fmt = VDI_C_CH_420;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	reg |= pixel_fmt;
+	reg |= VDI_C_BURST_SIZE2_4;
+	reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
+	reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	if (field = V4L2_FIELD_SEQ_TB)
+		__ipu_vdi_set_top_field_man(vdi, false);
+	else if (field = V4L2_FIELD_SEQ_BT)
+		__ipu_vdi_set_top_field_man(vdi, true);
+
+	__ipu_vdi_set_motion(vdi, motion_sel);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_setup);
+
+void ipu_vdi_unsetup(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+	ipu_vdi_write(vdi, 0, VDI_FSIZE);
+	ipu_vdi_write(vdi, 0, VDI_C);
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
+
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 mask_reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+	if (mask_reg = VDI_C_TOP_FIELD_MAN_1)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
+
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
+{
+	ipu_set_vdi_src_mux(vdi->ipu, csi);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_src);
+
+int ipu_vdi_enable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (!vdi->use_count)
+		ipu_module_enable(vdi->ipu, vdi->module);
+
+	vdi->use_count++;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_enable);
+
+int ipu_vdi_disable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (vdi->use_count) {
+		if (!--vdi->use_count)
+			ipu_module_disable(vdi->ipu, vdi->module);
+	}
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_disable);
+
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
+{
+	return ipu->vdi_priv;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_get);
+
+void ipu_vdi_put(struct ipu_vdi *vdi)
+{
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_put);
+
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module)
+{
+	struct ipu_vdi *vdi;
+
+	vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
+	if (!vdi)
+		return -ENOMEM;
+
+	ipu->vdi_priv = vdi;
+
+	spin_lock_init(&vdi->lock);
+	vdi->module = module;
+	vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
+	if (!vdi->base)
+		return -ENOMEM;
+
+	dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
+	vdi->ipu = ipu;
+
+	return 0;
+}
+
+void ipu_vdi_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 7adeaae..a7a28db 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -80,6 +80,16 @@ enum ipu_color_space {
 	IPUV3_COLORSPACE_UNKNOWN,
 };
 
+/*
+ * Enumeration of VDI MOTION select
+ */
+enum ipu_motion_sel {
+	MOTION_NONE = 0,
+	LOW_MOTION,
+	MED_MOTION,
+	HIGH_MOTION,
+};
+
 struct ipuv3_channel;
 
 enum ipu_channel_irq {
@@ -317,6 +327,23 @@ void ipu_ic_put(struct ipu_ic *ic);
 void ipu_ic_dump(struct ipu_ic *ic);
 
 /*
+ * IPU Video De-Interlacer (vdi) functions
+ */
+struct ipu_vdi;
+void ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0);
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
+void ipu_vdi_setup(struct ipu_vdi *vdi,
+		   u32 code, int xres, int yres, u32 field,
+		   enum ipu_motion_sel motion_sel);
+void ipu_vdi_unsetup(struct ipu_vdi *vdi);
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi);
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi);
+int ipu_vdi_enable(struct ipu_vdi *vdi);
+int ipu_vdi_disable(struct ipu_vdi *vdi);
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
+void ipu_vdi_put(struct ipu_vdi *vdi);
+
+/*
  * IPU Sensor Multiple FIFO Controller (SMFC) functions
  */
 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 02/13] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 6494a4d..a36c35e 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
 
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
+{
+	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
+	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
+
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index a7a28db..48c8dc5 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -194,6 +194,7 @@ void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
 void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
 void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 02/13] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 6494a4d..a36c35e 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
 
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
+{
+	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
+	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
+
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index a7a28db..48c8dc5 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -194,6 +194,7 @@ void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
 void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
 void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 03/13] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds ipu_cpmem_get_burstsize().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 6 ++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 7 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index a36c35e..fcb7dc8 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -275,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
 
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
+{
+	return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
+
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 48c8dc5..5d59739 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -197,6 +197,7 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
 void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
 void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
 void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 03/13] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds ipu_cpmem_get_burstsize().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 6 ++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 7 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index a36c35e..fcb7dc8 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -275,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
 
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
+{
+	return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
+
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 48c8dc5..5d59739 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -197,6 +197,7 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
 void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
 void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
 void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 04/13] gpu: ipu-v3: Add ipu_get_num()
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds of-alias id to ipu_soc and retrieve with ipu_get_num().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 8 ++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    | 1 +
 include/video/imx-ipu-v3.h      | 1 +
 3 files changed, 10 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 30dc115..49af121 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
 	writel(value, ipu->cm_reg + offset);
 }
 
+int ipu_get_num(struct ipu_soc *ipu)
+{
+	return ipu->id;
+}
+EXPORT_SYMBOL_GPL(ipu_get_num);
+
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
 {
 	u32 val;
@@ -1220,6 +1226,7 @@ static int ipu_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
 			of_match_device(imx_ipu_dt_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
 	struct ipu_soc *ipu;
 	struct resource *res;
 	unsigned long ipu_base;
@@ -1248,6 +1255,7 @@ static int ipu_probe(struct platform_device *pdev)
 		ipu->channel[i].ipu = ipu;
 	ipu->devtype = devtype;
 	ipu->ipu_type = devtype->type;
+	ipu->id = of_alias_get_id(np, "ipu");
 
 	spin_lock_init(&ipu->lock);
 	mutex_init(&ipu->channel_lock);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 845f64c..02057d8 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -153,6 +153,7 @@ struct ipu_soc {
 	void __iomem		*cm_reg;
 	void __iomem		*idmac_reg;
 
+	int			id;
 	int			usecount;
 
 	struct clk		*clk;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 5d59739..8a6ccaa 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -148,6 +148,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 /*
  * IPU Common functions
  */
+int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
 void ipu_dump(struct ipu_soc *ipu);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 04/13] gpu: ipu-v3: Add ipu_get_num()
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds of-alias id to ipu_soc and retrieve with ipu_get_num().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 8 ++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    | 1 +
 include/video/imx-ipu-v3.h      | 1 +
 3 files changed, 10 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 30dc115..49af121 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
 	writel(value, ipu->cm_reg + offset);
 }
 
+int ipu_get_num(struct ipu_soc *ipu)
+{
+	return ipu->id;
+}
+EXPORT_SYMBOL_GPL(ipu_get_num);
+
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
 {
 	u32 val;
@@ -1220,6 +1226,7 @@ static int ipu_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id  			of_match_device(imx_ipu_dt_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
 	struct ipu_soc *ipu;
 	struct resource *res;
 	unsigned long ipu_base;
@@ -1248,6 +1255,7 @@ static int ipu_probe(struct platform_device *pdev)
 		ipu->channel[i].ipu = ipu;
 	ipu->devtype = devtype;
 	ipu->ipu_type = devtype->type;
+	ipu->id = of_alias_get_id(np, "ipu");
 
 	spin_lock_init(&ipu->lock);
 	mutex_init(&ipu->channel_lock);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 845f64c..02057d8 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -153,6 +153,7 @@ struct ipu_soc {
 	void __iomem		*cm_reg;
 	void __iomem		*idmac_reg;
 
+	int			id;
 	int			usecount;
 
 	struct clk		*clk;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 5d59739..8a6ccaa 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -148,6 +148,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 /*
  * IPU Common functions
  */
+int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
 void ipu_dump(struct ipu_soc *ipu);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds functions to link and unlink IDMAC source channels to sink
channels.

So far the following links are supported:

IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP

More links can be added to the idmac_link_info[] array.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- replaced hardcoded channel numbers in idmac_link_info[] with
  above channel names.
---
 drivers/gpu/ipu-v3/ipu-common.c | 118 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |   3 +
 2 files changed, 121 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 49af121..1c5aff4 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,124 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+
+/* IDMAC Channel Linking */
+
+struct idmac_link_reg_info {
+	int chno;
+	u32 reg;
+	int shift;
+	int bits;
+	u32 sel;
+};
+
+struct idmac_link_info {
+	struct idmac_link_reg_info src;
+	struct idmac_link_reg_info sink;
+};
+
+static const struct idmac_link_info idmac_link_info[] = {
+	{
+		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
+			  0, 4, 7 },
+		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
+			  0, 4, 1 },
+	}, {
+		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
+			  8, 4, 8 },
+		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
+			  4, 4, 1 },
+	}, {
+		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
+			  16, 4, 5 },
+		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
+			  12, 4, 3 },
+	},
+};
+
+static const struct idmac_link_info *find_idmac_link_info(
+	struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(idmac_link_info); i++) {
+		if (src->num == idmac_link_info[i].src.chno &&
+		    sink->num == idmac_link_info[i].sink.chno)
+			return &idmac_link_info[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Links an IDMAC source channel to a sink channel.
+ */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	src_reg |= (link->src.sel << link->src.shift);
+
+	sink_reg &= ~sink_mask;
+	sink_reg |= (link->sink.sel << link->sink.shift);
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/*
+ * Unlinks IDMAC source and sink channels.
+ */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	sink_reg &= ~sink_mask;
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
 struct ipu_devtype {
 	const char *name;
 	unsigned long cm_ofs;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 8a6ccaa..b252cb4 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -128,6 +128,7 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_ROT_VF_MEM		49
 #define IPUV3_CHANNEL_ROT_PP_MEM		50
 #define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA		51
+#define IPUV3_NUM_CHANNELS			64
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
@@ -171,6 +172,8 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink);
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink);
 
 /*
  * IPU Channel Parameter Memory (cpmem) functions
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds functions to link and unlink IDMAC source channels to sink
channels.

So far the following links are supported:

IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP

More links can be added to the idmac_link_info[] array.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- replaced hardcoded channel numbers in idmac_link_info[] with
  above channel names.
---
 drivers/gpu/ipu-v3/ipu-common.c | 118 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |   3 +
 2 files changed, 121 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 49af121..1c5aff4 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,124 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+
+/* IDMAC Channel Linking */
+
+struct idmac_link_reg_info {
+	int chno;
+	u32 reg;
+	int shift;
+	int bits;
+	u32 sel;
+};
+
+struct idmac_link_info {
+	struct idmac_link_reg_info src;
+	struct idmac_link_reg_info sink;
+};
+
+static const struct idmac_link_info idmac_link_info[] = {
+	{
+		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
+			  0, 4, 7 },
+		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
+			  0, 4, 1 },
+	}, {
+		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
+			  8, 4, 8 },
+		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
+			  4, 4, 1 },
+	}, {
+		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
+			  16, 4, 5 },
+		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
+			  12, 4, 3 },
+	},
+};
+
+static const struct idmac_link_info *find_idmac_link_info(
+	struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(idmac_link_info); i++) {
+		if (src->num = idmac_link_info[i].src.chno &&
+		    sink->num = idmac_link_info[i].sink.chno)
+			return &idmac_link_info[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Links an IDMAC source channel to a sink channel.
+ */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	src_reg |= (link->src.sel << link->src.shift);
+
+	sink_reg &= ~sink_mask;
+	sink_reg |= (link->sink.sel << link->sink.shift);
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/*
+ * Unlinks IDMAC source and sink channels.
+ */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	sink_reg &= ~sink_mask;
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
 struct ipu_devtype {
 	const char *name;
 	unsigned long cm_ofs;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 8a6ccaa..b252cb4 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -128,6 +128,7 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_ROT_VF_MEM		49
 #define IPUV3_CHANNEL_ROT_PP_MEM		50
 #define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA		51
+#define IPUV3_NUM_CHANNELS			64
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
@@ -171,6 +172,8 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink);
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink);
 
 /*
  * IPU Channel Parameter Memory (cpmem) functions
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 06/13] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds ipu_set_vdi_src_mux() that selects the VDIC input
(from CSI or memory).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- added macros for the VDI source select bits in register IPU_FS_PROC_FLOW1.
---
 drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    |  6 ++++++
 include/video/imx-ipu-v3.h      |  1 +
 3 files changed, 27 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 1c5aff4..bb0377b 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+/*
+ * Set the source for the VDIC. Selects either from CSI[01] or memory.
+ */
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+	val &= ~FS_VDI_SRC_SEL_MASK;
+	if (csi)
+		val |= FS_VDI_SRC_SEL_CSI_DIRECT;
+	ipu_cm_write(ipu, val, IPU_FS_PROC_FLOW1);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_vdi_src_mux);
+
 
 /* IDMAC Channel Linking */
 
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 02057d8..74445fb 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -78,6 +78,12 @@ struct ipu_soc;
 #define IPU_DI0_COUNTER_RELEASE			(1 << 24)
 #define IPU_DI1_COUNTER_RELEASE			(1 << 25)
 
+/* IPU_FS_PROC_FLOW1 */
+#define FS_VDI_SRC_SEL_OFFSET			28
+#define FS_VDI_SRC_SEL_MASK			(0x3 << 28)
+#define FS_VDI_SRC_SEL_CSI_DIRECT		(0x1 << 28)
+#define FS_VDI_SRC_SEL_VDOA			(0x2 << 28)
+
 #define IPU_IDMAC_REG(offset)	(offset)
 
 #define IDMAC_CONF			IPU_IDMAC_REG(0x0000)
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index b252cb4..74d3059 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -152,6 +152,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi);
 void ipu_dump(struct ipu_soc *ipu);
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 06/13] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds ipu_set_vdi_src_mux() that selects the VDIC input
(from CSI or memory).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- added macros for the VDI source select bits in register IPU_FS_PROC_FLOW1.
---
 drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    |  6 ++++++
 include/video/imx-ipu-v3.h      |  1 +
 3 files changed, 27 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 1c5aff4..bb0377b 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+/*
+ * Set the source for the VDIC. Selects either from CSI[01] or memory.
+ */
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+	val &= ~FS_VDI_SRC_SEL_MASK;
+	if (csi)
+		val |= FS_VDI_SRC_SEL_CSI_DIRECT;
+	ipu_cm_write(ipu, val, IPU_FS_PROC_FLOW1);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_vdi_src_mux);
+
 
 /* IDMAC Channel Linking */
 
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 02057d8..74445fb 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -78,6 +78,12 @@ struct ipu_soc;
 #define IPU_DI0_COUNTER_RELEASE			(1 << 24)
 #define IPU_DI1_COUNTER_RELEASE			(1 << 25)
 
+/* IPU_FS_PROC_FLOW1 */
+#define FS_VDI_SRC_SEL_OFFSET			28
+#define FS_VDI_SRC_SEL_MASK			(0x3 << 28)
+#define FS_VDI_SRC_SEL_CSI_DIRECT		(0x1 << 28)
+#define FS_VDI_SRC_SEL_VDOA			(0x2 << 28)
+
 #define IPU_IDMAC_REG(offset)	(offset)
 
 #define IDMAC_CONF			IPU_IDMAC_REG(0x0000)
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index b252cb4..74d3059 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -152,6 +152,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi);
 void ipu_dump(struct ipu_soc *ipu);
 
 /*
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 07/13] gpu: ipu-v3: Add VDI input IDMAC channels
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds the VDIC field input IDMAC channels. These channels
transfer fields F(n-1), F(n), and F(N+1) from memory to
the VDIC (channels 8, 9, 10 respectively).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- made the channel names more descriptive: "_PREV" instead of "_P", etc.
---
 include/video/imx-ipu-v3.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 74d3059..afbc89d 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -107,6 +107,9 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_CSI2			 2
 #define IPUV3_CHANNEL_CSI3			 3
 #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
+#define IPUV3_CHANNEL_MEM_VDI_PREV		 8
+#define IPUV3_CHANNEL_MEM_VDI_CUR		 9
+#define IPUV3_CHANNEL_MEM_VDI_NEXT		10
 #define IPUV3_CHANNEL_MEM_IC_PP			11
 #define IPUV3_CHANNEL_MEM_IC_PRP_VF		12
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 07/13] gpu: ipu-v3: Add VDI input IDMAC channels
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Adds the VDIC field input IDMAC channels. These channels
transfer fields F(n-1), F(n), and F(N+1) from memory to
the VDIC (channels 8, 9, 10 respectively).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- made the channel names more descriptive: "_PREV" instead of "_P", etc.
---
 include/video/imx-ipu-v3.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 74d3059..afbc89d 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -107,6 +107,9 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_CSI2			 2
 #define IPUV3_CHANNEL_CSI3			 3
 #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
+#define IPUV3_CHANNEL_MEM_VDI_PREV		 8
+#define IPUV3_CHANNEL_MEM_VDI_CUR		 9
+#define IPUV3_CHANNEL_MEM_VDI_NEXT		10
 #define IPUV3_CHANNEL_MEM_IC_PP			11
 #define IPUV3_CHANNEL_MEM_IC_PRP_VF		12
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 08/13] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Set the sensor full frame based on whether the passed in mbus_fmt
is 720x480 (NTSC) or 720x576 (PAL).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 06631ac..641ed76 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -365,10 +365,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 {
 	struct ipu_csi_bus_config cfg;
 	unsigned long flags;
-	u32 data = 0;
+	u32 width, height, data = 0;
 
 	fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
 
+	/* set default sensor frame width and height */
+	width = mbus_fmt->width;
+	height = mbus_fmt->height;
+
 	/* Set the CSI_SENS_CONF register remaining fields */
 	data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
 		cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
@@ -386,11 +390,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 	ipu_csi_write(csi, data, CSI_SENS_CONF);
 
-	/* Setup sensor frame size */
-	ipu_csi_write(csi,
-		      (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
-		      CSI_SENS_FRM_SIZE);
-
 	/* Set CCIR registers */
 
 	switch (cfg.clk_mode) {
@@ -408,11 +407,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
 			 * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
 			 */
+			height = 625; /* framelines for PAL */
+
 			ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
-
 		} else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
 			/*
 			 * NTSC case
@@ -422,6 +422,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
 			 * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
 			 */
+			height = 525; /* framelines for NTSC */
+
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
@@ -447,6 +449,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 		break;
 	}
 
+	/* Setup sensor frame size */
+	ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
+		      CSI_SENS_FRM_SIZE);
+
 	dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
 		ipu_csi_read(csi, CSI_SENS_CONF));
 	dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 08/13] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Set the sensor full frame based on whether the passed in mbus_fmt
is 720x480 (NTSC) or 720x576 (PAL).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 06631ac..641ed76 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -365,10 +365,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 {
 	struct ipu_csi_bus_config cfg;
 	unsigned long flags;
-	u32 data = 0;
+	u32 width, height, data = 0;
 
 	fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
 
+	/* set default sensor frame width and height */
+	width = mbus_fmt->width;
+	height = mbus_fmt->height;
+
 	/* Set the CSI_SENS_CONF register remaining fields */
 	data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
 		cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
@@ -386,11 +390,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 	ipu_csi_write(csi, data, CSI_SENS_CONF);
 
-	/* Setup sensor frame size */
-	ipu_csi_write(csi,
-		      (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
-		      CSI_SENS_FRM_SIZE);
-
 	/* Set CCIR registers */
 
 	switch (cfg.clk_mode) {
@@ -408,11 +407,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
 			 * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
 			 */
+			height = 625; /* framelines for PAL */
+
 			ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
-
 		} else if (mbus_fmt->width = 720 && mbus_fmt->height = 480) {
 			/*
 			 * NTSC case
@@ -422,6 +422,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
 			 * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
 			 */
+			height = 525; /* framelines for NTSC */
+
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
@@ -447,6 +449,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 		break;
 	}
 
+	/* Setup sensor frame size */
+	ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
+		      CSI_SENS_FRM_SIZE);
+
 	dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
 		ipu_csi_read(csi, CSI_SENS_CONF));
 	dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 09/13] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

The CSI data format was being programmed incorrectly for the
1x16 media bus formats. The CSI data format for 16-bit must
be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 641ed76..d6e5ded 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
 		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 		break;
 	case MEDIA_BUS_FMT_UYVY8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-		cfg->mipi_dt = MIPI_DT_YUV422;
-		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
-		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 		cfg->mipi_dt = MIPI_DT_YUV422;
 		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
 		break;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 09/13] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

The CSI data format was being programmed incorrectly for the
1x16 media bus formats. The CSI data format for 16-bit must
be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 641ed76..d6e5ded 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
 		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 		break;
 	case MEDIA_BUS_FMT_UYVY8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-		cfg->mipi_dt = MIPI_DT_YUV422;
-		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
-		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 		cfg->mipi_dt = MIPI_DT_YUV422;
 		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
 		break;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 10/13] gpu: ipu-v3: Fix IRT usage
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

There can be multiple IC tasks using the IRT, so the IRT needs
a separate use counter. Create a private ipu_irt_enable() to
enable the IRT module when any IC task requires rotation, and
ipu_irt_disable() when a task no longer needs the IRT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- cleaned up irt_use_count decrement in ipu_irt_disable()
---
 drivers/gpu/ipu-v3/ipu-ic.c | 40 +++++++++++++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1dcb96c..1a37afc 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -160,6 +160,7 @@ struct ipu_ic_priv {
 	spinlock_t lock;
 	struct ipu_soc *ipu;
 	int use_count;
+	int irt_use_count;
 	struct ipu_ic task[IC_NUM_TASKS];
 };
 
@@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
 
 	ipu_ic_write(ic, ic_conf, IC_CONF);
 
-	ic->rotation = ic->graphics = false;
-
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
@@ -629,22 +628,41 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+static void ipu_irt_enable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (!priv->irt_use_count)
+		ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN);
+
+	priv->irt_use_count++;
+}
+
+static void ipu_irt_disable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (priv->irt_use_count) {
+		if (!--priv->irt_use_count)
+			ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN);
+	}
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	if (ic->rotation)
-		module |= IPU_CONF_ROT_EN;
-
 	if (!priv->use_count)
-		ipu_module_enable(priv->ipu, module);
+		ipu_module_enable(priv->ipu, IPU_CONF_IC_EN);
 
 	priv->use_count++;
 
+	if (ic->rotation)
+		ipu_irt_enable(ic);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -655,18 +673,22 @@ int ipu_ic_disable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
 	priv->use_count--;
 
 	if (!priv->use_count)
-		ipu_module_disable(priv->ipu, module);
+		ipu_module_disable(priv->ipu, IPU_CONF_IC_EN);
 
 	if (priv->use_count < 0)
 		priv->use_count = 0;
 
+	if (ic->rotation)
+		ipu_irt_disable(ic);
+
+	ic->rotation = ic->graphics = false;
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 10/13] gpu: ipu-v3: Fix IRT usage
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

There can be multiple IC tasks using the IRT, so the IRT needs
a separate use counter. Create a private ipu_irt_enable() to
enable the IRT module when any IC task requires rotation, and
ipu_irt_disable() when a task no longer needs the IRT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

---

v2:
- cleaned up irt_use_count decrement in ipu_irt_disable()
---
 drivers/gpu/ipu-v3/ipu-ic.c | 40 +++++++++++++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1dcb96c..1a37afc 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -160,6 +160,7 @@ struct ipu_ic_priv {
 	spinlock_t lock;
 	struct ipu_soc *ipu;
 	int use_count;
+	int irt_use_count;
 	struct ipu_ic task[IC_NUM_TASKS];
 };
 
@@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
 
 	ipu_ic_write(ic, ic_conf, IC_CONF);
 
-	ic->rotation = ic->graphics = false;
-
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
@@ -629,22 +628,41 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+static void ipu_irt_enable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (!priv->irt_use_count)
+		ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN);
+
+	priv->irt_use_count++;
+}
+
+static void ipu_irt_disable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (priv->irt_use_count) {
+		if (!--priv->irt_use_count)
+			ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN);
+	}
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	if (ic->rotation)
-		module |= IPU_CONF_ROT_EN;
-
 	if (!priv->use_count)
-		ipu_module_enable(priv->ipu, module);
+		ipu_module_enable(priv->ipu, IPU_CONF_IC_EN);
 
 	priv->use_count++;
 
+	if (ic->rotation)
+		ipu_irt_enable(ic);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -655,18 +673,22 @@ int ipu_ic_disable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
 	priv->use_count--;
 
 	if (!priv->use_count)
-		ipu_module_disable(priv->ipu, module);
+		ipu_module_disable(priv->ipu, IPU_CONF_IC_EN);
 
 	if (priv->use_count < 0)
 		priv->use_count = 0;
 
+	if (ic->rotation)
+		ipu_irt_disable(ic);
+
+	ic->rotation = ic->graphics = false;
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

This patch implements complete image conversion support to ipu-ic,
with tiling to support scaling to and from images up to 4096x4096.
Image rotation is also supported.

The internal API is subsystem agnostic (no V4L2 dependency except
for the use of V4L2 fourcc pixel formats).

Callers prepare for image conversion by calling
ipu_image_convert_prepare(), which initializes the parameters of
the conversion. The caller passes in the ipu_ic task to use for
the conversion, the input and output image formats, a rotation mode,
and a completion callback and completion context pointer:

struct image_converter_ctx *
ipu_image_convert_prepare(struct ipu_ic *ic,
                          struct ipu_image *in, struct ipu_image *out,
                          enum ipu_rotate_mode rot_mode,
                          image_converter_cb_t complete,
                          void *complete_context);

The caller is given a new conversion context that must be passed to
the further APIs:

struct image_converter_run *
ipu_image_convert_run(struct image_converter_ctx *ctx,
                      dma_addr_t in_phys, dma_addr_t out_phys);

This queues a new image conversion request to a run queue, and
starts the conversion immediately if the run queue is empty. Only
the physaddr's of the input and output image buffers are needed,
since the conversion context was created previously with
ipu_image_convert_prepare(). Returns a new run object pointer. When
the conversion completes, the run pointer is returned to the
completion callback.

void image_convert_abort(struct image_converter_ctx *ctx);

This will abort any active or pending conversions for this context.
Any currently active or pending runs belonging to this context are
returned via the completion callback with an error status.

void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);

Unprepares the conversion context. Any active or pending runs will
be aborted by calling image_convert_abort().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |   57 +-
 2 files changed, 1736 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1a37afc..5471b72 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -17,6 +17,8 @@
 #include <linux/bitrev.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
 #include "ipu-prv.h"
 
 /* IC Register Offsets */
@@ -82,6 +84,40 @@
 #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
 #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
 
+/*
+ * The IC Resizer has a restriction that the output frame from the
+ * resizer must be 1024 or less in both width (pixels) and height
+ * (lines).
+ *
+ * The image conversion support attempts to split up a conversion when
+ * the desired output (converted) frame resolution exceeds the IC resizer
+ * limit of 1024 in either dimension.
+ *
+ * If either dimension of the output frame exceeds the limit, the
+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum
+ * of 4*4 or 16 tiles. A conversion is then carried out for each
+ * tile (but taking care to pass the full frame stride length to
+ * the DMA channel's parameter memory!). IDMA double-buffering is used
+ * to convert each tile back-to-back when possible (see note below
+ * when double_buffering boolean is set).
+ *
+ * Note that the input frame must be split up into the same number
+ * of tiles as the output frame.
+ */
+#define MAX_STRIPES_W    4
+#define MAX_STRIPES_H    4
+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
+
+#define MIN_W     128
+#define MIN_H     128
+#define MAX_W     4096
+#define MAX_H     4096
+
+enum image_convert_type {
+	IMAGE_CONVERT_IN = 0,
+	IMAGE_CONVERT_OUT,
+};
+
 struct ic_task_regoffs {
 	u32 rsc;
 	u32 tpmem_csc[2];
@@ -96,6 +132,16 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	int in;
+	int out;
+	int rot_in;
+	int rot_out;
+	int vdi_in_p;
+	int vdi_in;
+	int vdi_in_n;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
 	},
 };
 
+static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
+	[IC_TASK_ENCODER] = {
+		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
+		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
+	},
+	[IC_TASK_VIEWFINDER] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV,
+		.vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR,
+		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT,
+	},
+	[IC_TASK_POST_PROCESSOR] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PP,
+		.out = IPUV3_CHANNEL_IC_PP_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
+		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
+	},
+};
+
+struct ipu_ic_dma_buf {
+	void          *virt;
+	dma_addr_t    phys;
+	unsigned long len;
+};
+
+/* dimensions of one tile */
+struct ipu_ic_tile {
+	unsigned int width;
+	unsigned int height;
+	/* size and strides are in bytes */
+	unsigned int size;
+	unsigned int stride;
+	unsigned int rot_stride;
+};
+
+struct ipu_ic_tile_off {
+	/* start Y or packed offset of this tile */
+	u32     offset;
+	/* offset from start to tile in U plane, for planar formats */
+	u32     u_off;
+	/* offset from start to tile in V plane, for planar formats */
+	u32     v_off;
+};
+
+struct ipu_ic_pixfmt {
+	char	*name;
+	u32	fourcc;        /* V4L2 fourcc */
+	int     bpp;           /* total bpp */
+	int     y_depth;       /* depth of Y plane for planar formats */
+	int     uv_width_dec;  /* decimation in width for U/V planes */
+	int     uv_height_dec; /* decimation in height for U/V planes */
+	bool    uv_swapped;    /* U and V planes are swapped */
+	bool    uv_packed;     /* partial planar (U and V in same plane) */
+};
+
+struct ipu_ic_image {
+	struct ipu_image base;
+	enum image_convert_type type;
+
+	const struct ipu_ic_pixfmt *fmt;
+	unsigned int stride;
+
+	/* # of rows (horizontal stripes) if dest height is > 1024 */
+	unsigned int num_rows;
+	/* # of columns (vertical stripes) if dest width is > 1024 */
+	unsigned int num_cols;
+
+	struct ipu_ic_tile tile;
+	struct ipu_ic_tile_off tile_off[MAX_TILES];
+};
+
+struct image_converter_ctx;
+struct image_converter;
 struct ipu_ic_priv;
+struct ipu_ic;
+
+struct image_converter_run {
+	struct image_converter_ctx *ctx;
+
+	dma_addr_t in_phys;
+	dma_addr_t out_phys;
+
+	int status;
+
+	struct list_head list;
+};
+
+struct image_converter_ctx {
+	struct image_converter *cvt;
+
+	image_converter_cb_t complete;
+	void *complete_context;
+
+	/* Source/destination image data and rotation mode */
+	struct ipu_ic_image in;
+	struct ipu_ic_image out;
+	enum ipu_rotate_mode rot_mode;
+
+	/* intermediate buffer for rotation */
+	struct ipu_ic_dma_buf rot_intermediate[2];
+
+	/* current buffer number for double buffering */
+	int cur_buf_num;
+
+	bool aborting;
+	struct completion aborted;
+
+	/* can we use double-buffering for this conversion operation? */
+	bool double_buffering;
+	/* num_rows * num_cols */
+	unsigned int num_tiles;
+	/* next tile to process */
+	unsigned int next_tile;
+	/* where to place converted tile in dest image */
+	unsigned int out_tile_map[MAX_TILES];
+
+	struct list_head list;
+};
+
+struct image_converter {
+	struct ipu_ic *ic;
+
+	struct ipuv3_channel *in_chan;
+	struct ipuv3_channel *out_chan;
+	struct ipuv3_channel *rotation_in_chan;
+	struct ipuv3_channel *rotation_out_chan;
+
+	/* the IPU end-of-frame irqs */
+	int out_eof_irq;
+	int rot_out_eof_irq;
+
+	spinlock_t irqlock;
+
+	/* list of convert contexts */
+	struct list_head ctx_list;
+	/* queue of conversion runs */
+	struct list_head pending_q;
+	/* queue of completed runs */
+	struct list_head done_q;
+
+	/* the current conversion run */
+	struct image_converter_run *current_run;
+};
 
 struct ipu_ic {
 	enum ipu_ic_task task;
 	const struct ic_task_regoffs *reg;
 	const struct ic_task_bitfields *bit;
+	const struct ic_task_channels *ch;
 
 	enum ipu_color_space in_cs, g_in_cs;
 	enum ipu_color_space out_cs;
@@ -151,6 +344,8 @@ struct ipu_ic {
 	bool rotation;
 	bool in_use;
 
+	struct image_converter cvt;
+
 	struct ipu_ic_priv *priv;
 };
 
@@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
 	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
 
-	if (rot >= IPU_ROTATE_90_RIGHT)
+	if (ipu_rot_mode_is_irt(rot))
 		ic->rotation = true;
 
 unlock:
@@ -648,6 +843,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
 	}
 }
 
+/*
+ * Complete image conversion support follows
+ */
+
+static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.bpp    = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+	}, {
+		.name	= "4:2:0 planar, YVU",
+		.fourcc	= V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_swapped = true,
+	}, {
+		.name   = "4:2:0 partial planar, NV12",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_packed = true,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name   = "4:2:2 partial planar, NV16",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+		.uv_packed = true,
+	},
+};
+
+static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
+{
+	const struct ipu_ic_pixfmt *ret = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
+		if (ipu_ic_formats[i].fourcc == fourcc) {
+			ret = &ipu_ic_formats[i];
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
+			       struct ipu_ic_image *ic_image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev,
+		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
+		ctx,
+		ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
+		ic_image->base.pix.width, ic_image->base.pix.height,
+		ic_image->num_cols, ic_image->num_rows,
+		ic_image->tile.width, ic_image->tile.height,
+		ic_image->fmt->fourcc & 0xff,
+		(ic_image->fmt->fourcc >> 8) & 0xff,
+		(ic_image->fmt->fourcc >> 16) & 0xff,
+		(ic_image->fmt->fourcc >> 24) & 0xff);
+}
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
+{
+	const struct ipu_ic_pixfmt *fmt;
+
+	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
+		return -EINVAL;
+
+	/* Format found */
+	fmt = &ipu_ic_formats[index];
+	*desc = fmt->name;
+	*fourcc = fmt->fourcc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
+
+static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf)
+{
+	if (buf->virt)
+		dma_free_coherent(priv->ipu->dev,
+				  buf->len, buf->virt, buf->phys);
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf,
+				int size)
+{
+	unsigned long newlen = PAGE_ALIGN(size);
+
+	if (buf->virt) {
+		if (buf->len == newlen)
+			return 0;
+		ipu_ic_free_dma_buf(priv, buf);
+	}
+
+	buf->len = newlen;
+	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline int ipu_ic_num_stripes(int dim)
+{
+	if (dim <= 1024)
+		return 1;
+	else if (dim <= 2048)
+		return 2;
+	else
+		return 4;
+}
+
+static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
+					struct ipu_ic_image *image)
+{
+	struct ipu_ic_tile *tile = &image->tile;
+
+	tile->height = image->base.pix.height / image->num_rows;
+	tile->width = image->base.pix.width / image->num_cols;
+	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
+
+	if (image->fmt->y_depth) {
+		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
+	} else {
+		tile->stride = (image->fmt->bpp * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
+	}
+}
+
+/*
+ * Use the rotation transformation to find the tile coordinates
+ * (row, col) of a tile in the destination frame that corresponds
+ * to the given tile coordinates of a source frame. The destination
+ * coordinate is then converted to a tile index.
+ */
+static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
+				       int src_row, int src_col)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	int cos, sin, dst_row, dst_col;
+
+	/* with no rotation it's a 1:1 mapping */
+	if (ctx->rot_mode == IPU_ROTATE_NONE)
+		return src_row * s_image->num_cols + src_col;
+
+	if (ctx->rot_mode & IPU_ROT_BIT_90) {
+		cos = 0;
+		sin = 1;
+	} else {
+		cos = 1;
+		sin = 0;
+	}
+
+	/*
+	 * before doing the transform, first we have to translate
+	 * source row,col for an origin in the center of s_image
+	 */
+	src_row *= 2;
+	src_col *= 2;
+	src_row -= s_image->num_rows - 1;
+	src_col -= s_image->num_cols - 1;
+
+	/* do the rotation transform */
+	dst_col = src_col * cos - src_row * sin;
+	dst_row = src_col * sin + src_row * cos;
+
+	/* apply flip */
+	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
+		dst_col = -dst_col;
+	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
+		dst_row = -dst_row;
+
+	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
+		ctx, src_col, src_row, dst_col, dst_row);
+
+	/*
+	 * finally translate dest row,col using an origin in upper
+	 * left of d_image
+	 */
+	dst_row += d_image->num_rows - 1;
+	dst_col += d_image->num_cols - 1;
+	dst_row /= 2;
+	dst_col /= 2;
+
+	return dst_row * d_image->num_cols + dst_col;
+}
+
+/*
+ * Fill the out_tile_map[] with transformed destination tile indeces.
+ */
+static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
+{
+	struct ipu_ic_image *s_image = &ctx->in;
+	unsigned int row, col, tile = 0;
+
+	for (row = 0; row < s_image->num_rows; row++) {
+		for (col = 0; col < s_image->num_cols; col++) {
+			ctx->out_tile_map[tile] =
+				ipu_ic_transform_tile_index(ctx, row, col);
+			tile++;
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 H, w, h, y_depth, y_stride, uv_stride;
+	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
+	u32 y_row_off, y_col_off, y_off;
+	u32 y_size, uv_size;
+
+	/* setup some convenience vars */
+	H = image->base.pix.height;
+	w = image->tile.width;
+	h = image->tile.height;
+
+	y_depth = fmt->y_depth;
+	y_stride = image->stride;
+	uv_stride = y_stride / fmt->uv_width_dec;
+	if (fmt->uv_packed)
+		uv_stride *= 2;
+
+	y_size = H * y_stride;
+	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
+
+	for (row = 0; row < image->num_rows; row++) {
+		y_row_off = row * h * y_stride;
+		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
+
+		for (col = 0; col < image->num_cols; col++) {
+			y_col_off = (col * w * y_depth) >> 3;
+			uv_col_off = y_col_off / fmt->uv_width_dec;
+			if (fmt->uv_packed)
+				uv_col_off *= 2;
+
+			y_off = y_row_off + y_col_off;
+			uv_off = uv_row_off + uv_col_off;
+
+			u_off = y_size - y_off + uv_off;
+			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
+			if (fmt->uv_swapped) {
+				tmp = u_off;
+				u_off = v_off;
+				v_off = tmp;
+			}
+
+			image->tile_off[tile].offset = y_off;
+			image->tile_off[tile].u_off = u_off;
+			image->tile_off[tile++].v_off = v_off;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
+				ctx, image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				y_off, u_off, v_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 w, h, bpp, stride;
+	u32 row_off, col_off;
+
+	/* setup some convenience vars */
+	w = image->tile.width;
+	h = image->tile.height;
+	stride = image->stride;
+	bpp = fmt->bpp;
+
+	for (row = 0; row < image->num_rows; row++) {
+		row_off = row * h * stride;
+
+		for (col = 0; col < image->num_cols; col++) {
+			col_off = (col * w * bpp) >> 3;
+
+			image->tile_off[tile].offset = row_off + col_off;
+			image->tile_off[tile].u_off = 0;
+			image->tile_off[tile++].v_off = 0;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
+				image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				row_off + col_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
+				     struct ipu_ic_image *image)
+{
+	memset(image->tile_off, 0, sizeof(image->tile_off));
+
+	if (image->fmt->y_depth)
+		ipu_ic_calc_tile_offsets_planar(ctx, image);
+	else
+		ipu_ic_calc_tile_offsets_packed(ctx, image);
+}
+
+/*
+ * return the number of runs in given queue (pending_q or done_q)
+ * for this context. hold irqlock when calling.
+ */
+static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
+				struct list_head *q)
+{
+	struct image_converter_run *run;
+	int count = 0;
+
+	list_for_each_entry(run, q, list) {
+		if (run->ctx == ctx)
+			count++;
+	}
+
+	return count;
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_convert_stop(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
+		__func__, ctx, run);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(cvt->ic);
+	ipu_idmac_disable_channel(cvt->in_chan);
+	ipu_idmac_disable_channel(cvt->out_chan);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_disable_channel(cvt->rotation_in_chan);
+		ipu_idmac_disable_channel(cvt->rotation_out_chan);
+		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
+	}
+
+	ipu_ic_disable(cvt->ic);
+}
+
+/* hold irqlock when calling */
+static void init_idmac_channel(struct image_converter_ctx *ctx,
+			       struct ipuv3_channel *channel,
+			       struct ipu_ic_image *image,
+			       enum ipu_rotate_mode rot_mode,
+			       bool rot_swap_width_height)
+{
+	struct image_converter *cvt = ctx->cvt;
+	unsigned int burst_size;
+	u32 width, height, stride;
+	dma_addr_t addr0, addr1 = 0;
+	struct ipu_image tile_image;
+	unsigned int tile_idx[2];
+
+	if (image->type == IMAGE_CONVERT_OUT) {
+		tile_idx[0] = ctx->out_tile_map[0];
+		tile_idx[1] = ctx->out_tile_map[1];
+	} else {
+		tile_idx[0] = 0;
+		tile_idx[1] = 1;
+	}
+
+	if (rot_swap_width_height) {
+		width = image->tile.height;
+		height = image->tile.width;
+		stride = image->tile.rot_stride;
+		addr0 = ctx->rot_intermediate[0].phys;
+		if (ctx->double_buffering)
+			addr1 = ctx->rot_intermediate[1].phys;
+	} else {
+		width = image->tile.width;
+		height = image->tile.height;
+		stride = image->stride;
+		addr0 = image->base.phys0 +
+			image->tile_off[tile_idx[0]].offset;
+		if (ctx->double_buffering)
+			addr1 = image->base.phys0 +
+				image->tile_off[tile_idx[1]].offset;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&tile_image, 0, sizeof(tile_image));
+	tile_image.pix.width = tile_image.rect.width = width;
+	tile_image.pix.height = tile_image.rect.height = height;
+	tile_image.pix.bytesperline = stride;
+	tile_image.pix.pixelformat =  image->fmt->fourcc;
+	tile_image.phys0 = addr0;
+	tile_image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &tile_image);
+
+	if (image->fmt->y_depth && !rot_swap_width_height)
+		ipu_cpmem_set_uv_offset(channel,
+					image->tile_off[tile_idx[0]].u_off,
+					image->tile_off[tile_idx[0]].v_off);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (channel == cvt->rotation_in_chan ||
+	    channel == cvt->rotation_out_chan) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else
+		burst_size = (width % 16) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
+			      burst_size, rot_mode);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_convert_start(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	enum ipu_color_space src_cs, dest_cs;
+	unsigned int dest_width, dest_height;
+	int ret;
+
+	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
+		__func__, ctx, run);
+
+	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
+	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* swap width/height for resizer */
+		dest_width = d_image->tile.height;
+		dest_height = d_image->tile.width;
+	} else {
+		dest_width = d_image->tile.width;
+		dest_height = d_image->tile.height;
+	}
+
+	/* setup the IC resizer and CSC */
+	ret = ipu_ic_task_init(cvt->ic,
+			       s_image->tile.width,
+			       s_image->tile.height,
+			       dest_width,
+			       dest_height,
+			       src_cs, dest_cs);
+	if (ret) {
+		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
+		return ret;
+	}
+
+	/* init the source MEM-->IC PP IDMAC channel */
+	init_idmac_channel(ctx, cvt->in_chan, s_image,
+			   IPU_ROTATE_NONE, false);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* init the IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   IPU_ROTATE_NONE, true);
+
+		/* init the MEM-->IC PP ROT IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
+				   ctx->rot_mode, true);
+
+		/* init the destination IC PP ROT-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
+				   IPU_ROTATE_NONE, false);
+
+		/* now link IC PP-->MEM to MEM-->IC PP ROT */
+		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
+	} else {
+		/* init the destination IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   ctx->rot_mode, false);
+	}
+
+	/* enable the IC */
+	ipu_ic_enable(cvt->ic);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(cvt->in_chan, 0);
+	ipu_idmac_select_buffer(cvt->out_chan, 0);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode))
+		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
+	if (ctx->double_buffering) {
+		ipu_idmac_select_buffer(cvt->in_chan, 1);
+		ipu_idmac_select_buffer(cvt->out_chan, 1);
+		if (ipu_rot_mode_is_irt(ctx->rot_mode))
+			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
+	}
+
+	/* enable the channels! */
+	ipu_idmac_enable_channel(cvt->in_chan);
+	ipu_idmac_enable_channel(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_enable_channel(cvt->rotation_in_chan);
+		ipu_idmac_enable_channel(cvt->rotation_out_chan);
+	}
+
+	ipu_ic_task_enable(cvt->ic);
+
+	ipu_cpmem_dump(cvt->in_chan);
+	ipu_cpmem_dump(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_cpmem_dump(cvt->rotation_in_chan);
+		ipu_cpmem_dump(cvt->rotation_out_chan);
+	}
+
+	ipu_dump(priv->ipu);
+
+	return 0;
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_run(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+
+	ctx->in.base.phys0 = run->in_phys;
+	ctx->out.base.phys0 = run->out_phys;
+
+	ctx->cur_buf_num = 0;
+	ctx->next_tile = 1;
+
+	/* remove run from pending_q and set as current */
+	list_del(&run->list);
+	cvt->current_run = run;
+
+	return ipu_ic_convert_start(run);
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_run_next(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		/* skip contexts that are aborting */
+		if (run->ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: skipping aborting ctx %p run %p\n",
+				 __func__, run->ctx, run);
+			continue;
+		}
+
+		ret = ipu_ic_run(run);
+		if (!ret)
+			break;
+
+		/*
+		 * something went wrong with start, add the run
+		 * to done q and continue to the next run in the
+		 * pending q.
+		 */
+		run->status = ret;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+	}
+}
+
+static void ipu_ic_empty_done_q(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	while (!list_empty(&cvt->done_q)) {
+		run = list_entry(cvt->done_q.next,
+				 struct image_converter_run,
+				 list);
+
+		list_del(&run->list);
+
+		dev_dbg(priv->ipu->dev,
+			"%s: completing ctx %p run %p with %d\n",
+			__func__, run->ctx, run, run->status);
+
+		/* call the completion callback and free the run */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		run->ctx->complete(run->ctx->complete_context, run,
+				   run->status);
+		kfree(run);
+		spin_lock_irqsave(&cvt->irqlock, flags);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+}
+
+/*
+ * the bottom half thread clears out the done_q, calling the
+ * completion handler for each.
+ */
+static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
+{
+	struct image_converter *cvt = dev_id;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+
+	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
+
+	ipu_ic_empty_done_q(cvt);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/*
+	 * the done_q is cleared out, signal any contexts
+	 * that are aborting that abort can complete.
+	 */
+	list_for_each_entry(ctx, &cvt->ctx_list, list) {
+		if (ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: signaling abort for ctx %p\n",
+				 __func__, ctx);
+			complete(&ctx->aborted);
+		}
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
+	return IRQ_HANDLED;
+}
+
+/* hold irqlock when calling */
+static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_tile_off *src_off, *dst_off;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	struct ipuv3_channel *outch;
+	unsigned int dst_idx;
+
+	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
+		cvt->rotation_out_chan : cvt->out_chan;
+
+	/*
+	 * It is difficult to stop the channel DMA before the channels
+	 * enter the paused state. Without double-buffering the channels
+	 * are always in a paused state when the EOF irq occurs, so it
+	 * is safe to stop the channels now. For double-buffering we
+	 * just ignore the abort until the operation completes, when it
+	 * is safe to shut down.
+	 */
+	if (ctx->aborting && !ctx->double_buffering) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		goto done;
+	}
+
+	if (ctx->next_tile == ctx->num_tiles) {
+		/*
+		 * the conversion is complete
+		 */
+		ipu_ic_convert_stop(run);
+		run->status = 0;
+		goto done;
+	}
+
+	/*
+	 * not done, place the next tile buffers.
+	 */
+	if (!ctx->double_buffering) {
+
+		src_off = &s_image->tile_off[ctx->next_tile];
+		dst_idx = ctx->out_tile_map[ctx->next_tile];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, 0,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, 0,
+				     d_image->base.phys0 + dst_off->offset);
+		if (s_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(cvt->in_chan,
+						src_off->u_off,
+						src_off->v_off);
+		if (d_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(outch,
+						dst_off->u_off,
+						dst_off->v_off);
+
+		ipu_idmac_select_buffer(cvt->in_chan, 0);
+		ipu_idmac_select_buffer(outch, 0);
+
+	} else if (ctx->next_tile < ctx->num_tiles - 1) {
+
+		src_off = &s_image->tile_off[ctx->next_tile + 1];
+		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
+				     d_image->base.phys0 + dst_off->offset);
+
+		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
+		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
+
+		ctx->cur_buf_num ^= 1;
+	}
+
+	ctx->next_tile++;
+	return IRQ_HANDLED;
+done:
+	list_add_tail(&run->list, &cvt->done_q);
+	cvt->current_run = NULL;
+	ipu_ic_run_next(cvt);
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this is a rotation operation, just ignore */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this was NOT a rotation operation, shouldn't happen */
+		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+/*
+ * try to force the completion of runs for this ctx. Called when
+ * abort wait times out in ipu_image_convert_abort().
+ */
+static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	run = cvt->current_run;
+	if (run && run->ctx == ctx) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+		ipu_ic_run_next(cvt);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	ipu_ic_empty_done_q(cvt);
+}
+
+static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
+{
+	if (cvt->out_eof_irq >= 0)
+		free_irq(cvt->out_eof_irq, cvt);
+	if (cvt->rot_out_eof_irq >= 0)
+		free_irq(cvt->rot_out_eof_irq, cvt);
+
+	if (!IS_ERR_OR_NULL(cvt->in_chan))
+		ipu_idmac_put(cvt->in_chan);
+	if (!IS_ERR_OR_NULL(cvt->out_chan))
+		ipu_idmac_put(cvt->out_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
+		ipu_idmac_put(cvt->rotation_in_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
+		ipu_idmac_put(cvt->rotation_out_chan);
+
+	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan =
+		cvt->rotation_out_chan = NULL;
+	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
+}
+
+static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
+{
+	const struct ic_task_channels *chan = cvt->ic->ch;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	int ret;
+
+	/* get IDMAC channels */
+	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
+	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
+	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
+		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
+	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
+	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
+		dev_err(priv->ipu->dev,
+			"could not acquire idmac rotation channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* acquire the EOF interrupts */
+	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						cvt->out_chan,
+						IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->out_eof_irq,
+				   ipu_ic_norotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			 cvt->out_eof_irq);
+		cvt->out_eof_irq = -1;
+		goto err;
+	}
+
+	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						     cvt->rotation_out_chan,
+						     IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->rot_out_eof_irq,
+				   ipu_ic_rotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			cvt->rot_out_eof_irq);
+		cvt->rot_out_eof_irq = -1;
+		goto err;
+	}
+
+	return 0;
+err:
+	ipu_ic_release_ipu_resources(cvt);
+	return ret;
+}
+
+static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
+			     struct ipu_ic_image *ic_image,
+			     struct ipu_image *image,
+			     enum image_convert_type type)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	ic_image->base = *image;
+	ic_image->type = type;
+
+	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
+	if (!ic_image->fmt) {
+		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
+			type == IMAGE_CONVERT_OUT ? "Output" : "Input");
+		return -EINVAL;
+	}
+
+	if (ic_image->fmt->y_depth)
+		ic_image->stride = (ic_image->fmt->y_depth *
+				    ic_image->base.pix.width) >> 3;
+	else
+		ic_image->stride  = ic_image->base.pix.bytesperline;
+
+	ipu_ic_calc_tile_dimensions(ctx, ic_image);
+	ipu_ic_calc_tile_offsets(ctx, ic_image);
+
+	return 0;
+}
+
+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+				unsigned int max, unsigned int align)
+{
+	/* Bits that must be zero to be aligned */
+	unsigned int mask = ~((1 << align) - 1);
+
+	/* Clamp to aligned min and max */
+	x = clamp(x, (min + ~mask) & mask, max & mask);
+
+	/* Round to nearest aligned value */
+	if (align)
+		x = (x + (1 << (align - 1))) & mask;
+
+	return x;
+}
+
+/*
+ * We have to adjust the tile width such that the tile physaddrs and
+ * U and V plane offsets are multiples of 8 bytes as required by
+ * the IPU DMA Controller. For the planar formats, this corresponds
+ * to a pixel alignment of 16 (but use a more formal equation since
+ * the variables are available). For all the packed formats, 8 is
+ * good enough.
+ */
+static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
+{
+	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
+}
+
+/*
+ * For tile height alignment, we have to ensure that the output tile
+ * heights are multiples of 8 lines if the IRT is required by the
+ * given rotation mode (the IRT performs rotations on 8x8 blocks
+ * at a time). If the IRT is not used, or for input image tiles,
+ * 2 lines are good enough.
+ */
+static inline u32 tile_height_align(enum image_convert_type type,
+				    enum ipu_rotate_mode rot_mode)
+{
+	return (type == IMAGE_CONVERT_OUT &&
+		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
+}
+
+/* Adjusts input/output images to IPU restrictions */
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	const struct ipu_ic_pixfmt *infmt, *outfmt;
+	unsigned int num_in_rows, num_in_cols;
+	unsigned int num_out_rows, num_out_cols;
+	u32 w_align, h_align;
+
+	infmt = ipu_ic_get_format(in->pix.pixelformat);
+	outfmt = ipu_ic_get_format(out->pix.pixelformat);
+
+	/* set some defaults if needed */
+	if (!infmt) {
+		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+	if (!outfmt) {
+		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+
+	if (!in->pix.width || !in->pix.height) {
+		in->pix.width = 640;
+		in->pix.height = 480;
+	}
+	if (!out->pix.width || !out->pix.height) {
+		out->pix.width = 640;
+		out->pix.height = 480;
+	}
+
+	/* image converter does not handle fields */
+	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.width / 4);
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.height / 4);
+	} else {
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.width / 4);
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.height / 4);
+	}
+
+	/* get tiling rows/cols from output format */
+	num_out_rows = ipu_ic_num_stripes(out->pix.height);
+	num_out_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		num_in_rows = num_out_cols;
+		num_in_cols = num_out_rows;
+	} else {
+		num_in_rows = num_out_rows;
+		num_in_cols = num_out_cols;
+	}
+
+	/* align input width/height */
+	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
+			num_in_rows);
+	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
+	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
+
+	/* align output width/height */
+	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
+			num_out_rows);
+	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
+	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
+
+	/* set input/output strides and image sizes */
+	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
+	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
+	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
+	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
+
+/*
+ * this is used by ipu_image_convert_prepare() to verify set input and
+ * output images are valid before starting the conversion. Clients can
+ * also call it before calling ipu_image_convert_prepare().
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	struct ipu_image testin, testout;
+	int ret;
+
+	testin = *in;
+	testout = *out;
+
+	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
+	if (ret)
+		return ret;
+
+	if (testin.pix.width != in->pix.width ||
+	    testin.pix.height != in->pix.height ||
+	    testout.pix.width != out->pix.width ||
+	    testout.pix.height != out->pix.height)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
+
+/*
+ * Call ipu_image_convert_prepare() to prepare for the conversion of
+ * given images and rotation mode. Returns a new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct image_converter *cvt = &ic->cvt;
+	struct ipu_ic_image *s_image, *d_image;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+	bool get_res;
+	int ret;
+
+	if (!ic || !in || !out || !complete)
+		return ERR_PTR(-EINVAL);
+
+	/* verify the in/out images before continuing */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret) {
+		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
+			__func__);
+		return ERR_PTR(ret);
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
+
+	ctx->cvt = cvt;
+	init_completion(&ctx->aborted);
+
+	s_image = &ctx->in;
+	d_image = &ctx->out;
+
+	/* set tiling and rotation */
+	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
+	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		s_image->num_rows = d_image->num_cols;
+		s_image->num_cols = d_image->num_rows;
+	} else {
+		s_image->num_rows = d_image->num_rows;
+		s_image->num_cols = d_image->num_cols;
+	}
+
+	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
+	ctx->rot_mode = rot_mode;
+
+	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
+	if (ret)
+		goto out_free;
+	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
+	if (ret)
+		goto out_free;
+
+	ipu_ic_calc_out_tile_map(ctx);
+
+	ipu_ic_dump_format(ctx, s_image);
+	ipu_ic_dump_format(ctx, d_image);
+
+	ctx->complete = complete;
+	ctx->complete_context = complete_context;
+
+	/*
+	 * Can we use double-buffering for this operation? If there is
+	 * only one tile (the whole image can be converted in a single
+	 * operation) there's no point in using double-buffering. Also,
+	 * the IPU's IDMAC channels allow only a single U and V plane
+	 * offset shared between both buffers, but these offsets change
+	 * for every tile, and therefore would have to be updated for
+	 * each buffer which is not possible. So double-buffering is
+	 * impossible when either the source or destination images are
+	 * a planar format (YUV420, YUV422P, etc.).
+	 */
+	ctx->double_buffering = (ctx->num_tiles > 1 &&
+				 !s_image->fmt->y_depth &&
+				 !d_image->fmt->y_depth);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
+					   d_image->tile.size);
+		if (ret)
+			goto out_free;
+		if (ctx->double_buffering) {
+			ret = ipu_ic_alloc_dma_buf(priv,
+						   &ctx->rot_intermediate[1],
+						   d_image->tile.size);
+			if (ret)
+				goto out_free_dmabuf0;
+		}
+	}
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	get_res = list_empty(&cvt->ctx_list);
+
+	list_add_tail(&ctx->list, &cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (get_res) {
+		ret = ipu_ic_get_ipu_resources(cvt);
+		if (ret)
+			goto out_free_dmabuf1;
+	}
+
+	return ctx;
+
+out_free_dmabuf1:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	spin_lock_irqsave(&cvt->irqlock, flags);
+	list_del(&ctx->list);
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+out_free_dmabuf0:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+out_free:
+	kfree(ctx);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+/*
+ * Carry out a single image conversion. Only the physaddr's of the input
+ * and output image buffers are needed. The conversion context must have
+ * been created previously with ipu_image_convert_prepare(). Returns the
+ * new run object.
+ */
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+	int ret = 0;
+
+	run = kzalloc(sizeof(*run), GFP_KERNEL);
+	if (!run)
+		return ERR_PTR(-ENOMEM);
+
+	run->ctx = ctx;
+	run->in_phys = in_phys;
+	run->out_phys = out_phys;
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
+		ctx, run);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	if (ctx->aborting) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	list_add_tail(&run->list, &cvt->pending_q);
+
+	if (!cvt->current_run) {
+		ret = ipu_ic_run(run);
+		if (ret)
+			cvt->current_run = NULL;
+	}
+unlock:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (ret) {
+		kfree(run);
+		run = ERR_PTR(ret);
+	}
+
+	return run;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+/* Abort any active or pending conversions for this context */
+void ipu_image_convert_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *active_run, *tmp;
+	unsigned long flags;
+	int run_count, ret;
+	bool need_abort;
+
+	reinit_completion(&ctx->aborted);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* move all remaining pending runs in this context to done_q */
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		if (run->ctx != ctx)
+			continue;
+		run->status = -EIO;
+		list_move_tail(&run->list, &cvt->done_q);
+	}
+
+	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
+	active_run = (cvt->current_run && cvt->current_run->ctx == ctx) ?
+		cvt->current_run : NULL;
+
+	need_abort = (run_count || active_run);
+
+	ctx->aborting = need_abort;
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (!need_abort) {
+		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
+			__func__, ctx);
+		return;
+	}
+
+	dev_dbg(priv->ipu->dev,
+		 "%s: wait for completion: %d runs, active run %p\n",
+		 __func__, run_count, active_run);
+
+	ret = wait_for_completion_timeout(&ctx->aborted,
+					  msecs_to_jiffies(10000));
+	if (ret == 0) {
+		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
+		ipu_ic_force_abort(ctx);
+	}
+
+	ctx->aborting = false;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
+
+/* Unprepare image conversion context */
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	unsigned long flags;
+	bool put_res;
+
+	/* make sure no runs are hanging around */
+	ipu_image_convert_abort(ctx);
+
+	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	list_del(&ctx->list);
+
+	put_res = list_empty(&cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (put_res)
+		ipu_ic_release_ipu_resources(cvt);
+
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+
+	kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
+
+/*
+ * "Canned" asynchronous single image conversion. On successful return
+ * caller must call ipu_image_convert_unprepare() after conversion completes.
+ * Returns the new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context)
+{
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+
+	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
+					complete, complete_context);
+	if (IS_ERR(ctx))
+		return ctx;
+
+	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
+	if (IS_ERR(run)) {
+		ipu_image_convert_unprepare(ctx);
+		return ERR_PTR(PTR_ERR(run));
+	}
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert);
+
+/* "Canned" synchronous single image conversion */
+static void image_convert_sync_complete(void *data,
+					struct image_converter_run *run,
+					int err)
+{
+	struct completion *comp = data;
+
+	complete(comp);
+}
+
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode)
+{
+	struct image_converter_ctx *ctx;
+	struct completion comp;
+	int ret;
+
+	init_completion(&comp);
+
+	ctx = ipu_image_convert(ic, in, out, rot_mode,
+				image_convert_sync_complete, &comp);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
+	ret = (ret == 0) ? -ETIMEDOUT : 0;
+
+	ipu_image_convert_unprepare(ctx);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -746,6 +2415,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	ipu->ic_priv = priv;
 
 	spin_lock_init(&priv->lock);
+
 	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
 	if (!priv->base)
 		return -ENOMEM;
@@ -758,10 +2428,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
-		priv->task[i].task = i;
-		priv->task[i].priv = priv;
-		priv->task[i].reg = &ic_task_reg[i];
-		priv->task[i].bit = &ic_task_bit[i];
+		struct ipu_ic *ic = &priv->task[i];
+		struct image_converter *cvt = &ic->cvt;
+
+		ic->task = i;
+		ic->priv = priv;
+		ic->reg = &ic_task_reg[i];
+		ic->bit = &ic_task_bit[i];
+		ic->ch = &ic_task_ch[i];
+
+		cvt->ic = ic;
+		spin_lock_init(&cvt->irqlock);
+		INIT_LIST_HEAD(&cvt->ctx_list);
+		INIT_LIST_HEAD(&cvt->pending_q);
+		INIT_LIST_HEAD(&cvt->done_q);
+		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
 	}
 
 	return 0;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index afbc89d..25c40b3 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -63,17 +63,25 @@ enum ipu_csi_dest {
 /*
  * Enumeration of IPU rotation modes
  */
+#define IPU_ROT_BIT_VFLIP (1 << 0)
+#define IPU_ROT_BIT_HFLIP (1 << 1)
+#define IPU_ROT_BIT_90    (1 << 2)
+
 enum ipu_rotate_mode {
 	IPU_ROTATE_NONE = 0,
-	IPU_ROTATE_VERT_FLIP,
-	IPU_ROTATE_HORIZ_FLIP,
-	IPU_ROTATE_180,
-	IPU_ROTATE_90_RIGHT,
-	IPU_ROTATE_90_RIGHT_VFLIP,
-	IPU_ROTATE_90_RIGHT_HFLIP,
-	IPU_ROTATE_90_LEFT,
+	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
+	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
+	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
+	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
+	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
+			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
 };
 
+/* 90-degree rotations require the IRT unit */
+#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
+
 enum ipu_color_space {
 	IPUV3_COLORSPACE_RGB,
 	IPUV3_COLORSPACE_YUV,
@@ -316,6 +324,7 @@ enum ipu_ic_task {
 };
 
 struct ipu_ic;
+
 int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
@@ -330,6 +339,40 @@ void ipu_ic_task_disable(struct ipu_ic *ic);
 int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
+
+struct image_converter_ctx;
+struct image_converter_run;
+
+typedef void (*image_converter_cb_t)(void *ctx,
+				     struct image_converter_run *run,
+				     int err);
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context);
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys);
+void ipu_image_convert_abort(struct image_converter_ctx *ctx);
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context);
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode);
+
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

This patch implements complete image conversion support to ipu-ic,
with tiling to support scaling to and from images up to 4096x4096.
Image rotation is also supported.

The internal API is subsystem agnostic (no V4L2 dependency except
for the use of V4L2 fourcc pixel formats).

Callers prepare for image conversion by calling
ipu_image_convert_prepare(), which initializes the parameters of
the conversion. The caller passes in the ipu_ic task to use for
the conversion, the input and output image formats, a rotation mode,
and a completion callback and completion context pointer:

struct image_converter_ctx *
ipu_image_convert_prepare(struct ipu_ic *ic,
                          struct ipu_image *in, struct ipu_image *out,
                          enum ipu_rotate_mode rot_mode,
                          image_converter_cb_t complete,
                          void *complete_context);

The caller is given a new conversion context that must be passed to
the further APIs:

struct image_converter_run *
ipu_image_convert_run(struct image_converter_ctx *ctx,
                      dma_addr_t in_phys, dma_addr_t out_phys);

This queues a new image conversion request to a run queue, and
starts the conversion immediately if the run queue is empty. Only
the physaddr's of the input and output image buffers are needed,
since the conversion context was created previously with
ipu_image_convert_prepare(). Returns a new run object pointer. When
the conversion completes, the run pointer is returned to the
completion callback.

void image_convert_abort(struct image_converter_ctx *ctx);

This will abort any active or pending conversions for this context.
Any currently active or pending runs belonging to this context are
returned via the completion callback with an error status.

void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);

Unprepares the conversion context. Any active or pending runs will
be aborted by calling image_convert_abort().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |   57 +-
 2 files changed, 1736 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1a37afc..5471b72 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -17,6 +17,8 @@
 #include <linux/bitrev.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
 #include "ipu-prv.h"
 
 /* IC Register Offsets */
@@ -82,6 +84,40 @@
 #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
 #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
 
+/*
+ * The IC Resizer has a restriction that the output frame from the
+ * resizer must be 1024 or less in both width (pixels) and height
+ * (lines).
+ *
+ * The image conversion support attempts to split up a conversion when
+ * the desired output (converted) frame resolution exceeds the IC resizer
+ * limit of 1024 in either dimension.
+ *
+ * If either dimension of the output frame exceeds the limit, the
+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum
+ * of 4*4 or 16 tiles. A conversion is then carried out for each
+ * tile (but taking care to pass the full frame stride length to
+ * the DMA channel's parameter memory!). IDMA double-buffering is used
+ * to convert each tile back-to-back when possible (see note below
+ * when double_buffering boolean is set).
+ *
+ * Note that the input frame must be split up into the same number
+ * of tiles as the output frame.
+ */
+#define MAX_STRIPES_W    4
+#define MAX_STRIPES_H    4
+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
+
+#define MIN_W     128
+#define MIN_H     128
+#define MAX_W     4096
+#define MAX_H     4096
+
+enum image_convert_type {
+	IMAGE_CONVERT_IN = 0,
+	IMAGE_CONVERT_OUT,
+};
+
 struct ic_task_regoffs {
 	u32 rsc;
 	u32 tpmem_csc[2];
@@ -96,6 +132,16 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	int in;
+	int out;
+	int rot_in;
+	int rot_out;
+	int vdi_in_p;
+	int vdi_in;
+	int vdi_in_n;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
 	},
 };
 
+static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
+	[IC_TASK_ENCODER] = {
+		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
+		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
+	},
+	[IC_TASK_VIEWFINDER] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV,
+		.vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR,
+		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT,
+	},
+	[IC_TASK_POST_PROCESSOR] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PP,
+		.out = IPUV3_CHANNEL_IC_PP_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
+		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
+	},
+};
+
+struct ipu_ic_dma_buf {
+	void          *virt;
+	dma_addr_t    phys;
+	unsigned long len;
+};
+
+/* dimensions of one tile */
+struct ipu_ic_tile {
+	unsigned int width;
+	unsigned int height;
+	/* size and strides are in bytes */
+	unsigned int size;
+	unsigned int stride;
+	unsigned int rot_stride;
+};
+
+struct ipu_ic_tile_off {
+	/* start Y or packed offset of this tile */
+	u32     offset;
+	/* offset from start to tile in U plane, for planar formats */
+	u32     u_off;
+	/* offset from start to tile in V plane, for planar formats */
+	u32     v_off;
+};
+
+struct ipu_ic_pixfmt {
+	char	*name;
+	u32	fourcc;        /* V4L2 fourcc */
+	int     bpp;           /* total bpp */
+	int     y_depth;       /* depth of Y plane for planar formats */
+	int     uv_width_dec;  /* decimation in width for U/V planes */
+	int     uv_height_dec; /* decimation in height for U/V planes */
+	bool    uv_swapped;    /* U and V planes are swapped */
+	bool    uv_packed;     /* partial planar (U and V in same plane) */
+};
+
+struct ipu_ic_image {
+	struct ipu_image base;
+	enum image_convert_type type;
+
+	const struct ipu_ic_pixfmt *fmt;
+	unsigned int stride;
+
+	/* # of rows (horizontal stripes) if dest height is > 1024 */
+	unsigned int num_rows;
+	/* # of columns (vertical stripes) if dest width is > 1024 */
+	unsigned int num_cols;
+
+	struct ipu_ic_tile tile;
+	struct ipu_ic_tile_off tile_off[MAX_TILES];
+};
+
+struct image_converter_ctx;
+struct image_converter;
 struct ipu_ic_priv;
+struct ipu_ic;
+
+struct image_converter_run {
+	struct image_converter_ctx *ctx;
+
+	dma_addr_t in_phys;
+	dma_addr_t out_phys;
+
+	int status;
+
+	struct list_head list;
+};
+
+struct image_converter_ctx {
+	struct image_converter *cvt;
+
+	image_converter_cb_t complete;
+	void *complete_context;
+
+	/* Source/destination image data and rotation mode */
+	struct ipu_ic_image in;
+	struct ipu_ic_image out;
+	enum ipu_rotate_mode rot_mode;
+
+	/* intermediate buffer for rotation */
+	struct ipu_ic_dma_buf rot_intermediate[2];
+
+	/* current buffer number for double buffering */
+	int cur_buf_num;
+
+	bool aborting;
+	struct completion aborted;
+
+	/* can we use double-buffering for this conversion operation? */
+	bool double_buffering;
+	/* num_rows * num_cols */
+	unsigned int num_tiles;
+	/* next tile to process */
+	unsigned int next_tile;
+	/* where to place converted tile in dest image */
+	unsigned int out_tile_map[MAX_TILES];
+
+	struct list_head list;
+};
+
+struct image_converter {
+	struct ipu_ic *ic;
+
+	struct ipuv3_channel *in_chan;
+	struct ipuv3_channel *out_chan;
+	struct ipuv3_channel *rotation_in_chan;
+	struct ipuv3_channel *rotation_out_chan;
+
+	/* the IPU end-of-frame irqs */
+	int out_eof_irq;
+	int rot_out_eof_irq;
+
+	spinlock_t irqlock;
+
+	/* list of convert contexts */
+	struct list_head ctx_list;
+	/* queue of conversion runs */
+	struct list_head pending_q;
+	/* queue of completed runs */
+	struct list_head done_q;
+
+	/* the current conversion run */
+	struct image_converter_run *current_run;
+};
 
 struct ipu_ic {
 	enum ipu_ic_task task;
 	const struct ic_task_regoffs *reg;
 	const struct ic_task_bitfields *bit;
+	const struct ic_task_channels *ch;
 
 	enum ipu_color_space in_cs, g_in_cs;
 	enum ipu_color_space out_cs;
@@ -151,6 +344,8 @@ struct ipu_ic {
 	bool rotation;
 	bool in_use;
 
+	struct image_converter cvt;
+
 	struct ipu_ic_priv *priv;
 };
 
@@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
 	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
 
-	if (rot >= IPU_ROTATE_90_RIGHT)
+	if (ipu_rot_mode_is_irt(rot))
 		ic->rotation = true;
 
 unlock:
@@ -648,6 +843,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
 	}
 }
 
+/*
+ * Complete image conversion support follows
+ */
+
+static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.bpp    = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+	}, {
+		.name	= "4:2:0 planar, YVU",
+		.fourcc	= V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_swapped = true,
+	}, {
+		.name   = "4:2:0 partial planar, NV12",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_packed = true,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name   = "4:2:2 partial planar, NV16",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+		.uv_packed = true,
+	},
+};
+
+static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
+{
+	const struct ipu_ic_pixfmt *ret = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
+		if (ipu_ic_formats[i].fourcc = fourcc) {
+			ret = &ipu_ic_formats[i];
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
+			       struct ipu_ic_image *ic_image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev,
+		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
+		ctx,
+		ic_image->type = IMAGE_CONVERT_OUT ? "Output" : "Input",
+		ic_image->base.pix.width, ic_image->base.pix.height,
+		ic_image->num_cols, ic_image->num_rows,
+		ic_image->tile.width, ic_image->tile.height,
+		ic_image->fmt->fourcc & 0xff,
+		(ic_image->fmt->fourcc >> 8) & 0xff,
+		(ic_image->fmt->fourcc >> 16) & 0xff,
+		(ic_image->fmt->fourcc >> 24) & 0xff);
+}
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
+{
+	const struct ipu_ic_pixfmt *fmt;
+
+	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
+		return -EINVAL;
+
+	/* Format found */
+	fmt = &ipu_ic_formats[index];
+	*desc = fmt->name;
+	*fourcc = fmt->fourcc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
+
+static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf)
+{
+	if (buf->virt)
+		dma_free_coherent(priv->ipu->dev,
+				  buf->len, buf->virt, buf->phys);
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf,
+				int size)
+{
+	unsigned long newlen = PAGE_ALIGN(size);
+
+	if (buf->virt) {
+		if (buf->len = newlen)
+			return 0;
+		ipu_ic_free_dma_buf(priv, buf);
+	}
+
+	buf->len = newlen;
+	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline int ipu_ic_num_stripes(int dim)
+{
+	if (dim <= 1024)
+		return 1;
+	else if (dim <= 2048)
+		return 2;
+	else
+		return 4;
+}
+
+static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
+					struct ipu_ic_image *image)
+{
+	struct ipu_ic_tile *tile = &image->tile;
+
+	tile->height = image->base.pix.height / image->num_rows;
+	tile->width = image->base.pix.width / image->num_cols;
+	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
+
+	if (image->fmt->y_depth) {
+		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
+	} else {
+		tile->stride = (image->fmt->bpp * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
+	}
+}
+
+/*
+ * Use the rotation transformation to find the tile coordinates
+ * (row, col) of a tile in the destination frame that corresponds
+ * to the given tile coordinates of a source frame. The destination
+ * coordinate is then converted to a tile index.
+ */
+static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
+				       int src_row, int src_col)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	int cos, sin, dst_row, dst_col;
+
+	/* with no rotation it's a 1:1 mapping */
+	if (ctx->rot_mode = IPU_ROTATE_NONE)
+		return src_row * s_image->num_cols + src_col;
+
+	if (ctx->rot_mode & IPU_ROT_BIT_90) {
+		cos = 0;
+		sin = 1;
+	} else {
+		cos = 1;
+		sin = 0;
+	}
+
+	/*
+	 * before doing the transform, first we have to translate
+	 * source row,col for an origin in the center of s_image
+	 */
+	src_row *= 2;
+	src_col *= 2;
+	src_row -= s_image->num_rows - 1;
+	src_col -= s_image->num_cols - 1;
+
+	/* do the rotation transform */
+	dst_col = src_col * cos - src_row * sin;
+	dst_row = src_col * sin + src_row * cos;
+
+	/* apply flip */
+	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
+		dst_col = -dst_col;
+	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
+		dst_row = -dst_row;
+
+	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
+		ctx, src_col, src_row, dst_col, dst_row);
+
+	/*
+	 * finally translate dest row,col using an origin in upper
+	 * left of d_image
+	 */
+	dst_row += d_image->num_rows - 1;
+	dst_col += d_image->num_cols - 1;
+	dst_row /= 2;
+	dst_col /= 2;
+
+	return dst_row * d_image->num_cols + dst_col;
+}
+
+/*
+ * Fill the out_tile_map[] with transformed destination tile indeces.
+ */
+static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
+{
+	struct ipu_ic_image *s_image = &ctx->in;
+	unsigned int row, col, tile = 0;
+
+	for (row = 0; row < s_image->num_rows; row++) {
+		for (col = 0; col < s_image->num_cols; col++) {
+			ctx->out_tile_map[tile] +				ipu_ic_transform_tile_index(ctx, row, col);
+			tile++;
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 H, w, h, y_depth, y_stride, uv_stride;
+	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
+	u32 y_row_off, y_col_off, y_off;
+	u32 y_size, uv_size;
+
+	/* setup some convenience vars */
+	H = image->base.pix.height;
+	w = image->tile.width;
+	h = image->tile.height;
+
+	y_depth = fmt->y_depth;
+	y_stride = image->stride;
+	uv_stride = y_stride / fmt->uv_width_dec;
+	if (fmt->uv_packed)
+		uv_stride *= 2;
+
+	y_size = H * y_stride;
+	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
+
+	for (row = 0; row < image->num_rows; row++) {
+		y_row_off = row * h * y_stride;
+		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
+
+		for (col = 0; col < image->num_cols; col++) {
+			y_col_off = (col * w * y_depth) >> 3;
+			uv_col_off = y_col_off / fmt->uv_width_dec;
+			if (fmt->uv_packed)
+				uv_col_off *= 2;
+
+			y_off = y_row_off + y_col_off;
+			uv_off = uv_row_off + uv_col_off;
+
+			u_off = y_size - y_off + uv_off;
+			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
+			if (fmt->uv_swapped) {
+				tmp = u_off;
+				u_off = v_off;
+				v_off = tmp;
+			}
+
+			image->tile_off[tile].offset = y_off;
+			image->tile_off[tile].u_off = u_off;
+			image->tile_off[tile++].v_off = v_off;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
+				ctx, image->type = IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				y_off, u_off, v_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 w, h, bpp, stride;
+	u32 row_off, col_off;
+
+	/* setup some convenience vars */
+	w = image->tile.width;
+	h = image->tile.height;
+	stride = image->stride;
+	bpp = fmt->bpp;
+
+	for (row = 0; row < image->num_rows; row++) {
+		row_off = row * h * stride;
+
+		for (col = 0; col < image->num_cols; col++) {
+			col_off = (col * w * bpp) >> 3;
+
+			image->tile_off[tile].offset = row_off + col_off;
+			image->tile_off[tile].u_off = 0;
+			image->tile_off[tile++].v_off = 0;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
+				image->type = IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				row_off + col_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
+				     struct ipu_ic_image *image)
+{
+	memset(image->tile_off, 0, sizeof(image->tile_off));
+
+	if (image->fmt->y_depth)
+		ipu_ic_calc_tile_offsets_planar(ctx, image);
+	else
+		ipu_ic_calc_tile_offsets_packed(ctx, image);
+}
+
+/*
+ * return the number of runs in given queue (pending_q or done_q)
+ * for this context. hold irqlock when calling.
+ */
+static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
+				struct list_head *q)
+{
+	struct image_converter_run *run;
+	int count = 0;
+
+	list_for_each_entry(run, q, list) {
+		if (run->ctx = ctx)
+			count++;
+	}
+
+	return count;
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_convert_stop(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
+		__func__, ctx, run);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(cvt->ic);
+	ipu_idmac_disable_channel(cvt->in_chan);
+	ipu_idmac_disable_channel(cvt->out_chan);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_disable_channel(cvt->rotation_in_chan);
+		ipu_idmac_disable_channel(cvt->rotation_out_chan);
+		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
+	}
+
+	ipu_ic_disable(cvt->ic);
+}
+
+/* hold irqlock when calling */
+static void init_idmac_channel(struct image_converter_ctx *ctx,
+			       struct ipuv3_channel *channel,
+			       struct ipu_ic_image *image,
+			       enum ipu_rotate_mode rot_mode,
+			       bool rot_swap_width_height)
+{
+	struct image_converter *cvt = ctx->cvt;
+	unsigned int burst_size;
+	u32 width, height, stride;
+	dma_addr_t addr0, addr1 = 0;
+	struct ipu_image tile_image;
+	unsigned int tile_idx[2];
+
+	if (image->type = IMAGE_CONVERT_OUT) {
+		tile_idx[0] = ctx->out_tile_map[0];
+		tile_idx[1] = ctx->out_tile_map[1];
+	} else {
+		tile_idx[0] = 0;
+		tile_idx[1] = 1;
+	}
+
+	if (rot_swap_width_height) {
+		width = image->tile.height;
+		height = image->tile.width;
+		stride = image->tile.rot_stride;
+		addr0 = ctx->rot_intermediate[0].phys;
+		if (ctx->double_buffering)
+			addr1 = ctx->rot_intermediate[1].phys;
+	} else {
+		width = image->tile.width;
+		height = image->tile.height;
+		stride = image->stride;
+		addr0 = image->base.phys0 +
+			image->tile_off[tile_idx[0]].offset;
+		if (ctx->double_buffering)
+			addr1 = image->base.phys0 +
+				image->tile_off[tile_idx[1]].offset;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&tile_image, 0, sizeof(tile_image));
+	tile_image.pix.width = tile_image.rect.width = width;
+	tile_image.pix.height = tile_image.rect.height = height;
+	tile_image.pix.bytesperline = stride;
+	tile_image.pix.pixelformat =  image->fmt->fourcc;
+	tile_image.phys0 = addr0;
+	tile_image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &tile_image);
+
+	if (image->fmt->y_depth && !rot_swap_width_height)
+		ipu_cpmem_set_uv_offset(channel,
+					image->tile_off[tile_idx[0]].u_off,
+					image->tile_off[tile_idx[0]].v_off);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (channel = cvt->rotation_in_chan ||
+	    channel = cvt->rotation_out_chan) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else
+		burst_size = (width % 16) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
+			      burst_size, rot_mode);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_convert_start(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	enum ipu_color_space src_cs, dest_cs;
+	unsigned int dest_width, dest_height;
+	int ret;
+
+	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
+		__func__, ctx, run);
+
+	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
+	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* swap width/height for resizer */
+		dest_width = d_image->tile.height;
+		dest_height = d_image->tile.width;
+	} else {
+		dest_width = d_image->tile.width;
+		dest_height = d_image->tile.height;
+	}
+
+	/* setup the IC resizer and CSC */
+	ret = ipu_ic_task_init(cvt->ic,
+			       s_image->tile.width,
+			       s_image->tile.height,
+			       dest_width,
+			       dest_height,
+			       src_cs, dest_cs);
+	if (ret) {
+		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
+		return ret;
+	}
+
+	/* init the source MEM-->IC PP IDMAC channel */
+	init_idmac_channel(ctx, cvt->in_chan, s_image,
+			   IPU_ROTATE_NONE, false);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* init the IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   IPU_ROTATE_NONE, true);
+
+		/* init the MEM-->IC PP ROT IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
+				   ctx->rot_mode, true);
+
+		/* init the destination IC PP ROT-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
+				   IPU_ROTATE_NONE, false);
+
+		/* now link IC PP-->MEM to MEM-->IC PP ROT */
+		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
+	} else {
+		/* init the destination IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   ctx->rot_mode, false);
+	}
+
+	/* enable the IC */
+	ipu_ic_enable(cvt->ic);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(cvt->in_chan, 0);
+	ipu_idmac_select_buffer(cvt->out_chan, 0);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode))
+		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
+	if (ctx->double_buffering) {
+		ipu_idmac_select_buffer(cvt->in_chan, 1);
+		ipu_idmac_select_buffer(cvt->out_chan, 1);
+		if (ipu_rot_mode_is_irt(ctx->rot_mode))
+			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
+	}
+
+	/* enable the channels! */
+	ipu_idmac_enable_channel(cvt->in_chan);
+	ipu_idmac_enable_channel(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_enable_channel(cvt->rotation_in_chan);
+		ipu_idmac_enable_channel(cvt->rotation_out_chan);
+	}
+
+	ipu_ic_task_enable(cvt->ic);
+
+	ipu_cpmem_dump(cvt->in_chan);
+	ipu_cpmem_dump(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_cpmem_dump(cvt->rotation_in_chan);
+		ipu_cpmem_dump(cvt->rotation_out_chan);
+	}
+
+	ipu_dump(priv->ipu);
+
+	return 0;
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_run(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+
+	ctx->in.base.phys0 = run->in_phys;
+	ctx->out.base.phys0 = run->out_phys;
+
+	ctx->cur_buf_num = 0;
+	ctx->next_tile = 1;
+
+	/* remove run from pending_q and set as current */
+	list_del(&run->list);
+	cvt->current_run = run;
+
+	return ipu_ic_convert_start(run);
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_run_next(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		/* skip contexts that are aborting */
+		if (run->ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: skipping aborting ctx %p run %p\n",
+				 __func__, run->ctx, run);
+			continue;
+		}
+
+		ret = ipu_ic_run(run);
+		if (!ret)
+			break;
+
+		/*
+		 * something went wrong with start, add the run
+		 * to done q and continue to the next run in the
+		 * pending q.
+		 */
+		run->status = ret;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+	}
+}
+
+static void ipu_ic_empty_done_q(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	while (!list_empty(&cvt->done_q)) {
+		run = list_entry(cvt->done_q.next,
+				 struct image_converter_run,
+				 list);
+
+		list_del(&run->list);
+
+		dev_dbg(priv->ipu->dev,
+			"%s: completing ctx %p run %p with %d\n",
+			__func__, run->ctx, run, run->status);
+
+		/* call the completion callback and free the run */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		run->ctx->complete(run->ctx->complete_context, run,
+				   run->status);
+		kfree(run);
+		spin_lock_irqsave(&cvt->irqlock, flags);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+}
+
+/*
+ * the bottom half thread clears out the done_q, calling the
+ * completion handler for each.
+ */
+static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
+{
+	struct image_converter *cvt = dev_id;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+
+	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
+
+	ipu_ic_empty_done_q(cvt);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/*
+	 * the done_q is cleared out, signal any contexts
+	 * that are aborting that abort can complete.
+	 */
+	list_for_each_entry(ctx, &cvt->ctx_list, list) {
+		if (ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: signaling abort for ctx %p\n",
+				 __func__, ctx);
+			complete(&ctx->aborted);
+		}
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
+	return IRQ_HANDLED;
+}
+
+/* hold irqlock when calling */
+static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_tile_off *src_off, *dst_off;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	struct ipuv3_channel *outch;
+	unsigned int dst_idx;
+
+	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
+		cvt->rotation_out_chan : cvt->out_chan;
+
+	/*
+	 * It is difficult to stop the channel DMA before the channels
+	 * enter the paused state. Without double-buffering the channels
+	 * are always in a paused state when the EOF irq occurs, so it
+	 * is safe to stop the channels now. For double-buffering we
+	 * just ignore the abort until the operation completes, when it
+	 * is safe to shut down.
+	 */
+	if (ctx->aborting && !ctx->double_buffering) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		goto done;
+	}
+
+	if (ctx->next_tile = ctx->num_tiles) {
+		/*
+		 * the conversion is complete
+		 */
+		ipu_ic_convert_stop(run);
+		run->status = 0;
+		goto done;
+	}
+
+	/*
+	 * not done, place the next tile buffers.
+	 */
+	if (!ctx->double_buffering) {
+
+		src_off = &s_image->tile_off[ctx->next_tile];
+		dst_idx = ctx->out_tile_map[ctx->next_tile];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, 0,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, 0,
+				     d_image->base.phys0 + dst_off->offset);
+		if (s_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(cvt->in_chan,
+						src_off->u_off,
+						src_off->v_off);
+		if (d_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(outch,
+						dst_off->u_off,
+						dst_off->v_off);
+
+		ipu_idmac_select_buffer(cvt->in_chan, 0);
+		ipu_idmac_select_buffer(outch, 0);
+
+	} else if (ctx->next_tile < ctx->num_tiles - 1) {
+
+		src_off = &s_image->tile_off[ctx->next_tile + 1];
+		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
+				     d_image->base.phys0 + dst_off->offset);
+
+		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
+		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
+
+		ctx->cur_buf_num ^= 1;
+	}
+
+	ctx->next_tile++;
+	return IRQ_HANDLED;
+done:
+	list_add_tail(&run->list, &cvt->done_q);
+	cvt->current_run = NULL;
+	ipu_ic_run_next(cvt);
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this is a rotation operation, just ignore */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this was NOT a rotation operation, shouldn't happen */
+		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+/*
+ * try to force the completion of runs for this ctx. Called when
+ * abort wait times out in ipu_image_convert_abort().
+ */
+static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	run = cvt->current_run;
+	if (run && run->ctx = ctx) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+		ipu_ic_run_next(cvt);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	ipu_ic_empty_done_q(cvt);
+}
+
+static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
+{
+	if (cvt->out_eof_irq >= 0)
+		free_irq(cvt->out_eof_irq, cvt);
+	if (cvt->rot_out_eof_irq >= 0)
+		free_irq(cvt->rot_out_eof_irq, cvt);
+
+	if (!IS_ERR_OR_NULL(cvt->in_chan))
+		ipu_idmac_put(cvt->in_chan);
+	if (!IS_ERR_OR_NULL(cvt->out_chan))
+		ipu_idmac_put(cvt->out_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
+		ipu_idmac_put(cvt->rotation_in_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
+		ipu_idmac_put(cvt->rotation_out_chan);
+
+	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan +		cvt->rotation_out_chan = NULL;
+	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
+}
+
+static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
+{
+	const struct ic_task_channels *chan = cvt->ic->ch;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	int ret;
+
+	/* get IDMAC channels */
+	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
+	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
+	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
+		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
+	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
+	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
+		dev_err(priv->ipu->dev,
+			"could not acquire idmac rotation channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* acquire the EOF interrupts */
+	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						cvt->out_chan,
+						IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->out_eof_irq,
+				   ipu_ic_norotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			 cvt->out_eof_irq);
+		cvt->out_eof_irq = -1;
+		goto err;
+	}
+
+	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						     cvt->rotation_out_chan,
+						     IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->rot_out_eof_irq,
+				   ipu_ic_rotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			cvt->rot_out_eof_irq);
+		cvt->rot_out_eof_irq = -1;
+		goto err;
+	}
+
+	return 0;
+err:
+	ipu_ic_release_ipu_resources(cvt);
+	return ret;
+}
+
+static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
+			     struct ipu_ic_image *ic_image,
+			     struct ipu_image *image,
+			     enum image_convert_type type)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	ic_image->base = *image;
+	ic_image->type = type;
+
+	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
+	if (!ic_image->fmt) {
+		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
+			type = IMAGE_CONVERT_OUT ? "Output" : "Input");
+		return -EINVAL;
+	}
+
+	if (ic_image->fmt->y_depth)
+		ic_image->stride = (ic_image->fmt->y_depth *
+				    ic_image->base.pix.width) >> 3;
+	else
+		ic_image->stride  = ic_image->base.pix.bytesperline;
+
+	ipu_ic_calc_tile_dimensions(ctx, ic_image);
+	ipu_ic_calc_tile_offsets(ctx, ic_image);
+
+	return 0;
+}
+
+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+				unsigned int max, unsigned int align)
+{
+	/* Bits that must be zero to be aligned */
+	unsigned int mask = ~((1 << align) - 1);
+
+	/* Clamp to aligned min and max */
+	x = clamp(x, (min + ~mask) & mask, max & mask);
+
+	/* Round to nearest aligned value */
+	if (align)
+		x = (x + (1 << (align - 1))) & mask;
+
+	return x;
+}
+
+/*
+ * We have to adjust the tile width such that the tile physaddrs and
+ * U and V plane offsets are multiples of 8 bytes as required by
+ * the IPU DMA Controller. For the planar formats, this corresponds
+ * to a pixel alignment of 16 (but use a more formal equation since
+ * the variables are available). For all the packed formats, 8 is
+ * good enough.
+ */
+static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
+{
+	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
+}
+
+/*
+ * For tile height alignment, we have to ensure that the output tile
+ * heights are multiples of 8 lines if the IRT is required by the
+ * given rotation mode (the IRT performs rotations on 8x8 blocks
+ * at a time). If the IRT is not used, or for input image tiles,
+ * 2 lines are good enough.
+ */
+static inline u32 tile_height_align(enum image_convert_type type,
+				    enum ipu_rotate_mode rot_mode)
+{
+	return (type = IMAGE_CONVERT_OUT &&
+		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
+}
+
+/* Adjusts input/output images to IPU restrictions */
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	const struct ipu_ic_pixfmt *infmt, *outfmt;
+	unsigned int num_in_rows, num_in_cols;
+	unsigned int num_out_rows, num_out_cols;
+	u32 w_align, h_align;
+
+	infmt = ipu_ic_get_format(in->pix.pixelformat);
+	outfmt = ipu_ic_get_format(out->pix.pixelformat);
+
+	/* set some defaults if needed */
+	if (!infmt) {
+		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+	if (!outfmt) {
+		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+
+	if (!in->pix.width || !in->pix.height) {
+		in->pix.width = 640;
+		in->pix.height = 480;
+	}
+	if (!out->pix.width || !out->pix.height) {
+		out->pix.width = 640;
+		out->pix.height = 480;
+	}
+
+	/* image converter does not handle fields */
+	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.width / 4);
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.height / 4);
+	} else {
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.width / 4);
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.height / 4);
+	}
+
+	/* get tiling rows/cols from output format */
+	num_out_rows = ipu_ic_num_stripes(out->pix.height);
+	num_out_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		num_in_rows = num_out_cols;
+		num_in_cols = num_out_rows;
+	} else {
+		num_in_rows = num_out_rows;
+		num_in_cols = num_out_cols;
+	}
+
+	/* align input width/height */
+	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
+			num_in_rows);
+	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
+	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
+
+	/* align output width/height */
+	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
+			num_out_rows);
+	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
+	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
+
+	/* set input/output strides and image sizes */
+	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
+	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
+	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
+	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
+
+/*
+ * this is used by ipu_image_convert_prepare() to verify set input and
+ * output images are valid before starting the conversion. Clients can
+ * also call it before calling ipu_image_convert_prepare().
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	struct ipu_image testin, testout;
+	int ret;
+
+	testin = *in;
+	testout = *out;
+
+	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
+	if (ret)
+		return ret;
+
+	if (testin.pix.width != in->pix.width ||
+	    testin.pix.height != in->pix.height ||
+	    testout.pix.width != out->pix.width ||
+	    testout.pix.height != out->pix.height)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
+
+/*
+ * Call ipu_image_convert_prepare() to prepare for the conversion of
+ * given images and rotation mode. Returns a new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct image_converter *cvt = &ic->cvt;
+	struct ipu_ic_image *s_image, *d_image;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+	bool get_res;
+	int ret;
+
+	if (!ic || !in || !out || !complete)
+		return ERR_PTR(-EINVAL);
+
+	/* verify the in/out images before continuing */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret) {
+		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
+			__func__);
+		return ERR_PTR(ret);
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
+
+	ctx->cvt = cvt;
+	init_completion(&ctx->aborted);
+
+	s_image = &ctx->in;
+	d_image = &ctx->out;
+
+	/* set tiling and rotation */
+	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
+	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		s_image->num_rows = d_image->num_cols;
+		s_image->num_cols = d_image->num_rows;
+	} else {
+		s_image->num_rows = d_image->num_rows;
+		s_image->num_cols = d_image->num_cols;
+	}
+
+	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
+	ctx->rot_mode = rot_mode;
+
+	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
+	if (ret)
+		goto out_free;
+	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
+	if (ret)
+		goto out_free;
+
+	ipu_ic_calc_out_tile_map(ctx);
+
+	ipu_ic_dump_format(ctx, s_image);
+	ipu_ic_dump_format(ctx, d_image);
+
+	ctx->complete = complete;
+	ctx->complete_context = complete_context;
+
+	/*
+	 * Can we use double-buffering for this operation? If there is
+	 * only one tile (the whole image can be converted in a single
+	 * operation) there's no point in using double-buffering. Also,
+	 * the IPU's IDMAC channels allow only a single U and V plane
+	 * offset shared between both buffers, but these offsets change
+	 * for every tile, and therefore would have to be updated for
+	 * each buffer which is not possible. So double-buffering is
+	 * impossible when either the source or destination images are
+	 * a planar format (YUV420, YUV422P, etc.).
+	 */
+	ctx->double_buffering = (ctx->num_tiles > 1 &&
+				 !s_image->fmt->y_depth &&
+				 !d_image->fmt->y_depth);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
+					   d_image->tile.size);
+		if (ret)
+			goto out_free;
+		if (ctx->double_buffering) {
+			ret = ipu_ic_alloc_dma_buf(priv,
+						   &ctx->rot_intermediate[1],
+						   d_image->tile.size);
+			if (ret)
+				goto out_free_dmabuf0;
+		}
+	}
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	get_res = list_empty(&cvt->ctx_list);
+
+	list_add_tail(&ctx->list, &cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (get_res) {
+		ret = ipu_ic_get_ipu_resources(cvt);
+		if (ret)
+			goto out_free_dmabuf1;
+	}
+
+	return ctx;
+
+out_free_dmabuf1:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	spin_lock_irqsave(&cvt->irqlock, flags);
+	list_del(&ctx->list);
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+out_free_dmabuf0:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+out_free:
+	kfree(ctx);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+/*
+ * Carry out a single image conversion. Only the physaddr's of the input
+ * and output image buffers are needed. The conversion context must have
+ * been created previously with ipu_image_convert_prepare(). Returns the
+ * new run object.
+ */
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+	int ret = 0;
+
+	run = kzalloc(sizeof(*run), GFP_KERNEL);
+	if (!run)
+		return ERR_PTR(-ENOMEM);
+
+	run->ctx = ctx;
+	run->in_phys = in_phys;
+	run->out_phys = out_phys;
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
+		ctx, run);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	if (ctx->aborting) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	list_add_tail(&run->list, &cvt->pending_q);
+
+	if (!cvt->current_run) {
+		ret = ipu_ic_run(run);
+		if (ret)
+			cvt->current_run = NULL;
+	}
+unlock:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (ret) {
+		kfree(run);
+		run = ERR_PTR(ret);
+	}
+
+	return run;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+/* Abort any active or pending conversions for this context */
+void ipu_image_convert_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *active_run, *tmp;
+	unsigned long flags;
+	int run_count, ret;
+	bool need_abort;
+
+	reinit_completion(&ctx->aborted);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* move all remaining pending runs in this context to done_q */
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		if (run->ctx != ctx)
+			continue;
+		run->status = -EIO;
+		list_move_tail(&run->list, &cvt->done_q);
+	}
+
+	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
+	active_run = (cvt->current_run && cvt->current_run->ctx = ctx) ?
+		cvt->current_run : NULL;
+
+	need_abort = (run_count || active_run);
+
+	ctx->aborting = need_abort;
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (!need_abort) {
+		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
+			__func__, ctx);
+		return;
+	}
+
+	dev_dbg(priv->ipu->dev,
+		 "%s: wait for completion: %d runs, active run %p\n",
+		 __func__, run_count, active_run);
+
+	ret = wait_for_completion_timeout(&ctx->aborted,
+					  msecs_to_jiffies(10000));
+	if (ret = 0) {
+		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
+		ipu_ic_force_abort(ctx);
+	}
+
+	ctx->aborting = false;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
+
+/* Unprepare image conversion context */
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	unsigned long flags;
+	bool put_res;
+
+	/* make sure no runs are hanging around */
+	ipu_image_convert_abort(ctx);
+
+	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	list_del(&ctx->list);
+
+	put_res = list_empty(&cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (put_res)
+		ipu_ic_release_ipu_resources(cvt);
+
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+
+	kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
+
+/*
+ * "Canned" asynchronous single image conversion. On successful return
+ * caller must call ipu_image_convert_unprepare() after conversion completes.
+ * Returns the new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context)
+{
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+
+	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
+					complete, complete_context);
+	if (IS_ERR(ctx))
+		return ctx;
+
+	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
+	if (IS_ERR(run)) {
+		ipu_image_convert_unprepare(ctx);
+		return ERR_PTR(PTR_ERR(run));
+	}
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert);
+
+/* "Canned" synchronous single image conversion */
+static void image_convert_sync_complete(void *data,
+					struct image_converter_run *run,
+					int err)
+{
+	struct completion *comp = data;
+
+	complete(comp);
+}
+
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode)
+{
+	struct image_converter_ctx *ctx;
+	struct completion comp;
+	int ret;
+
+	init_completion(&comp);
+
+	ctx = ipu_image_convert(ic, in, out, rot_mode,
+				image_convert_sync_complete, &comp);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
+	ret = (ret = 0) ? -ETIMEDOUT : 0;
+
+	ipu_image_convert_unprepare(ctx);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -746,6 +2415,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	ipu->ic_priv = priv;
 
 	spin_lock_init(&priv->lock);
+
 	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
 	if (!priv->base)
 		return -ENOMEM;
@@ -758,10 +2428,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
-		priv->task[i].task = i;
-		priv->task[i].priv = priv;
-		priv->task[i].reg = &ic_task_reg[i];
-		priv->task[i].bit = &ic_task_bit[i];
+		struct ipu_ic *ic = &priv->task[i];
+		struct image_converter *cvt = &ic->cvt;
+
+		ic->task = i;
+		ic->priv = priv;
+		ic->reg = &ic_task_reg[i];
+		ic->bit = &ic_task_bit[i];
+		ic->ch = &ic_task_ch[i];
+
+		cvt->ic = ic;
+		spin_lock_init(&cvt->irqlock);
+		INIT_LIST_HEAD(&cvt->ctx_list);
+		INIT_LIST_HEAD(&cvt->pending_q);
+		INIT_LIST_HEAD(&cvt->done_q);
+		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
 	}
 
 	return 0;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index afbc89d..25c40b3 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -63,17 +63,25 @@ enum ipu_csi_dest {
 /*
  * Enumeration of IPU rotation modes
  */
+#define IPU_ROT_BIT_VFLIP (1 << 0)
+#define IPU_ROT_BIT_HFLIP (1 << 1)
+#define IPU_ROT_BIT_90    (1 << 2)
+
 enum ipu_rotate_mode {
 	IPU_ROTATE_NONE = 0,
-	IPU_ROTATE_VERT_FLIP,
-	IPU_ROTATE_HORIZ_FLIP,
-	IPU_ROTATE_180,
-	IPU_ROTATE_90_RIGHT,
-	IPU_ROTATE_90_RIGHT_VFLIP,
-	IPU_ROTATE_90_RIGHT_HFLIP,
-	IPU_ROTATE_90_LEFT,
+	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
+	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
+	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
+	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
+	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
+			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
 };
 
+/* 90-degree rotations require the IRT unit */
+#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
+
 enum ipu_color_space {
 	IPUV3_COLORSPACE_RGB,
 	IPUV3_COLORSPACE_YUV,
@@ -316,6 +324,7 @@ enum ipu_ic_task {
 };
 
 struct ipu_ic;
+
 int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
@@ -330,6 +339,40 @@ void ipu_ic_task_disable(struct ipu_ic *ic);
 int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
+
+struct image_converter_ctx;
+struct image_converter_run;
+
+typedef void (*image_converter_cb_t)(void *ctx,
+				     struct image_converter_run *run,
+				     int err);
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context);
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys);
+void ipu_image_convert_abort(struct image_converter_ctx *ctx);
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context);
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode);
+
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 12/13] gpu: ipu-ic: allow multiple handles to ic
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

The image converter kernel API supports conversion contexts and
job queues, so we should allow more than one handle to the IC, so
that multiple users can add jobs to the queue.

Note however that users that control the IC manually (that do not
use the image converter APIs but setup the IC task by hand via calls
to ipu_ic_task_enable(), ipu_ic_enable(), etc.) must still be careful not
to share the IC handle with other threads. At this point, the only user
that still controls the IC manually is the i.mx capture driver. In that
case the capture driver only allows one open context to get a handle
to the IC at a time, so we should be ok there.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 5471b72..cc0780b 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -342,7 +342,6 @@ struct ipu_ic {
 	enum ipu_color_space out_cs;
 	bool graphics;
 	bool rotation;
-	bool in_use;
 
 	struct image_converter cvt;
 
@@ -2367,38 +2366,16 @@ EXPORT_SYMBOL_GPL(ipu_ic_disable);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
 {
 	struct ipu_ic_priv *priv = ipu->ic_priv;
-	unsigned long flags;
-	struct ipu_ic *ic, *ret;
 
 	if (task >= IC_NUM_TASKS)
 		return ERR_PTR(-EINVAL);
 
-	ic = &priv->task[task];
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	if (ic->in_use) {
-		ret = ERR_PTR(-EBUSY);
-		goto unlock;
-	}
-
-	ic->in_use = true;
-	ret = ic;
-
-unlock:
-	spin_unlock_irqrestore(&priv->lock, flags);
-	return ret;
+	return &priv->task[task];
 }
 EXPORT_SYMBOL_GPL(ipu_ic_get);
 
 void ipu_ic_put(struct ipu_ic *ic)
 {
-	struct ipu_ic_priv *priv = ic->priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ic->in_use = false;
-	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_put);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 12/13] gpu: ipu-ic: allow multiple handles to ic
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

The image converter kernel API supports conversion contexts and
job queues, so we should allow more than one handle to the IC, so
that multiple users can add jobs to the queue.

Note however that users that control the IC manually (that do not
use the image converter APIs but setup the IC task by hand via calls
to ipu_ic_task_enable(), ipu_ic_enable(), etc.) must still be careful not
to share the IC handle with other threads. At this point, the only user
that still controls the IC manually is the i.mx capture driver. In that
case the capture driver only allows one open context to get a handle
to the IC at a time, so we should be ok there.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 5471b72..cc0780b 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -342,7 +342,6 @@ struct ipu_ic {
 	enum ipu_color_space out_cs;
 	bool graphics;
 	bool rotation;
-	bool in_use;
 
 	struct image_converter cvt;
 
@@ -2367,38 +2366,16 @@ EXPORT_SYMBOL_GPL(ipu_ic_disable);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
 {
 	struct ipu_ic_priv *priv = ipu->ic_priv;
-	unsigned long flags;
-	struct ipu_ic *ic, *ret;
 
 	if (task >= IC_NUM_TASKS)
 		return ERR_PTR(-EINVAL);
 
-	ic = &priv->task[task];
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	if (ic->in_use) {
-		ret = ERR_PTR(-EBUSY);
-		goto unlock;
-	}
-
-	ic->in_use = true;
-	ret = ic;
-
-unlock:
-	spin_unlock_irqrestore(&priv->lock, flags);
-	return ret;
+	return &priv->task[task];
 }
 EXPORT_SYMBOL_GPL(ipu_ic_get);
 
 void ipu_ic_put(struct ipu_ic *ic)
 {
-	struct ipu_ic_priv *priv = ic->priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ic->in_use = false;
-	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_put);
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 13/13] gpu: ipu-v3: rename CSI client device
  2016-07-20  1:10   ` Steve Longerbeam
@ 2016-07-20  1:11     ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Rename the CSI client device in the client_reg[] table to
"imx-ipuv3-csi".

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index bb0377b..464c1c1 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1159,14 +1159,14 @@ static struct ipu_platform_reg client_reg[] = {
 			.dma[0] = IPUV3_CHANNEL_CSI0,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.csi = 1,
 			.dma[0] = IPUV3_CHANNEL_CSI1,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.di = 0,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 119+ messages in thread

* [PATCH v2 13/13] gpu: ipu-v3: rename CSI client device
@ 2016-07-20  1:11     ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-20  1:11 UTC (permalink / raw)
  To: p.zabel, plagnioj, tomi.valkeinen
  Cc: dri-devel, linux-fbdev, linux-kernel, Steve Longerbeam

Rename the CSI client device in the client_reg[] table to
"imx-ipuv3-csi".

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index bb0377b..464c1c1 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1159,14 +1159,14 @@ static struct ipu_platform_reg client_reg[] = {
 			.dma[0] = IPUV3_CHANNEL_CSI0,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.csi = 1,
 			.dma[0] = IPUV3_CHANNEL_CSI1,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.di = 0,
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-20  1:10     ` Steve Longerbeam
  (?)
@ 2016-07-25  6:06       ` kbuild test robot
  -1 siblings, 0 replies; 119+ messages in thread
From: kbuild test robot @ 2016-07-25  6:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: kbuild-all, p.zabel, plagnioj, tomi.valkeinen, dri-devel,
	linux-fbdev, linux-kernel, Steve Longerbeam

[-- Attachment #1: Type: text/plain, Size: 1786 bytes --]

Hi,

[auto build test ERROR on stable/master]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Steve-Longerbeam/IPUv3-prep-for-i-MX5-6-v4l2-staging-drivers-v2/20160725-010817
base:   https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git master
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 5.4.0-6) 5.4.0 20160609
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

Note: the linux-review/Steve-Longerbeam/IPUv3-prep-for-i-MX5-6-v4l2-staging-drivers-v2/20160725-010817 HEAD cc37134b2553bad1596348e3cab9859f69471f0a builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   drivers/gpu/ipu-v3/ipu-vdi.c: In function 'ipu_vdi_set_src':
>> drivers/gpu/ipu-v3/ipu-vdi.c:187:2: error: implicit declaration of function 'ipu_set_vdi_src_mux' [-Werror=implicit-function-declaration]
     ipu_set_vdi_src_mux(vdi->ipu, csi);
     ^
   cc1: some warnings being treated as errors

vim +/ipu_set_vdi_src_mux +187 drivers/gpu/ipu-v3/ipu-vdi.c

   181		spin_unlock_irqrestore(&vdi->lock, flags);
   182	}
   183	EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
   184	
   185	int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
   186	{
 > 187		ipu_set_vdi_src_mux(vdi->ipu, csi);
   188		return 0;
   189	}
   190	EXPORT_SYMBOL_GPL(ipu_vdi_set_src);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 57608 bytes --]

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
@ 2016-07-25  6:06       ` kbuild test robot
  0 siblings, 0 replies; 119+ messages in thread
From: kbuild test robot @ 2016-07-25  6:06 UTC (permalink / raw)
  To: linux-fbdev

[-- Attachment #1: Type: text/plain, Size: 1786 bytes --]

Hi,

[auto build test ERROR on stable/master]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Steve-Longerbeam/IPUv3-prep-for-i-MX5-6-v4l2-staging-drivers-v2/20160725-010817
base:   https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git master
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 5.4.0-6) 5.4.0 20160609
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

Note: the linux-review/Steve-Longerbeam/IPUv3-prep-for-i-MX5-6-v4l2-staging-drivers-v2/20160725-010817 HEAD cc37134b2553bad1596348e3cab9859f69471f0a builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   drivers/gpu/ipu-v3/ipu-vdi.c: In function 'ipu_vdi_set_src':
>> drivers/gpu/ipu-v3/ipu-vdi.c:187:2: error: implicit declaration of function 'ipu_set_vdi_src_mux' [-Werror=implicit-function-declaration]
     ipu_set_vdi_src_mux(vdi->ipu, csi);
     ^
   cc1: some warnings being treated as errors

vim +/ipu_set_vdi_src_mux +187 drivers/gpu/ipu-v3/ipu-vdi.c

   181		spin_unlock_irqrestore(&vdi->lock, flags);
   182	}
   183	EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
   184	
   185	int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
   186	{
 > 187		ipu_set_vdi_src_mux(vdi->ipu, csi);
   188		return 0;
   189	}
   190	EXPORT_SYMBOL_GPL(ipu_vdi_set_src);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 57608 bytes --]

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
@ 2016-07-25  6:06       ` kbuild test robot
  0 siblings, 0 replies; 119+ messages in thread
From: kbuild test robot @ 2016-07-25  6:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, Steve Longerbeam, linux-kernel, dri-devel,
	tomi.valkeinen, kbuild-all, plagnioj

[-- Attachment #1: Type: text/plain, Size: 1786 bytes --]

Hi,

[auto build test ERROR on stable/master]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Steve-Longerbeam/IPUv3-prep-for-i-MX5-6-v4l2-staging-drivers-v2/20160725-010817
base:   https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git master
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 5.4.0-6) 5.4.0 20160609
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

Note: the linux-review/Steve-Longerbeam/IPUv3-prep-for-i-MX5-6-v4l2-staging-drivers-v2/20160725-010817 HEAD cc37134b2553bad1596348e3cab9859f69471f0a builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   drivers/gpu/ipu-v3/ipu-vdi.c: In function 'ipu_vdi_set_src':
>> drivers/gpu/ipu-v3/ipu-vdi.c:187:2: error: implicit declaration of function 'ipu_set_vdi_src_mux' [-Werror=implicit-function-declaration]
     ipu_set_vdi_src_mux(vdi->ipu, csi);
     ^
   cc1: some warnings being treated as errors

vim +/ipu_set_vdi_src_mux +187 drivers/gpu/ipu-v3/ipu-vdi.c

   181		spin_unlock_irqrestore(&vdi->lock, flags);
   182	}
   183	EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
   184	
   185	int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
   186	{
 > 187		ipu_set_vdi_src_mux(vdi->ipu, csi);
   188		return 0;
   189	}
   190	EXPORT_SYMBOL_GPL(ipu_vdi_set_src);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 57608 bytes --]

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2
  2016-07-20  1:10   ` Steve Longerbeam
  (?)
@ 2016-07-26 10:06     ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel,
	Steve Longerbeam

Hi Steve,

Am Dienstag, den 19.07.2016, 18:10 -0700 schrieb Steve Longerbeam:
> These updates to IPUv3 are needed for media staging drivers
> for i.MX5/6 video capture and mem2mem.
> 
> Steve Longerbeam (13):
>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>   gpu: ipu-v3: Add ipu_get_num()
>   gpu: ipu-v3: Add VDI input IDMAC channels
>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>   gpu: ipu-v3: Fix IRT usage
>   gpu: ipu-v3: rename CSI client device

I have applied these.

>  gpu: ipu-v3: Add Video Deinterlacer unit
>  gpu: ipu-v3: Add IDMA channel linking support
>  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>  gpu: ipu-ic: Add complete image conversion support with tiling
>  gpu: ipu-ic: allow multiple handles to ic

But I have questions or comments for these.

The VDI depends on ipu_set_vdi_src_mux, which I still don't think is in
the correct place. Channel linking looks mostly ok to me, but the image
converter tiling doesn't work with fixed size tiles. See the other mails
for details.

best regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2
@ 2016-07-26 10:06     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, Steve Longerbeam, linux-kernel, dri-devel,
	tomi.valkeinen, plagnioj

Hi Steve,

Am Dienstag, den 19.07.2016, 18:10 -0700 schrieb Steve Longerbeam:
> These updates to IPUv3 are needed for media staging drivers
> for i.MX5/6 video capture and mem2mem.
> 
> Steve Longerbeam (13):
>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>   gpu: ipu-v3: Add ipu_get_num()
>   gpu: ipu-v3: Add VDI input IDMAC channels
>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>   gpu: ipu-v3: Fix IRT usage
>   gpu: ipu-v3: rename CSI client device

I have applied these.

>  gpu: ipu-v3: Add Video Deinterlacer unit
>  gpu: ipu-v3: Add IDMA channel linking support
>  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>  gpu: ipu-ic: Add complete image conversion support with tiling
>  gpu: ipu-ic: allow multiple handles to ic

But I have questions or comments for these.

The VDI depends on ipu_set_vdi_src_mux, which I still don't think is in
the correct place. Channel linking looks mostly ok to me, but the image
converter tiling doesn't work with fixed size tiles. See the other mails
for details.

best regards
Philipp


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2
@ 2016-07-26 10:06     ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, Steve Longerbeam, linux-kernel, dri-devel,
	tomi.valkeinen, plagnioj

Hi Steve,

Am Dienstag, den 19.07.2016, 18:10 -0700 schrieb Steve Longerbeam:
> These updates to IPUv3 are needed for media staging drivers
> for i.MX5/6 video capture and mem2mem.
> 
> Steve Longerbeam (13):
>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>   gpu: ipu-v3: Add ipu_get_num()
>   gpu: ipu-v3: Add VDI input IDMAC channels
>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>   gpu: ipu-v3: Fix IRT usage
>   gpu: ipu-v3: rename CSI client device

I have applied these.

>  gpu: ipu-v3: Add Video Deinterlacer unit
>  gpu: ipu-v3: Add IDMA channel linking support
>  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>  gpu: ipu-ic: Add complete image conversion support with tiling
>  gpu: ipu-ic: allow multiple handles to ic

But I have questions or comments for these.

The VDI depends on ipu_set_vdi_src_mux, which I still don't think is in
the correct place. Channel linking looks mostly ok to me, but the image
converter tiling doesn't work with fixed size tiles. See the other mails
for details.

best regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-20  1:10     ` Steve Longerbeam
@ 2016-07-26 10:06       ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel,
	Steve Longerbeam

Am Dienstag, den 19.07.2016, 18:10 -0700 schrieb Steve Longerbeam:
> Adds the Video Deinterlacer (VDIC) unit.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
[...]
> +++ b/drivers/gpu/ipu-v3/ipu-vdi.c
[...]
> +static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
> +{
> +	u32 reg;
> +
> +	reg = ipu_vdi_read(vdi, VDI_C);
> +	if (top_field_0)
> +		reg &= ~VDI_C_TOP_FIELD_MAN_1;
> +	else
> +		reg |= VDI_C_TOP_FIELD_MAN_1;
> +	ipu_vdi_write(vdi, reg, VDI_C);
> +}
[...]
> +void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
> +{
> +	unsigned long flags;
> +	u32 reg;
> +	u32 mask_reg;
> +
> +	spin_lock_irqsave(&vdi->lock, flags);
> +
> +	reg = ipu_vdi_read(vdi, VDI_C);
> +	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
> +	if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
> +		reg &= ~VDI_C_TOP_FIELD_MAN_1;
> +	else
> +		reg |= VDI_C_TOP_FIELD_MAN_1;
> +
> +	ipu_vdi_write(vdi, reg, VDI_C);
> +
> +	spin_unlock_irqrestore(&vdi->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);

Why not export set top field man? Does it make sense to keep the user of
this API in the dark about the current setting?

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit
@ 2016-07-26 10:06       ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel,
	Steve Longerbeam

Am Dienstag, den 19.07.2016, 18:10 -0700 schrieb Steve Longerbeam:
> Adds the Video Deinterlacer (VDIC) unit.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
[...]
> +++ b/drivers/gpu/ipu-v3/ipu-vdi.c
[...]
> +static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
> +{
> +	u32 reg;
> +
> +	reg = ipu_vdi_read(vdi, VDI_C);
> +	if (top_field_0)
> +		reg &= ~VDI_C_TOP_FIELD_MAN_1;
> +	else
> +		reg |= VDI_C_TOP_FIELD_MAN_1;
> +	ipu_vdi_write(vdi, reg, VDI_C);
> +}
[...]
> +void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
> +{
> +	unsigned long flags;
> +	u32 reg;
> +	u32 mask_reg;
> +
> +	spin_lock_irqsave(&vdi->lock, flags);
> +
> +	reg = ipu_vdi_read(vdi, VDI_C);
> +	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
> +	if (mask_reg = VDI_C_TOP_FIELD_MAN_1)
> +		reg &= ~VDI_C_TOP_FIELD_MAN_1;
> +	else
> +		reg |= VDI_C_TOP_FIELD_MAN_1;
> +
> +	ipu_vdi_write(vdi, reg, VDI_C);
> +
> +	spin_unlock_irqrestore(&vdi->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);

Why not export set top field man? Does it make sense to keep the user of
this API in the dark about the current setting?

regards
Philipp


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-20  1:11     ` Steve Longerbeam
  (?)
@ 2016-07-26 10:06       ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel,
	Steve Longerbeam

Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
> Adds functions to link and unlink IDMAC source channels to sink
> channels.
> 
> So far the following links are supported:
> 
> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
> 
> More links can be added to the idmac_link_info[] array.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

This patch looks good to me, but the Frame Synchronisation Unit supports
also linking the internal channels, not only the IDMAC channels.

[...]
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
[...]
> +static const struct idmac_link_info idmac_link_info[] = {
> +	{
> +		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
> +			  0, 4, 7 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
> +			  0, 4, 1 },
> +	}, {
> +		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
> +			  8, 4, 8 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
> +			  4, 4, 1 },
> +	}, {
> +		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
> +			  16, 4, 5 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
> +			  12, 4, 3 },
> +	},
> +};

How about adding new (internal) channel numbers for the CSI->VDI link
and having something like:

	{
		.src =  { IPUV3_CHANNEL_CSI_DIRECT, IPU_FS_PROC_FLOW1,
			  FS_VDI_SRC_SEL_OFFSET, 2, 1 },
		.sink = { IPUV3_CHANNEL_VDI_CUR, 0, 0, 0 },
	},

instead of ipu_set_vdi_src_mux? Then in addition to

[...]
> +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)

We could have a lower level

ipu_fsu_link(int channel1, int channel2)

which could be called like

ipu_fsu_link(IPUV3_CHANNEL_CSI_DIRECT, IPUV3_CHANNEL_VDI_CUR);

Come to think of it, could we replace shift,bits,sel with mask,value and
add #defines for the FSU bit fields and values? I'm thinking:

/* FS_PROC_FLOW1 */
#define FS_PRPENC_ROT_SRC_SEL_MASK	(0xf << 0)
#define FS_PRPENC_ROT_SRC_SEL_ENC		(0x7 << 0)
#define FS_PRPVF_ROT_SRC_SEL_MASK	(0xf << 8)
#define FS_PRPVF_ROT_SRC_SEL_VF			(0x8 << 8)
#define FS_PP_SRC_SEL_MASK		(0xf << 12)
#define FS_PP_ROT_SRC_SEL_MASK		(0xf << 16)
#define FS_PP_ROT_SRC_SEL_PP			(0x5 << 16)
#define FS_VDI1_SRC_SEL_MASK		(0x3 << 20)
#define FS_VDI3_SRC_SEL_MASK		(0x3 << 20)
#define FS_PRP_SRC_SEL_MASK		(0xf << 24)
#define FS_VDI_SRC_SEL_MASK		(0x3 << 28)

/* FS_PROC_FLOW2 */
#define FS_PRP_ENC_DEST_SEL_MASK	(0xf << 0)
#define FS_PRP_ENC_DEST_SEL_IRT_ENC		(0x1 << 0)
#define FS_PRPVF_DEST_SEL_MASK		(0xf << 4)
#define FS_PRPVF_DEST_SEL_IRT_VF		(0x1 << 4)
#define FS_PRPVF_ROT_DEST_SEL_MASK	(0xf << 8)
#define FS_PP_DEST_SEL_MASK		(0xf << 12)
#define FS_PP_DEST_SEL_IRT_PP			(0x3 << 12)
#define FS_PP_ROT_DEST_SEL_MASK		(0xf << 16)
#define FS_PRPENC_ROT_DEST_SEL_MASK	(0xf << 20)
#define FS_PRP_DEST_SEL_MASK		(0xf << 24)

struct idmac_link_reg_info {
	int chno;
	u32 reg;
	u32 mask;
	u32 val;
};

static const struct idmac_link_info idmac_link_info[] = {
	{
		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
	}, {
               .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
                         FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
               .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
                         FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
	}, {
               .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
                         FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
               .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
                         FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
	},
};

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-26 10:06       ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, Steve Longerbeam, linux-kernel, dri-devel,
	tomi.valkeinen, plagnioj

Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
> Adds functions to link and unlink IDMAC source channels to sink
> channels.
> 
> So far the following links are supported:
> 
> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
> 
> More links can be added to the idmac_link_info[] array.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

This patch looks good to me, but the Frame Synchronisation Unit supports
also linking the internal channels, not only the IDMAC channels.

[...]
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
[...]
> +static const struct idmac_link_info idmac_link_info[] = {
> +	{
> +		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
> +			  0, 4, 7 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
> +			  0, 4, 1 },
> +	}, {
> +		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
> +			  8, 4, 8 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
> +			  4, 4, 1 },
> +	}, {
> +		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
> +			  16, 4, 5 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
> +			  12, 4, 3 },
> +	},
> +};

How about adding new (internal) channel numbers for the CSI->VDI link
and having something like:

	{
		.src =  { IPUV3_CHANNEL_CSI_DIRECT, IPU_FS_PROC_FLOW1,
			  FS_VDI_SRC_SEL_OFFSET, 2, 1 },
		.sink = { IPUV3_CHANNEL_VDI_CUR, 0, 0, 0 },
	},

instead of ipu_set_vdi_src_mux? Then in addition to

[...]
> +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)

We could have a lower level

ipu_fsu_link(int channel1, int channel2)

which could be called like

ipu_fsu_link(IPUV3_CHANNEL_CSI_DIRECT, IPUV3_CHANNEL_VDI_CUR);

Come to think of it, could we replace shift,bits,sel with mask,value and
add #defines for the FSU bit fields and values? I'm thinking:

/* FS_PROC_FLOW1 */
#define FS_PRPENC_ROT_SRC_SEL_MASK	(0xf << 0)
#define FS_PRPENC_ROT_SRC_SEL_ENC		(0x7 << 0)
#define FS_PRPVF_ROT_SRC_SEL_MASK	(0xf << 8)
#define FS_PRPVF_ROT_SRC_SEL_VF			(0x8 << 8)
#define FS_PP_SRC_SEL_MASK		(0xf << 12)
#define FS_PP_ROT_SRC_SEL_MASK		(0xf << 16)
#define FS_PP_ROT_SRC_SEL_PP			(0x5 << 16)
#define FS_VDI1_SRC_SEL_MASK		(0x3 << 20)
#define FS_VDI3_SRC_SEL_MASK		(0x3 << 20)
#define FS_PRP_SRC_SEL_MASK		(0xf << 24)
#define FS_VDI_SRC_SEL_MASK		(0x3 << 28)

/* FS_PROC_FLOW2 */
#define FS_PRP_ENC_DEST_SEL_MASK	(0xf << 0)
#define FS_PRP_ENC_DEST_SEL_IRT_ENC		(0x1 << 0)
#define FS_PRPVF_DEST_SEL_MASK		(0xf << 4)
#define FS_PRPVF_DEST_SEL_IRT_VF		(0x1 << 4)
#define FS_PRPVF_ROT_DEST_SEL_MASK	(0xf << 8)
#define FS_PP_DEST_SEL_MASK		(0xf << 12)
#define FS_PP_DEST_SEL_IRT_PP			(0x3 << 12)
#define FS_PP_ROT_DEST_SEL_MASK		(0xf << 16)
#define FS_PRPENC_ROT_DEST_SEL_MASK	(0xf << 20)
#define FS_PRP_DEST_SEL_MASK		(0xf << 24)

struct idmac_link_reg_info {
	int chno;
	u32 reg;
	u32 mask;
	u32 val;
};

static const struct idmac_link_info idmac_link_info[] = {
	{
		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
	}, {
               .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
                         FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
               .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
                         FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
	}, {
               .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
                         FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
               .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
                         FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
	},
};

regards
Philipp


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-26 10:06       ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:06 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, Steve Longerbeam, linux-kernel, dri-devel,
	tomi.valkeinen, plagnioj

Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
> Adds functions to link and unlink IDMAC source channels to sink
> channels.
> 
> So far the following links are supported:
> 
> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
> 
> More links can be added to the idmac_link_info[] array.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>

This patch looks good to me, but the Frame Synchronisation Unit supports
also linking the internal channels, not only the IDMAC channels.

[...]
> +++ b/drivers/gpu/ipu-v3/ipu-common.c
[...]
> +static const struct idmac_link_info idmac_link_info[] = {
> +	{
> +		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
> +			  0, 4, 7 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
> +			  0, 4, 1 },
> +	}, {
> +		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
> +			  8, 4, 8 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
> +			  4, 4, 1 },
> +	}, {
> +		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
> +			  16, 4, 5 },
> +		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
> +			  12, 4, 3 },
> +	},
> +};

How about adding new (internal) channel numbers for the CSI->VDI link
and having something like:

	{
		.src =  { IPUV3_CHANNEL_CSI_DIRECT, IPU_FS_PROC_FLOW1,
			  FS_VDI_SRC_SEL_OFFSET, 2, 1 },
		.sink = { IPUV3_CHANNEL_VDI_CUR, 0, 0, 0 },
	},

instead of ipu_set_vdi_src_mux? Then in addition to

[...]
> +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)

We could have a lower level

ipu_fsu_link(int channel1, int channel2)

which could be called like

ipu_fsu_link(IPUV3_CHANNEL_CSI_DIRECT, IPUV3_CHANNEL_VDI_CUR);

Come to think of it, could we replace shift,bits,sel with mask,value and
add #defines for the FSU bit fields and values? I'm thinking:

/* FS_PROC_FLOW1 */
#define FS_PRPENC_ROT_SRC_SEL_MASK	(0xf << 0)
#define FS_PRPENC_ROT_SRC_SEL_ENC		(0x7 << 0)
#define FS_PRPVF_ROT_SRC_SEL_MASK	(0xf << 8)
#define FS_PRPVF_ROT_SRC_SEL_VF			(0x8 << 8)
#define FS_PP_SRC_SEL_MASK		(0xf << 12)
#define FS_PP_ROT_SRC_SEL_MASK		(0xf << 16)
#define FS_PP_ROT_SRC_SEL_PP			(0x5 << 16)
#define FS_VDI1_SRC_SEL_MASK		(0x3 << 20)
#define FS_VDI3_SRC_SEL_MASK		(0x3 << 20)
#define FS_PRP_SRC_SEL_MASK		(0xf << 24)
#define FS_VDI_SRC_SEL_MASK		(0x3 << 28)

/* FS_PROC_FLOW2 */
#define FS_PRP_ENC_DEST_SEL_MASK	(0xf << 0)
#define FS_PRP_ENC_DEST_SEL_IRT_ENC		(0x1 << 0)
#define FS_PRPVF_DEST_SEL_MASK		(0xf << 4)
#define FS_PRPVF_DEST_SEL_IRT_VF		(0x1 << 4)
#define FS_PRPVF_ROT_DEST_SEL_MASK	(0xf << 8)
#define FS_PP_DEST_SEL_MASK		(0xf << 12)
#define FS_PP_DEST_SEL_IRT_PP			(0x3 << 12)
#define FS_PP_ROT_DEST_SEL_MASK		(0xf << 16)
#define FS_PRPENC_ROT_DEST_SEL_MASK	(0xf << 20)
#define FS_PRP_DEST_SEL_MASK		(0xf << 24)

struct idmac_link_reg_info {
	int chno;
	u32 reg;
	u32 mask;
	u32 val;
};

static const struct idmac_link_info idmac_link_info[] = {
	{
		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
	}, {
               .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
                         FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
               .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
                         FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
	}, {
               .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
                         FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
               .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
                         FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
	},
};

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-20  1:11     ` Steve Longerbeam
@ 2016-07-26 10:08       ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:08 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel,
	Steve Longerbeam

Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
> This patch implements complete image conversion support to ipu-ic,
> with tiling to support scaling to and from images up to 4096x4096.
> Image rotation is also supported.
> 
> The internal API is subsystem agnostic (no V4L2 dependency except
> for the use of V4L2 fourcc pixel formats).
> 
> Callers prepare for image conversion by calling
> ipu_image_convert_prepare(), which initializes the parameters of
> the conversion. The caller passes in the ipu_ic task to use for
> the conversion, the input and output image formats, a rotation mode,
> and a completion callback and completion context pointer:
> 
> struct image_converter_ctx *
> ipu_image_convert_prepare(struct ipu_ic *ic,
>                           struct ipu_image *in, struct ipu_image *out,
>                           enum ipu_rotate_mode rot_mode,
>                           image_converter_cb_t complete,
>                           void *complete_context);
> 
> The caller is given a new conversion context that must be passed to
> the further APIs:
> 
> struct image_converter_run *
> ipu_image_convert_run(struct image_converter_ctx *ctx,
>                       dma_addr_t in_phys, dma_addr_t out_phys);
> 
> This queues a new image conversion request to a run queue, and
> starts the conversion immediately if the run queue is empty. Only
> the physaddr's of the input and output image buffers are needed,
> since the conversion context was created previously with
> ipu_image_convert_prepare(). Returns a new run object pointer. When
> the conversion completes, the run pointer is returned to the
> completion callback.
> 
> void image_convert_abort(struct image_converter_ctx *ctx);
> 
> This will abort any active or pending conversions for this context.
> Any currently active or pending runs belonging to this context are
> returned via the completion callback with an error status.
> 
> void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
> 
> Unprepares the conversion context. Any active or pending runs will
> be aborted by calling image_convert_abort().
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
>  include/video/imx-ipu-v3.h  |   57 +-
>  2 files changed, 1736 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index 1a37afc..5471b72 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -17,6 +17,8 @@
>  #include <linux/bitrev.h>
>  #include <linux/io.h>
>  #include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
>  #include "ipu-prv.h"
>  
>  /* IC Register Offsets */
> @@ -82,6 +84,40 @@
>  #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
>  #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
>  
> +/*
> + * The IC Resizer has a restriction that the output frame from the
> + * resizer must be 1024 or less in both width (pixels) and height
> + * (lines).
> + *
> + * The image conversion support attempts to split up a conversion when
> + * the desired output (converted) frame resolution exceeds the IC resizer
> + * limit of 1024 in either dimension.
> + *
> + * If either dimension of the output frame exceeds the limit, the
> + * dimension is split into 1, 2, or 4 equal stripes, 

Imagine converting a 320x200 image up to 1280x800, and consider only the
x coordinate. The IC upscaler is a simple bilinear scaler.

We want target pixel x' = 1279 to sample the source pixel x = 319, so
the scaling factor rsc is calculated to:

x = x' * (320-1)/(1280-1) = x' * 8192/rsc, with rsc = 32846

That means that the target pixels x' = 639 and x' = 640 should be
sampled (bilinearly) from x = 639 * 8192/32846. = 159.37 and x = 640 *
8192/32846. = 159.62, respectively.

Now split the frame in half and suddenly pixel x' = 640 is the start of
a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
the edge of the source image.
This problem gets worse if you start using arbitrary frame sizes and YUV
planar images and consider that tile start addresses are (currently)
limited to 8 byte boundaries, to the point that there are very visible
seams in the center of the image, depending on scaling factor and image
sizes.

I wonder how much effort it would be to remove the tiling code for now
and add it back in a second step once it is fixed? Otherwise we could
just disallow scaled tiling for now, but I'd like this to be prepared
for tiles with different sizes at least, before merging.

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-07-26 10:08       ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-07-26 10:08 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel,
	Steve Longerbeam

Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
> This patch implements complete image conversion support to ipu-ic,
> with tiling to support scaling to and from images up to 4096x4096.
> Image rotation is also supported.
> 
> The internal API is subsystem agnostic (no V4L2 dependency except
> for the use of V4L2 fourcc pixel formats).
> 
> Callers prepare for image conversion by calling
> ipu_image_convert_prepare(), which initializes the parameters of
> the conversion. The caller passes in the ipu_ic task to use for
> the conversion, the input and output image formats, a rotation mode,
> and a completion callback and completion context pointer:
> 
> struct image_converter_ctx *
> ipu_image_convert_prepare(struct ipu_ic *ic,
>                           struct ipu_image *in, struct ipu_image *out,
>                           enum ipu_rotate_mode rot_mode,
>                           image_converter_cb_t complete,
>                           void *complete_context);
> 
> The caller is given a new conversion context that must be passed to
> the further APIs:
> 
> struct image_converter_run *
> ipu_image_convert_run(struct image_converter_ctx *ctx,
>                       dma_addr_t in_phys, dma_addr_t out_phys);
> 
> This queues a new image conversion request to a run queue, and
> starts the conversion immediately if the run queue is empty. Only
> the physaddr's of the input and output image buffers are needed,
> since the conversion context was created previously with
> ipu_image_convert_prepare(). Returns a new run object pointer. When
> the conversion completes, the run pointer is returned to the
> completion callback.
> 
> void image_convert_abort(struct image_converter_ctx *ctx);
> 
> This will abort any active or pending conversions for this context.
> Any currently active or pending runs belonging to this context are
> returned via the completion callback with an error status.
> 
> void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
> 
> Unprepares the conversion context. Any active or pending runs will
> be aborted by calling image_convert_abort().
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
>  include/video/imx-ipu-v3.h  |   57 +-
>  2 files changed, 1736 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index 1a37afc..5471b72 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -17,6 +17,8 @@
>  #include <linux/bitrev.h>
>  #include <linux/io.h>
>  #include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
>  #include "ipu-prv.h"
>  
>  /* IC Register Offsets */
> @@ -82,6 +84,40 @@
>  #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
>  #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
>  
> +/*
> + * The IC Resizer has a restriction that the output frame from the
> + * resizer must be 1024 or less in both width (pixels) and height
> + * (lines).
> + *
> + * The image conversion support attempts to split up a conversion when
> + * the desired output (converted) frame resolution exceeds the IC resizer
> + * limit of 1024 in either dimension.
> + *
> + * If either dimension of the output frame exceeds the limit, the
> + * dimension is split into 1, 2, or 4 equal stripes, 

Imagine converting a 320x200 image up to 1280x800, and consider only the
x coordinate. The IC upscaler is a simple bilinear scaler.

We want target pixel x' = 1279 to sample the source pixel x = 319, so
the scaling factor rsc is calculated to:

x = x' * (320-1)/(1280-1) = x' * 8192/rsc, with rsc = 32846

That means that the target pixels x' = 639 and x' = 640 should be
sampled (bilinearly) from x = 639 * 8192/32846. = 159.37 and x = 640 *
8192/32846. = 159.62, respectively.

Now split the frame in half and suddenly pixel x' = 640 is the start of
a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
the edge of the source image.
This problem gets worse if you start using arbitrary frame sizes and YUV
planar images and consider that tile start addresses are (currently)
limited to 8 byte boundaries, to the point that there are very visible
seams in the center of the image, depending on scaling factor and image
sizes.

I wonder how much effort it would be to remove the tiling code for now
and add it back in a second step once it is fixed? Otherwise we could
just disallow scaled tiling for now, but I'd like this to be prepared
for tiles with different sizes at least, before merging.

regards
Philipp


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-26 10:08       ` Philipp Zabel
  (?)
@ 2016-07-28 23:09         ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-28 23:09 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel

Hi Philipp,

On 07/26/2016 03:08 AM, Philipp Zabel wrote:
>
>>   
>> +/*
>> + * The IC Resizer has a restriction that the output frame from the
>> + * resizer must be 1024 or less in both width (pixels) and height
>> + * (lines).
>> + *
>> + * The image conversion support attempts to split up a conversion when
>> + * the desired output (converted) frame resolution exceeds the IC resizer
>> + * limit of 1024 in either dimension.
>> + *
>> + * If either dimension of the output frame exceeds the limit, the
>> + * dimension is split into 1, 2, or 4 equal stripes,
> Imagine converting a 320x200 image up to 1280x800, and consider only the
> x coordinate. The IC upscaler is a simple bilinear scaler.

Right, the upscaling is a simple linear interpolation between two
adjacent input pixels, just paraphrasing you.

> We want target pixel x' = 1279 to sample the source pixel x = 319, so
> the scaling factor rsc is calculated to:
>
> x = x' * (320-1)/(1280-1) = x' * 8192/rsc, with rsc = 32846
>
> That means that the target pixels x' = 639 and x' = 640 should be
> sampled (bilinearly) from x = 639 * 8192/32846. = 159.37 and x = 640 *
> 8192/32846. = 159.62, respectively.

I'm with you so far.

>
> Now split the frame in half and suddenly pixel x' = 640 is the start of
> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> the edge of the source image.

Here's where we part.

The 320x200 --> 1280x800 conversion is split into two 160x200 -->
640x800 conversions. The DMA controller and ipu_ic_task_init() are given
those width/height dimensions, not the dimensions of the original images.
So this is simply two separate 160x200 --> 640x800 conversions. The only
difference from a true 160x200 --> 640x800 image conversion is that the DMA
controller must be given the stride lengths of the original 320x200 and 
1280x800
images.

The rsc for the 160x200 --> 640x800 conversions is

x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923


So original horizontal position 640 is really x' = 0 of the second 
conversion,
which is sampled at x = 0 of the second conversion. And the pixel at x' 
= 1279
is really x' = 639 of the second conversion, which is sampled at x = 639 
* 8192/32923
= 158.98, which does not read over the edge of the source tile.


> This problem gets worse if you start using arbitrary frame sizes and YUV
> planar images and consider that tile start addresses are (currently)
> limited to 8 byte boundaries, to the point that there are very visible
> seams in the center of the image, depending on scaling factor and image
> sizes.

Indeed there could be other parameters that would cause the resizer to
read past the edge of the source tiles, I will need to try and find such 
cases.
But not in the above case.

That said, I _have_ noticed seams, but I have always attributed them to the
fact that we have a discontinuity in color-space conversion and/or resize
interpolation at the boundary between tiles.

I've also found that the seams are quite noticeable when rendered to a
display overlay, but become significantly less pronounced if the images are
converted to a back buffer, and then page-flipped to front buffer when the
conversion (all tiles) completes.

Steve

> I wonder how much effort it would be to remove the tiling code for now
> and add it back in a second step once it is fixed? Otherwise we could
> just disallow scaled tiling for now, but I'd like this to be prepared
> for tiles with different sizes at least, before merging.
>
> regards
> Philipp
>

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-07-28 23:09         ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-28 23:09 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel

Hi Philipp,

On 07/26/2016 03:08 AM, Philipp Zabel wrote:
>
>>   
>> +/*
>> + * The IC Resizer has a restriction that the output frame from the
>> + * resizer must be 1024 or less in both width (pixels) and height
>> + * (lines).
>> + *
>> + * The image conversion support attempts to split up a conversion when
>> + * the desired output (converted) frame resolution exceeds the IC resizer
>> + * limit of 1024 in either dimension.
>> + *
>> + * If either dimension of the output frame exceeds the limit, the
>> + * dimension is split into 1, 2, or 4 equal stripes,
> Imagine converting a 320x200 image up to 1280x800, and consider only the
> x coordinate. The IC upscaler is a simple bilinear scaler.

Right, the upscaling is a simple linear interpolation between two
adjacent input pixels, just paraphrasing you.

> We want target pixel x' = 1279 to sample the source pixel x = 319, so
> the scaling factor rsc is calculated to:
>
> x = x' * (320-1)/(1280-1) = x' * 8192/rsc, with rsc = 32846
>
> That means that the target pixels x' = 639 and x' = 640 should be
> sampled (bilinearly) from x = 639 * 8192/32846. = 159.37 and x = 640 *
> 8192/32846. = 159.62, respectively.

I'm with you so far.

>
> Now split the frame in half and suddenly pixel x' = 640 is the start of
> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> the edge of the source image.

Here's where we part.

The 320x200 --> 1280x800 conversion is split into two 160x200 -->
640x800 conversions. The DMA controller and ipu_ic_task_init() are given
those width/height dimensions, not the dimensions of the original images.
So this is simply two separate 160x200 --> 640x800 conversions. The only
difference from a true 160x200 --> 640x800 image conversion is that the DMA
controller must be given the stride lengths of the original 320x200 and 
1280x800
images.

The rsc for the 160x200 --> 640x800 conversions is

x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923


So original horizontal position 640 is really x' = 0 of the second 
conversion,
which is sampled at x = 0 of the second conversion. And the pixel at x' 
= 1279
is really x' = 639 of the second conversion, which is sampled at x = 639 
* 8192/32923
= 158.98, which does not read over the edge of the source tile.


> This problem gets worse if you start using arbitrary frame sizes and YUV
> planar images and consider that tile start addresses are (currently)
> limited to 8 byte boundaries, to the point that there are very visible
> seams in the center of the image, depending on scaling factor and image
> sizes.

Indeed there could be other parameters that would cause the resizer to
read past the edge of the source tiles, I will need to try and find such 
cases.
But not in the above case.

That said, I _have_ noticed seams, but I have always attributed them to the
fact that we have a discontinuity in color-space conversion and/or resize
interpolation at the boundary between tiles.

I've also found that the seams are quite noticeable when rendered to a
display overlay, but become significantly less pronounced if the images are
converted to a back buffer, and then page-flipped to front buffer when the
conversion (all tiles) completes.

Steve

> I wonder how much effort it would be to remove the tiling code for now
> and add it back in a second step once it is fixed? Otherwise we could
> just disallow scaled tiling for now, but I'd like this to be prepared
> for tiles with different sizes at least, before merging.
>
> regards
> Philipp
>


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-07-28 23:09         ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-28 23:09 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel

Hi Philipp,

On 07/26/2016 03:08 AM, Philipp Zabel wrote:
>
>>   
>> +/*
>> + * The IC Resizer has a restriction that the output frame from the
>> + * resizer must be 1024 or less in both width (pixels) and height
>> + * (lines).
>> + *
>> + * The image conversion support attempts to split up a conversion when
>> + * the desired output (converted) frame resolution exceeds the IC resizer
>> + * limit of 1024 in either dimension.
>> + *
>> + * If either dimension of the output frame exceeds the limit, the
>> + * dimension is split into 1, 2, or 4 equal stripes,
> Imagine converting a 320x200 image up to 1280x800, and consider only the
> x coordinate. The IC upscaler is a simple bilinear scaler.

Right, the upscaling is a simple linear interpolation between two
adjacent input pixels, just paraphrasing you.

> We want target pixel x' = 1279 to sample the source pixel x = 319, so
> the scaling factor rsc is calculated to:
>
> x = x' * (320-1)/(1280-1) = x' * 8192/rsc, with rsc = 32846
>
> That means that the target pixels x' = 639 and x' = 640 should be
> sampled (bilinearly) from x = 639 * 8192/32846. = 159.37 and x = 640 *
> 8192/32846. = 159.62, respectively.

I'm with you so far.

>
> Now split the frame in half and suddenly pixel x' = 640 is the start of
> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> the edge of the source image.

Here's where we part.

The 320x200 --> 1280x800 conversion is split into two 160x200 -->
640x800 conversions. The DMA controller and ipu_ic_task_init() are given
those width/height dimensions, not the dimensions of the original images.
So this is simply two separate 160x200 --> 640x800 conversions. The only
difference from a true 160x200 --> 640x800 image conversion is that the DMA
controller must be given the stride lengths of the original 320x200 and 
1280x800
images.

The rsc for the 160x200 --> 640x800 conversions is

x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923


So original horizontal position 640 is really x' = 0 of the second 
conversion,
which is sampled at x = 0 of the second conversion. And the pixel at x' 
= 1279
is really x' = 639 of the second conversion, which is sampled at x = 639 
* 8192/32923
= 158.98, which does not read over the edge of the source tile.


> This problem gets worse if you start using arbitrary frame sizes and YUV
> planar images and consider that tile start addresses are (currently)
> limited to 8 byte boundaries, to the point that there are very visible
> seams in the center of the image, depending on scaling factor and image
> sizes.

Indeed there could be other parameters that would cause the resizer to
read past the edge of the source tiles, I will need to try and find such 
cases.
But not in the above case.

That said, I _have_ noticed seams, but I have always attributed them to the
fact that we have a discontinuity in color-space conversion and/or resize
interpolation at the boundary between tiles.

I've also found that the seams are quite noticeable when rendered to a
display overlay, but become significantly less pronounced if the images are
converted to a back buffer, and then page-flipped to front buffer when the
conversion (all tiles) completes.

Steve

> I wonder how much effort it would be to remove the tiling code for now
> and add it back in a second step once it is fixed? Otherwise we could
> just disallow scaled tiling for now, but I'd like this to be prepared
> for tiles with different sizes at least, before merging.
>
> regards
> Philipp
>

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-26 10:06       ` Philipp Zabel
  (?)
@ 2016-07-28 23:40         ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-28 23:40 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel



On 07/26/2016 03:06 AM, Philipp Zabel wrote:
> Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
>> Adds functions to link and unlink IDMAC source channels to sink
>> channels.
>>
>> So far the following links are supported:
>>
>> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
>> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
>> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
>>
>> More links can be added to the idmac_link_info[] array.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> This patch looks good to me, but the Frame Synchronisation Unit supports
> also linking the internal channels, not only the IDMAC channels.
>
> [...]
>> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> [...]
>> +static const struct idmac_link_info idmac_link_info[] = {
>> +	{
>> +		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
>> +			  0, 4, 7 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
>> +			  0, 4, 1 },
>> +	}, {
>> +		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
>> +			  8, 4, 8 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
>> +			  4, 4, 1 },
>> +	}, {
>> +		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
>> +			  16, 4, 5 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
>> +			  12, 4, 3 },
>> +	},
>> +};
> How about adding new (internal) channel numbers for the CSI->VDI link
> and having something like:
>
> 	{
> 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, IPU_FS_PROC_FLOW1,
> 			  FS_VDI_SRC_SEL_OFFSET, 2, 1 },
> 		.sink = { IPUV3_CHANNEL_VDI_CUR, 0, 0, 0 },
> 	},
>
> instead of ipu_set_vdi_src_mux? Then in addition to
>
> [...]
>> +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
> We could have a lower level
>
> ipu_fsu_link(int channel1, int channel2)
>
> which could be called like
>
> ipu_fsu_link(IPUV3_CHANNEL_CSI_DIRECT, IPUV3_CHANNEL_VDI_CUR);
>
> Come to think of it, could we replace shift,bits,sel with mask,value and
> add #defines for the FSU bit fields and values? I'm thinking:
>
> /* FS_PROC_FLOW1 */
> #define FS_PRPENC_ROT_SRC_SEL_MASK	(0xf << 0)
> #define FS_PRPENC_ROT_SRC_SEL_ENC		(0x7 << 0)
> #define FS_PRPVF_ROT_SRC_SEL_MASK	(0xf << 8)
> #define FS_PRPVF_ROT_SRC_SEL_VF			(0x8 << 8)
> #define FS_PP_SRC_SEL_MASK		(0xf << 12)
> #define FS_PP_ROT_SRC_SEL_MASK		(0xf << 16)
> #define FS_PP_ROT_SRC_SEL_PP			(0x5 << 16)
> #define FS_VDI1_SRC_SEL_MASK		(0x3 << 20)
> #define FS_VDI3_SRC_SEL_MASK		(0x3 << 20)
> #define FS_PRP_SRC_SEL_MASK		(0xf << 24)
> #define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
>
> /* FS_PROC_FLOW2 */
> #define FS_PRP_ENC_DEST_SEL_MASK	(0xf << 0)
> #define FS_PRP_ENC_DEST_SEL_IRT_ENC		(0x1 << 0)
> #define FS_PRPVF_DEST_SEL_MASK		(0xf << 4)
> #define FS_PRPVF_DEST_SEL_IRT_VF		(0x1 << 4)
> #define FS_PRPVF_ROT_DEST_SEL_MASK	(0xf << 8)
> #define FS_PP_DEST_SEL_MASK		(0xf << 12)
> #define FS_PP_DEST_SEL_IRT_PP			(0x3 << 12)
> #define FS_PP_ROT_DEST_SEL_MASK		(0xf << 16)
> #define FS_PRPENC_ROT_DEST_SEL_MASK	(0xf << 20)
> #define FS_PRP_DEST_SEL_MASK		(0xf << 24)
>
> struct idmac_link_reg_info {
> 	int chno;
> 	u32 reg;
> 	u32 mask;
> 	u32 val;
> };
>
> static const struct idmac_link_info idmac_link_info[] = {
> 	{
> 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
> 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
> 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
> 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
> 	}, {
>                 .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
>                           FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
>                 .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
>                           FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
> 	}, {
>                 .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
>                           FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
>                 .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
>                           FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
> 	},
> };

Hi Philipp,

Great ideas. I'll work on this for next version, or shall I let you? 
Should we create an
ipu-fsu.c ?

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-28 23:40         ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-28 23:40 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel



On 07/26/2016 03:06 AM, Philipp Zabel wrote:
> Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
>> Adds functions to link and unlink IDMAC source channels to sink
>> channels.
>>
>> So far the following links are supported:
>>
>> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
>> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
>> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
>>
>> More links can be added to the idmac_link_info[] array.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> This patch looks good to me, but the Frame Synchronisation Unit supports
> also linking the internal channels, not only the IDMAC channels.
>
> [...]
>> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> [...]
>> +static const struct idmac_link_info idmac_link_info[] = {
>> +	{
>> +		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
>> +			  0, 4, 7 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
>> +			  0, 4, 1 },
>> +	}, {
>> +		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
>> +			  8, 4, 8 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
>> +			  4, 4, 1 },
>> +	}, {
>> +		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
>> +			  16, 4, 5 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
>> +			  12, 4, 3 },
>> +	},
>> +};
> How about adding new (internal) channel numbers for the CSI->VDI link
> and having something like:
>
> 	{
> 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, IPU_FS_PROC_FLOW1,
> 			  FS_VDI_SRC_SEL_OFFSET, 2, 1 },
> 		.sink = { IPUV3_CHANNEL_VDI_CUR, 0, 0, 0 },
> 	},
>
> instead of ipu_set_vdi_src_mux? Then in addition to
>
> [...]
>> +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
> We could have a lower level
>
> ipu_fsu_link(int channel1, int channel2)
>
> which could be called like
>
> ipu_fsu_link(IPUV3_CHANNEL_CSI_DIRECT, IPUV3_CHANNEL_VDI_CUR);
>
> Come to think of it, could we replace shift,bits,sel with mask,value and
> add #defines for the FSU bit fields and values? I'm thinking:
>
> /* FS_PROC_FLOW1 */
> #define FS_PRPENC_ROT_SRC_SEL_MASK	(0xf << 0)
> #define FS_PRPENC_ROT_SRC_SEL_ENC		(0x7 << 0)
> #define FS_PRPVF_ROT_SRC_SEL_MASK	(0xf << 8)
> #define FS_PRPVF_ROT_SRC_SEL_VF			(0x8 << 8)
> #define FS_PP_SRC_SEL_MASK		(0xf << 12)
> #define FS_PP_ROT_SRC_SEL_MASK		(0xf << 16)
> #define FS_PP_ROT_SRC_SEL_PP			(0x5 << 16)
> #define FS_VDI1_SRC_SEL_MASK		(0x3 << 20)
> #define FS_VDI3_SRC_SEL_MASK		(0x3 << 20)
> #define FS_PRP_SRC_SEL_MASK		(0xf << 24)
> #define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
>
> /* FS_PROC_FLOW2 */
> #define FS_PRP_ENC_DEST_SEL_MASK	(0xf << 0)
> #define FS_PRP_ENC_DEST_SEL_IRT_ENC		(0x1 << 0)
> #define FS_PRPVF_DEST_SEL_MASK		(0xf << 4)
> #define FS_PRPVF_DEST_SEL_IRT_VF		(0x1 << 4)
> #define FS_PRPVF_ROT_DEST_SEL_MASK	(0xf << 8)
> #define FS_PP_DEST_SEL_MASK		(0xf << 12)
> #define FS_PP_DEST_SEL_IRT_PP			(0x3 << 12)
> #define FS_PP_ROT_DEST_SEL_MASK		(0xf << 16)
> #define FS_PRPENC_ROT_DEST_SEL_MASK	(0xf << 20)
> #define FS_PRP_DEST_SEL_MASK		(0xf << 24)
>
> struct idmac_link_reg_info {
> 	int chno;
> 	u32 reg;
> 	u32 mask;
> 	u32 val;
> };
>
> static const struct idmac_link_info idmac_link_info[] = {
> 	{
> 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
> 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
> 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
> 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
> 	}, {
>                 .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
>                           FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
>                 .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
>                           FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
> 	}, {
>                 .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
>                           FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
>                 .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
>                           FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
> 	},
> };

Hi Philipp,

Great ideas. I'll work on this for next version, or shall I let you? 
Should we create an
ipu-fsu.c ?

Steve


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support
@ 2016-07-28 23:40         ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-07-28 23:40 UTC (permalink / raw)
  To: Philipp Zabel, Steve Longerbeam
  Cc: plagnioj, tomi.valkeinen, dri-devel, linux-fbdev, linux-kernel



On 07/26/2016 03:06 AM, Philipp Zabel wrote:
> Am Dienstag, den 19.07.2016, 18:11 -0700 schrieb Steve Longerbeam:
>> Adds functions to link and unlink IDMAC source channels to sink
>> channels.
>>
>> So far the following links are supported:
>>
>> IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
>> PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
>> IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP
>>
>> More links can be added to the idmac_link_info[] array.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> This patch looks good to me, but the Frame Synchronisation Unit supports
> also linking the internal channels, not only the IDMAC channels.
>
> [...]
>> +++ b/drivers/gpu/ipu-v3/ipu-common.c
> [...]
>> +static const struct idmac_link_info idmac_link_info[] = {
>> +	{
>> +		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
>> +			  0, 4, 7 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
>> +			  0, 4, 1 },
>> +	}, {
>> +		.src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
>> +			  8, 4, 8 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
>> +			  4, 4, 1 },
>> +	}, {
>> +		.src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
>> +			  16, 4, 5 },
>> +		.sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
>> +			  12, 4, 3 },
>> +	},
>> +};
> How about adding new (internal) channel numbers for the CSI->VDI link
> and having something like:
>
> 	{
> 		.src =  { IPUV3_CHANNEL_CSI_DIRECT, IPU_FS_PROC_FLOW1,
> 			  FS_VDI_SRC_SEL_OFFSET, 2, 1 },
> 		.sink = { IPUV3_CHANNEL_VDI_CUR, 0, 0, 0 },
> 	},
>
> instead of ipu_set_vdi_src_mux? Then in addition to
>
> [...]
>> +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
> We could have a lower level
>
> ipu_fsu_link(int channel1, int channel2)
>
> which could be called like
>
> ipu_fsu_link(IPUV3_CHANNEL_CSI_DIRECT, IPUV3_CHANNEL_VDI_CUR);
>
> Come to think of it, could we replace shift,bits,sel with mask,value and
> add #defines for the FSU bit fields and values? I'm thinking:
>
> /* FS_PROC_FLOW1 */
> #define FS_PRPENC_ROT_SRC_SEL_MASK	(0xf << 0)
> #define FS_PRPENC_ROT_SRC_SEL_ENC		(0x7 << 0)
> #define FS_PRPVF_ROT_SRC_SEL_MASK	(0xf << 8)
> #define FS_PRPVF_ROT_SRC_SEL_VF			(0x8 << 8)
> #define FS_PP_SRC_SEL_MASK		(0xf << 12)
> #define FS_PP_ROT_SRC_SEL_MASK		(0xf << 16)
> #define FS_PP_ROT_SRC_SEL_PP			(0x5 << 16)
> #define FS_VDI1_SRC_SEL_MASK		(0x3 << 20)
> #define FS_VDI3_SRC_SEL_MASK		(0x3 << 20)
> #define FS_PRP_SRC_SEL_MASK		(0xf << 24)
> #define FS_VDI_SRC_SEL_MASK		(0x3 << 28)
>
> /* FS_PROC_FLOW2 */
> #define FS_PRP_ENC_DEST_SEL_MASK	(0xf << 0)
> #define FS_PRP_ENC_DEST_SEL_IRT_ENC		(0x1 << 0)
> #define FS_PRPVF_DEST_SEL_MASK		(0xf << 4)
> #define FS_PRPVF_DEST_SEL_IRT_VF		(0x1 << 4)
> #define FS_PRPVF_ROT_DEST_SEL_MASK	(0xf << 8)
> #define FS_PP_DEST_SEL_MASK		(0xf << 12)
> #define FS_PP_DEST_SEL_IRT_PP			(0x3 << 12)
> #define FS_PP_ROT_DEST_SEL_MASK		(0xf << 16)
> #define FS_PRPENC_ROT_DEST_SEL_MASK	(0xf << 20)
> #define FS_PRP_DEST_SEL_MASK		(0xf << 24)
>
> struct idmac_link_reg_info {
> 	int chno;
> 	u32 reg;
> 	u32 mask;
> 	u32 val;
> };
>
> static const struct idmac_link_info idmac_link_info[] = {
> 	{
> 		.src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW1,
> 			  FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
> 		.sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW2,
> 			  FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
> 	}, {
>                 .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW1,
>                           FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
>                 .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW2,
>                           FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
> 	}, {
>                 .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW1,
>                           FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
>                 .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW2,
>                           FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
> 	},
> };

Hi Philipp,

Great ideas. I'll work on this for next version, or shall I let you? 
Should we create an
ipu-fsu.c ?

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-28 23:09         ` Steve Longerbeam
  (?)
@ 2016-08-01  9:29           ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-08-01  9:29 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Steve Longerbeam, plagnioj, tomi.valkeinen, dri-devel,
	linux-fbdev, linux-kernel

Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
> > Now split the frame in half and suddenly pixel x' = 640 is the start of
> > a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> > sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> > the edge of the source image.
> 
> Here's where we part.
> 
> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
> those width/height dimensions, not the dimensions of the original images.
> So this is simply two separate 160x200 --> 640x800 conversions. The only
> difference from a true 160x200 --> 640x800 image conversion is that the DMA
> controller must be given the stride lengths of the original 320x200 and 
> 1280x800
> images.
> 
> The rsc for the 160x200 --> 640x800 conversions is
> 
> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
> 
> 
> So original horizontal position 640 is really x' = 0 of the second 
> conversion,
> which is sampled at x = 0 of the second conversion. And the pixel at x' 
> = 1279
> is really x' = 639 of the second conversion, which is sampled at x = 639 
> * 8192/32923
> = 158.98, which does not read over the edge of the source tile.

My bad, I somehow thought that the scaling factor is calculated per
image (as it IMHO should be), not just per tile.

Of course in that case you won't ever read over the edge, but on the
other hand the visual problems are worse because you underestimate the
scaling factor and introduce a sharp edge at the center: even if the
source pixel step per target pixel step is a fraction, between pixels
width/2-1 and width/2 there's always a whole source pixel step.

Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
respectively. Due to tiling they will be x = 15 and 16, introducing a
sharp seam in the otherwise blurry mess.

> > This problem gets worse if you start using arbitrary frame sizes and YUV
> > planar images and consider that tile start addresses are (currently)
> > limited to 8 byte boundaries, to the point that there are very visible
> > seams in the center of the image, depending on scaling factor and image
> > sizes.
> 
> Indeed there could be other parameters that would cause the resizer to
> read past the edge of the source tiles, I will need to try and find such 
> cases. 
> But not in the above case.

Ok.

> That said, I _have_ noticed seams, but I have always attributed them to the
> fact that we have a discontinuity in color-space conversion and/or resize
> interpolation at the boundary between tiles.
>
> I've also found that the seams are quite noticeable when rendered to a
> display overlay, but become significantly less pronounced if the images are
> converted to a back buffer, and then page-flipped to front buffer when the
> conversion (all tiles) completes.

I don't know what to make of this. Maybe it is a timing issue and what
you are actually seeing is tearing between tiles of different frames?

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-08-01  9:29           ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-08-01  9:29 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, linux-kernel, dri-devel, tomi.valkeinen,
	Steve Longerbeam, plagnioj

Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
> > Now split the frame in half and suddenly pixel x' = 640 is the start of
> > a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> > sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> > the edge of the source image.
> 
> Here's where we part.
> 
> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
> those width/height dimensions, not the dimensions of the original images.
> So this is simply two separate 160x200 --> 640x800 conversions. The only
> difference from a true 160x200 --> 640x800 image conversion is that the DMA
> controller must be given the stride lengths of the original 320x200 and 
> 1280x800
> images.
> 
> The rsc for the 160x200 --> 640x800 conversions is
> 
> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
> 
> 
> So original horizontal position 640 is really x' = 0 of the second 
> conversion,
> which is sampled at x = 0 of the second conversion. And the pixel at x' 
> = 1279
> is really x' = 639 of the second conversion, which is sampled at x = 639 
> * 8192/32923
> = 158.98, which does not read over the edge of the source tile.

My bad, I somehow thought that the scaling factor is calculated per
image (as it IMHO should be), not just per tile.

Of course in that case you won't ever read over the edge, but on the
other hand the visual problems are worse because you underestimate the
scaling factor and introduce a sharp edge at the center: even if the
source pixel step per target pixel step is a fraction, between pixels
width/2-1 and width/2 there's always a whole source pixel step.

Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
respectively. Due to tiling they will be x = 15 and 16, introducing a
sharp seam in the otherwise blurry mess.

> > This problem gets worse if you start using arbitrary frame sizes and YUV
> > planar images and consider that tile start addresses are (currently)
> > limited to 8 byte boundaries, to the point that there are very visible
> > seams in the center of the image, depending on scaling factor and image
> > sizes.
> 
> Indeed there could be other parameters that would cause the resizer to
> read past the edge of the source tiles, I will need to try and find such 
> cases. 
> But not in the above case.

Ok.

> That said, I _have_ noticed seams, but I have always attributed them to the
> fact that we have a discontinuity in color-space conversion and/or resize
> interpolation at the boundary between tiles.
>
> I've also found that the seams are quite noticeable when rendered to a
> display overlay, but become significantly less pronounced if the images are
> converted to a back buffer, and then page-flipped to front buffer when the
> conversion (all tiles) completes.

I don't know what to make of this. Maybe it is a timing issue and what
you are actually seeing is tearing between tiles of different frames?

regards
Philipp


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-08-01  9:29           ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-08-01  9:29 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, linux-kernel, dri-devel, tomi.valkeinen,
	Steve Longerbeam, plagnioj

Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
> > Now split the frame in half and suddenly pixel x' = 640 is the start of
> > a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> > sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> > the edge of the source image.
> 
> Here's where we part.
> 
> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
> those width/height dimensions, not the dimensions of the original images.
> So this is simply two separate 160x200 --> 640x800 conversions. The only
> difference from a true 160x200 --> 640x800 image conversion is that the DMA
> controller must be given the stride lengths of the original 320x200 and 
> 1280x800
> images.
> 
> The rsc for the 160x200 --> 640x800 conversions is
> 
> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
> 
> 
> So original horizontal position 640 is really x' = 0 of the second 
> conversion,
> which is sampled at x = 0 of the second conversion. And the pixel at x' 
> = 1279
> is really x' = 639 of the second conversion, which is sampled at x = 639 
> * 8192/32923
> = 158.98, which does not read over the edge of the source tile.

My bad, I somehow thought that the scaling factor is calculated per
image (as it IMHO should be), not just per tile.

Of course in that case you won't ever read over the edge, but on the
other hand the visual problems are worse because you underestimate the
scaling factor and introduce a sharp edge at the center: even if the
source pixel step per target pixel step is a fraction, between pixels
width/2-1 and width/2 there's always a whole source pixel step.

Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
respectively. Due to tiling they will be x = 15 and 16, introducing a
sharp seam in the otherwise blurry mess.

> > This problem gets worse if you start using arbitrary frame sizes and YUV
> > planar images and consider that tile start addresses are (currently)
> > limited to 8 byte boundaries, to the point that there are very visible
> > seams in the center of the image, depending on scaling factor and image
> > sizes.
> 
> Indeed there could be other parameters that would cause the resizer to
> read past the edge of the source tiles, I will need to try and find such 
> cases. 
> But not in the above case.

Ok.

> That said, I _have_ noticed seams, but I have always attributed them to the
> fact that we have a discontinuity in color-space conversion and/or resize
> interpolation at the boundary between tiles.
>
> I've also found that the seams are quite noticeable when rendered to a
> display overlay, but become significantly less pronounced if the images are
> converted to a back buffer, and then page-flipped to front buffer when the
> conversion (all tiles) completes.

I don't know what to make of this. Maybe it is a timing issue and what
you are actually seeing is tearing between tiles of different frames?

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-08-01  9:29           ` Philipp Zabel
  (?)
@ 2016-08-04  0:18             ` Steve Longerbeam
  -1 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-08-04  0:18 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Steve Longerbeam, plagnioj, tomi.valkeinen, dri-devel,
	linux-fbdev, linux-kernel

On 08/01/2016 02:29 AM, Philipp Zabel wrote:
> Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
>>> Now split the frame in half and suddenly pixel x' = 640 is the start of
>>> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
>>> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
>>> the edge of the source image.
>> Here's where we part.
>>
>> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
>> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
>> those width/height dimensions, not the dimensions of the original images.
>> So this is simply two separate 160x200 --> 640x800 conversions. The only
>> difference from a true 160x200 --> 640x800 image conversion is that the DMA
>> controller must be given the stride lengths of the original 320x200 and 
>> 1280x800
>> images.
>>
>> The rsc for the 160x200 --> 640x800 conversions is
>>
>> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
>>
>>
>> So original horizontal position 640 is really x' = 0 of the second 
>> conversion,
>> which is sampled at x = 0 of the second conversion. And the pixel at x' 
>> = 1279
>> is really x' = 639 of the second conversion, which is sampled at x = 639 
>> * 8192/32923
>> = 158.98, which does not read over the edge of the source tile.
> My bad, I somehow thought that the scaling factor is calculated per
> image (as it IMHO should be), not just per tile.
>
> Of course in that case you won't ever read over the edge, but on the
> other hand the visual problems are worse because you underestimate the
> scaling factor and introduce a sharp edge at the center: even if the
> source pixel step per target pixel step is a fraction, between pixels
> width/2-1 and width/2 there's always a whole source pixel step.
>
> Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
> source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
> respectively. Due to tiling they will be x = 15 and 16, introducing a
> sharp seam in the otherwise blurry mess.

I think you mean at x' = 539 and x' = 540.

But yes I agree. Due to tiling, at x' = 539, the input pixel is sampled at x = 15.
If the interpolation were to continue (no tiling), at x' = 540, the input pixel
would be sampled at (31/1079)*540 = 15.514. Instead, because of tiling,
there is a discontinuity in the interpolation (it is reset), beginning again at
x' = 0 (540), which is sampled at x = 0 (16).

The only way I can think of to resolve this problem is to add some width
to the output tiles such that the interpolation completes a full span between
input position w - 2 and w - 1. That is, add to w' until floor(F*w') increments
to the next whole integer, where F = (w-1)/(w'-1) is the scaling factor.

But that will likely cause the next tile DMA addrs to fail to fall on the IDMAC
8 byte alignment.


>
>
>> That said, I _have_ noticed seams, but I have always attributed them to the
>> fact that we have a discontinuity in color-space conversion and/or resize
>> interpolation at the boundary between tiles.
>>
>> I've also found that the seams are quite noticeable when rendered to a
>> display overlay, but become significantly less pronounced if the images are
>> converted to a back buffer, and then page-flipped to front buffer when the
>> conversion (all tiles) completes.
> I don't know what to make of this. Maybe it is a timing issue and what
> you are actually seeing is tearing between tiles of different frames?

Yes, that's always been my assumption, a scan-out contains a mix
of tiles from different frames, when page-flip is not used.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-08-04  0:18             ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-08-04  0:18 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Steve Longerbeam, plagnioj, tomi.valkeinen, dri-devel,
	linux-fbdev, linux-kernel

On 08/01/2016 02:29 AM, Philipp Zabel wrote:
> Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
>>> Now split the frame in half and suddenly pixel x' = 640 is the start of
>>> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
>>> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
>>> the edge of the source image.
>> Here's where we part.
>>
>> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
>> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
>> those width/height dimensions, not the dimensions of the original images.
>> So this is simply two separate 160x200 --> 640x800 conversions. The only
>> difference from a true 160x200 --> 640x800 image conversion is that the DMA
>> controller must be given the stride lengths of the original 320x200 and 
>> 1280x800
>> images.
>>
>> The rsc for the 160x200 --> 640x800 conversions is
>>
>> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
>>
>>
>> So original horizontal position 640 is really x' = 0 of the second 
>> conversion,
>> which is sampled at x = 0 of the second conversion. And the pixel at x' 
>> = 1279
>> is really x' = 639 of the second conversion, which is sampled at x = 639 
>> * 8192/32923
>> = 158.98, which does not read over the edge of the source tile.
> My bad, I somehow thought that the scaling factor is calculated per
> image (as it IMHO should be), not just per tile.
>
> Of course in that case you won't ever read over the edge, but on the
> other hand the visual problems are worse because you underestimate the
> scaling factor and introduce a sharp edge at the center: even if the
> source pixel step per target pixel step is a fraction, between pixels
> width/2-1 and width/2 there's always a whole source pixel step.
>
> Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
> source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
> respectively. Due to tiling they will be x = 15 and 16, introducing a
> sharp seam in the otherwise blurry mess.

I think you mean at x' = 539 and x' = 540.

But yes I agree. Due to tiling, at x' = 539, the input pixel is sampled at x = 15.
If the interpolation were to continue (no tiling), at x' = 540, the input pixel
would be sampled at (31/1079)*540 = 15.514. Instead, because of tiling,
there is a discontinuity in the interpolation (it is reset), beginning again at
x' = 0 (540), which is sampled at x = 0 (16).

The only way I can think of to resolve this problem is to add some width
to the output tiles such that the interpolation completes a full span between
input position w - 2 and w - 1. That is, add to w' until floor(F*w') increments
to the next whole integer, where F = (w-1)/(w'-1) is the scaling factor.

But that will likely cause the next tile DMA addrs to fail to fall on the IDMAC
8 byte alignment.


>
>
>> That said, I _have_ noticed seams, but I have always attributed them to the
>> fact that we have a discontinuity in color-space conversion and/or resize
>> interpolation at the boundary between tiles.
>>
>> I've also found that the seams are quite noticeable when rendered to a
>> display overlay, but become significantly less pronounced if the images are
>> converted to a back buffer, and then page-flipped to front buffer when the
>> conversion (all tiles) completes.
> I don't know what to make of this. Maybe it is a timing issue and what
> you are actually seeing is tearing between tiles of different frames?

Yes, that's always been my assumption, a scan-out contains a mix
of tiles from different frames, when page-flip is not used.

Steve


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-08-04  0:18             ` Steve Longerbeam
  0 siblings, 0 replies; 119+ messages in thread
From: Steve Longerbeam @ 2016-08-04  0:18 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Steve Longerbeam, plagnioj, tomi.valkeinen, dri-devel,
	linux-fbdev, linux-kernel

On 08/01/2016 02:29 AM, Philipp Zabel wrote:
> Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
>>> Now split the frame in half and suddenly pixel x' = 640 is the start of
>>> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
>>> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
>>> the edge of the source image.
>> Here's where we part.
>>
>> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
>> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
>> those width/height dimensions, not the dimensions of the original images.
>> So this is simply two separate 160x200 --> 640x800 conversions. The only
>> difference from a true 160x200 --> 640x800 image conversion is that the DMA
>> controller must be given the stride lengths of the original 320x200 and 
>> 1280x800
>> images.
>>
>> The rsc for the 160x200 --> 640x800 conversions is
>>
>> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
>>
>>
>> So original horizontal position 640 is really x' = 0 of the second 
>> conversion,
>> which is sampled at x = 0 of the second conversion. And the pixel at x' 
>> = 1279
>> is really x' = 639 of the second conversion, which is sampled at x = 639 
>> * 8192/32923
>> = 158.98, which does not read over the edge of the source tile.
> My bad, I somehow thought that the scaling factor is calculated per
> image (as it IMHO should be), not just per tile.
>
> Of course in that case you won't ever read over the edge, but on the
> other hand the visual problems are worse because you underestimate the
> scaling factor and introduce a sharp edge at the center: even if the
> source pixel step per target pixel step is a fraction, between pixels
> width/2-1 and width/2 there's always a whole source pixel step.
>
> Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
> source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
> respectively. Due to tiling they will be x = 15 and 16, introducing a
> sharp seam in the otherwise blurry mess.

I think you mean at x' = 539 and x' = 540.

But yes I agree. Due to tiling, at x' = 539, the input pixel is sampled at x = 15.
If the interpolation were to continue (no tiling), at x' = 540, the input pixel
would be sampled at (31/1079)*540 = 15.514. Instead, because of tiling,
there is a discontinuity in the interpolation (it is reset), beginning again at
x' = 0 (540), which is sampled at x = 0 (16).

The only way I can think of to resolve this problem is to add some width
to the output tiles such that the interpolation completes a full span between
input position w - 2 and w - 1. That is, add to w' until floor(F*w') increments
to the next whole integer, where F = (w-1)/(w'-1) is the scaling factor.

But that will likely cause the next tile DMA addrs to fail to fall on the IDMAC
8 byte alignment.


>
>
>> That said, I _have_ noticed seams, but I have always attributed them to the
>> fact that we have a discontinuity in color-space conversion and/or resize
>> interpolation at the boundary between tiles.
>>
>> I've also found that the seams are quite noticeable when rendered to a
>> display overlay, but become significantly less pronounced if the images are
>> converted to a back buffer, and then page-flipped to front buffer when the
>> conversion (all tiles) completes.
> I don't know what to make of this. Maybe it is a timing issue and what
> you are actually seeing is tearing between tiles of different frames?

Yes, that's always been my assumption, a scan-out contains a mix
of tiles from different frames, when page-flip is not used.

Steve

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-08-04  0:18             ` Steve Longerbeam
  (?)
@ 2016-08-12 14:56               ` Philipp Zabel
  -1 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-08-12 14:56 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Steve Longerbeam, plagnioj, tomi.valkeinen, dri-devel,
	linux-fbdev, linux-kernel

Am Mittwoch, den 03.08.2016, 17:18 -0700 schrieb Steve Longerbeam:
> On 08/01/2016 02:29 AM, Philipp Zabel wrote:
> > Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
> >>> Now split the frame in half and suddenly pixel x' = 640 is the start of
> >>> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> >>> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> >>> the edge of the source image.
> >> Here's where we part.
> >>
> >> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
> >> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
> >> those width/height dimensions, not the dimensions of the original images.
> >> So this is simply two separate 160x200 --> 640x800 conversions. The only
> >> difference from a true 160x200 --> 640x800 image conversion is that the DMA
> >> controller must be given the stride lengths of the original 320x200 and 
> >> 1280x800
> >> images.
> >>
> >> The rsc for the 160x200 --> 640x800 conversions is
> >>
> >> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
> >>
> >>
> >> So original horizontal position 640 is really x' = 0 of the second 
> >> conversion,
> >> which is sampled at x = 0 of the second conversion. And the pixel at x' 
> >> = 1279
> >> is really x' = 639 of the second conversion, which is sampled at x = 639 
> >> * 8192/32923
> >> = 158.98, which does not read over the edge of the source tile.
> > My bad, I somehow thought that the scaling factor is calculated per
> > image (as it IMHO should be), not just per tile.
> >
> > Of course in that case you won't ever read over the edge, but on the
> > other hand the visual problems are worse because you underestimate the
> > scaling factor and introduce a sharp edge at the center: even if the
> > source pixel step per target pixel step is a fraction, between pixels
> > width/2-1 and width/2 there's always a whole source pixel step.
> >
> > Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
> > source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
> > respectively. Due to tiling they will be x = 15 and 16, introducing a
> > sharp seam in the otherwise blurry mess.
> 
> I think you mean at x' = 539 and x' = 540.
> 
> But yes I agree. Due to tiling, at x' = 539, the input pixel is sampled at x = 15.
> If the interpolation were to contnue (no tiling), at x' = 540, the input pixel
> would be sampled at (31/1079)*540 = 15.514. Instead, because of tiling,
> there is a discontinuity in the interpolation (it is reset), beginning again at
> x' = 0 (540), which is sampled at x = 0 (16).
> 
> The only way I can think of to resolve this problem is to add some width
> to the output tiles such that the interpolation completes a full span between
> input position w - 2 and w - 1. That is, add to w' until floor(F*w') increments
> to the next whole integer, where F = (w-1)/(w'-1) is the scaling factor.
> 
> But that will likely cause the next tile DMA addrs to fail to fall on the IDMAC
> 8 byte alignment.

I always wanted to have a look at the scroll feature, maybe SX can be
used to start at odd pixels?

regards
Philipp

^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-08-12 14:56               ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-08-12 14:56 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, linux-kernel, dri-devel, tomi.valkeinen,
	Steve Longerbeam, plagnioj

Am Mittwoch, den 03.08.2016, 17:18 -0700 schrieb Steve Longerbeam:
> On 08/01/2016 02:29 AM, Philipp Zabel wrote:
> > Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
> >>> Now split the frame in half and suddenly pixel x' = 640 is the start of
> >>> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> >>> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> >>> the edge of the source image.
> >> Here's where we part.
> >>
> >> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
> >> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
> >> those width/height dimensions, not the dimensions of the original images.
> >> So this is simply two separate 160x200 --> 640x800 conversions. The only
> >> difference from a true 160x200 --> 640x800 image conversion is that the DMA
> >> controller must be given the stride lengths of the original 320x200 and 
> >> 1280x800
> >> images.
> >>
> >> The rsc for the 160x200 --> 640x800 conversions is
> >>
> >> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
> >>
> >>
> >> So original horizontal position 640 is really x' = 0 of the second 
> >> conversion,
> >> which is sampled at x = 0 of the second conversion. And the pixel at x' 
> >> = 1279
> >> is really x' = 639 of the second conversion, which is sampled at x = 639 
> >> * 8192/32923
> >> = 158.98, which does not read over the edge of the source tile.
> > My bad, I somehow thought that the scaling factor is calculated per
> > image (as it IMHO should be), not just per tile.
> >
> > Of course in that case you won't ever read over the edge, but on the
> > other hand the visual problems are worse because you underestimate the
> > scaling factor and introduce a sharp edge at the center: even if the
> > source pixel step per target pixel step is a fraction, between pixels
> > width/2-1 and width/2 there's always a whole source pixel step.
> >
> > Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
> > source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
> > respectively. Due to tiling they will be x = 15 and 16, introducing a
> > sharp seam in the otherwise blurry mess.
> 
> I think you mean at x' = 539 and x' = 540.
> 
> But yes I agree. Due to tiling, at x' = 539, the input pixel is sampled at x = 15.
> If the interpolation were to contnue (no tiling), at x' = 540, the input pixel
> would be sampled at (31/1079)*540 = 15.514. Instead, because of tiling,
> there is a discontinuity in the interpolation (it is reset), beginning again at
> x' = 0 (540), which is sampled at x = 0 (16).
> 
> The only way I can think of to resolve this problem is to add some width
> to the output tiles such that the interpolation completes a full span between
> input position w - 2 and w - 1. That is, add to w' until floor(F*w') increments
> to the next whole integer, where F = (w-1)/(w'-1) is the scaling factor.
> 
> But that will likely cause the next tile DMA addrs to fail to fall on the IDMAC
> 8 byte alignment.

I always wanted to have a look at the scroll feature, maybe SX can be
used to start at odd pixels?

regards
Philipp


^ permalink raw reply	[flat|nested] 119+ messages in thread

* Re: [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling
@ 2016-08-12 14:56               ` Philipp Zabel
  0 siblings, 0 replies; 119+ messages in thread
From: Philipp Zabel @ 2016-08-12 14:56 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-fbdev, linux-kernel, dri-devel, tomi.valkeinen,
	Steve Longerbeam, plagnioj

Am Mittwoch, den 03.08.2016, 17:18 -0700 schrieb Steve Longerbeam:
> On 08/01/2016 02:29 AM, Philipp Zabel wrote:
> > Am Donnerstag, den 28.07.2016, 16:09 -0700 schrieb Steve Longerbeam:
> >>> Now split the frame in half and suddenly pixel x' = 640 is the start of
> >>> a new tile, so it is sampled at x = 160, and pixel x' = 1279 will be
> >>> sampled at x = 160 + (1279 - 640) * 8192/32846. = 319.37, reading over
> >>> the edge of the source image.
> >> Here's where we part.
> >>
> >> The 320x200 --> 1280x800 conversion is split into two 160x200 -->
> >> 640x800 conversions. The DMA controller and ipu_ic_task_init() are given
> >> those width/height dimensions, not the dimensions of the original images.
> >> So this is simply two separate 160x200 --> 640x800 conversions. The only
> >> difference from a true 160x200 --> 640x800 image conversion is that the DMA
> >> controller must be given the stride lengths of the original 320x200 and 
> >> 1280x800
> >> images.
> >>
> >> The rsc for the 160x200 --> 640x800 conversions is
> >>
> >> x = x' * (160-1)/(640-1) = x' * 8192/rsc, so rsc = 32923
> >>
> >>
> >> So original horizontal position 640 is really x' = 0 of the second 
> >> conversion,
> >> which is sampled at x = 0 of the second conversion. And the pixel at x' 
> >> = 1279
> >> is really x' = 639 of the second conversion, which is sampled at x = 639 
> >> * 8192/32923
> >> = 158.98, which does not read over the edge of the source tile.
> > My bad, I somehow thought that the scaling factor is calculated per
> > image (as it IMHO should be), not just per tile.
> >
> > Of course in that case you won't ever read over the edge, but on the
> > other hand the visual problems are worse because you underestimate the
> > scaling factor and introduce a sharp edge at the center: even if the
> > source pixel step per target pixel step is a fraction, between pixels
> > width/2-1 and width/2 there's always a whole source pixel step.
> >
> > Take the extreme example of scaling 32x32 to 1080x1080 pixels. The ideal
> > source pixels for x' = 519 and 520 should be x = 14.911 and 14.939,
> > respectively. Due to tiling they will be x = 15 and 16, introducing a
> > sharp seam in the otherwise blurry mess.
> 
> I think you mean at x' = 539 and x' = 540.
> 
> But yes I agree. Due to tiling, at x' = 539, the input pixel is sampled at x = 15.
> If the interpolation were to contnue (no tiling), at x' = 540, the input pixel
> would be sampled at (31/1079)*540 = 15.514. Instead, because of tiling,
> there is a discontinuity in the interpolation (it is reset), beginning again at
> x' = 0 (540), which is sampled at x = 0 (16).
> 
> The only way I can think of to resolve this problem is to add some width
> to the output tiles such that the interpolation completes a full span between
> input position w - 2 and w - 1. That is, add to w' until floor(F*w') increments
> to the next whole integer, where F = (w-1)/(w'-1) is the scaling factor.
> 
> But that will likely cause the next tile DMA addrs to fail to fall on the IDMAC
> 8 byte alignment.

I always wanted to have a look at the scroll feature, maybe SX can be
used to start at odd pixels?

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 119+ messages in thread

end of thread, other threads:[~2016-08-12 14:56 UTC | newest]

Thread overview: 119+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-07 23:03 [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Steve Longerbeam
2016-07-07 23:03 ` [PATCH 01/16] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
2016-07-11  0:02   ` Paul Gortmaker
2016-07-15 22:35     ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 02/16] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2016-07-08 17:34   ` Philipp Zabel
2016-07-08 17:34     ` Philipp Zabel
2016-07-13 22:54     ` Steve Longerbeam
2016-07-13 22:54       ` Steve Longerbeam
2016-07-15 12:45       ` Philipp Zabel
2016-07-07 23:03 ` [PATCH 03/16] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2016-07-07 23:03 ` [PATCH 04/16] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
2016-07-15 12:45   ` Philipp Zabel
2016-07-07 23:03 ` [PATCH 05/16] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2016-07-08 17:34   ` Philipp Zabel
2016-07-08 17:34     ` Philipp Zabel
2016-07-13 22:55     ` Steve Longerbeam
2016-07-13 22:55       ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 06/16] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
2016-07-15 12:48   ` Philipp Zabel
2016-07-15 12:48     ` Philipp Zabel
2016-07-15 21:33     ` Steve Longerbeam
2016-07-15 21:33       ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 07/16] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
2016-07-15 12:45   ` Philipp Zabel
2016-07-15 12:45     ` Philipp Zabel
2016-07-15 21:34     ` Steve Longerbeam
2016-07-15 21:34       ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 08/16] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
2016-07-15 12:49   ` Philipp Zabel
2016-07-15 21:45     ` Steve Longerbeam
2016-07-15 21:45       ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 09/16] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
2016-07-15 12:45   ` Philipp Zabel
2016-07-15 21:57     ` Steve Longerbeam
2016-07-15 21:57       ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 10/16] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
2016-07-07 23:03 ` [PATCH 11/16] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
2016-07-08 17:38   ` Philipp Zabel
2016-07-08 17:38     ` Philipp Zabel
2016-07-07 23:03 ` [PATCH 12/16] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
2016-07-08 17:34   ` Philipp Zabel
2016-07-08 17:34     ` Philipp Zabel
2016-07-10 16:33     ` Steve Longerbeam
2016-07-10 16:33       ` Steve Longerbeam
2016-07-13 23:02       ` Steve Longerbeam
2016-07-13 23:02         ` Steve Longerbeam
2016-07-15 12:58         ` Philipp Zabel
2016-07-15 12:58           ` Philipp Zabel
2016-07-15 23:09           ` Steve Longerbeam
2016-07-15 23:09             ` Steve Longerbeam
2016-07-16 20:24             ` Steve Longerbeam
2016-07-16 20:24               ` Steve Longerbeam
2016-07-19 13:32               ` Philipp Zabel
2016-07-19 13:32                 ` Philipp Zabel
2016-07-20  0:07                 ` Steve Longerbeam
2016-07-20  0:07                   ` Steve Longerbeam
2016-07-07 23:03 ` [PATCH 13/16] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
2016-07-07 23:03 ` [PATCH 14/16] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
2016-07-07 23:03 ` [PATCH 15/16] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
2016-07-07 23:03 ` [PATCH 16/16] gpu: ipu-v3: rename CSI client device Steve Longerbeam
2016-07-08 17:34 ` [PATCH 00/16] IPUv3 prep for i.MX5/6 v4l2 staging drivers Philipp Zabel
2016-07-08 17:34   ` Philipp Zabel
2016-07-20  1:10 ` [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2 Steve Longerbeam
2016-07-20  1:10   ` Steve Longerbeam
2016-07-20  1:10   ` [PATCH v2 01/13] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
2016-07-20  1:10     ` Steve Longerbeam
2016-07-25  6:06     ` kbuild test robot
2016-07-25  6:06       ` kbuild test robot
2016-07-25  6:06       ` kbuild test robot
2016-07-26 10:06     ` Philipp Zabel
2016-07-26 10:06       ` Philipp Zabel
2016-07-20  1:11   ` [PATCH v2 02/13] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 03/13] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 04/13] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 05/13] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-26 10:06     ` Philipp Zabel
2016-07-26 10:06       ` Philipp Zabel
2016-07-26 10:06       ` Philipp Zabel
2016-07-28 23:40       ` Steve Longerbeam
2016-07-28 23:40         ` Steve Longerbeam
2016-07-28 23:40         ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 06/13] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 07/13] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 08/13] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 09/13] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 10/13] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 11/13] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-26 10:08     ` Philipp Zabel
2016-07-26 10:08       ` Philipp Zabel
2016-07-28 23:09       ` Steve Longerbeam
2016-07-28 23:09         ` Steve Longerbeam
2016-07-28 23:09         ` Steve Longerbeam
2016-08-01  9:29         ` Philipp Zabel
2016-08-01  9:29           ` Philipp Zabel
2016-08-01  9:29           ` Philipp Zabel
2016-08-04  0:18           ` Steve Longerbeam
2016-08-04  0:18             ` Steve Longerbeam
2016-08-04  0:18             ` Steve Longerbeam
2016-08-12 14:56             ` Philipp Zabel
2016-08-12 14:56               ` Philipp Zabel
2016-08-12 14:56               ` Philipp Zabel
2016-07-20  1:11   ` [PATCH v2 12/13] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-20  1:11   ` [PATCH v2 13/13] gpu: ipu-v3: rename CSI client device Steve Longerbeam
2016-07-20  1:11     ` Steve Longerbeam
2016-07-26 10:06   ` [PATCH v2 00/13] IPUv3 prep for i.MX5/6 v4l2 staging drivers, v2 Philipp Zabel
2016-07-26 10:06     ` Philipp Zabel
2016-07-26 10:06     ` Philipp Zabel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.