All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] i.MX5/6 mem2mem scaler
@ 2015-03-18 10:22 Philipp Zabel
  2015-03-18 10:22   ` Philipp Zabel
                   ` (5 more replies)
  0 siblings, 6 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	kernel, Philipp Zabel

Hi,

this series uses the IPU IC post-processing task, to implement
a mem2mem device for scaling and colorspace conversion. The first
version had a fixup applied to the wrong patch.

Changes since v1:
 - Removed deinterlacer support left-overs

regards
Philipp

Philipp Zabel (3):
  gpu: ipu-v3: Add missing IDMAC channel names
  gpu: ipu-v3: Add mem2mem image conversion support to IC
  gpu: ipu-v3: Register scaler platform device

Sascha Hauer (2):
  [media] imx-ipu: Add ipu media common code
  [media] imx-ipu: Add i.MX IPUv3 scaler driver

 drivers/gpu/ipu-v3/ipu-common.c             |   2 +
 drivers/gpu/ipu-v3/ipu-ic.c                 | 787 ++++++++++++++++++++++++-
 drivers/media/platform/Kconfig              |   2 +
 drivers/media/platform/Makefile             |   1 +
 drivers/media/platform/imx/Kconfig          |  11 +
 drivers/media/platform/imx/Makefile         |   2 +
 drivers/media/platform/imx/imx-ipu-scaler.c | 869 ++++++++++++++++++++++++++++
 drivers/media/platform/imx/imx-ipu.c        | 313 ++++++++++
 drivers/media/platform/imx/imx-ipu.h        |  36 ++
 include/video/imx-ipu-v3.h                  |  49 +-
 10 files changed, 2055 insertions(+), 17 deletions(-)
 create mode 100644 drivers/media/platform/imx/Kconfig
 create mode 100644 drivers/media/platform/imx/Makefile
 create mode 100644 drivers/media/platform/imx/imx-ipu-scaler.c
 create mode 100644 drivers/media/platform/imx/imx-ipu.c
 create mode 100644 drivers/media/platform/imx/imx-ipu.h

-- 
2.1.4


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

* [PATCH v2 1/5] gpu: ipu-v3: Add missing IDMAC channel names
  2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
@ 2015-03-18 10:22   ` Philipp Zabel
  2015-03-18 10:22   ` Philipp Zabel
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	kernel, Philipp Zabel

This patch adds the remaining missing IDMAC channel names: all VDIC channels
for deinterlacing and combining, the separate alpha channels for the MEM->IC
and MEM->DC ASYNC channels, and the DC read / command / output mask channels.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 include/video/imx-ipu-v3.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 73390c1..459508e 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -96,20 +96,34 @@ 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_VDI_MEM_RECENT		13
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
 #define IPUV3_CHANNEL_G_MEM_IC_PP		15
+#define IPUV3_CHANNEL_G_MEM_IC_PRP_VF_ALPHA	17
+#define IPUV3_CHANNEL_G_MEM_IC_PP_ALPHA		18
+#define IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB_ALPHA	19
 #define IPUV3_CHANNEL_IC_PRP_ENC_MEM		20
 #define IPUV3_CHANNEL_IC_PRP_VF_MEM		21
 #define IPUV3_CHANNEL_IC_PP_MEM			22
 #define IPUV3_CHANNEL_MEM_BG_SYNC		23
 #define IPUV3_CHANNEL_MEM_BG_ASYNC		24
+#define IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB	25
+#define IPUV3_CHANNEL_MEM_VDI_PLANE3_COMB	26
 #define IPUV3_CHANNEL_MEM_FG_SYNC		27
 #define IPUV3_CHANNEL_MEM_DC_SYNC		28
 #define IPUV3_CHANNEL_MEM_FG_ASYNC		29
 #define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA		31
+#define IPUV3_CHANNEL_MEM_FG_ASYNC_ALPHA	33
+#define IPUV3_CHANNEL_DC_MEM_READ		40
 #define IPUV3_CHANNEL_MEM_DC_ASYNC		41
+#define IPUV3_CHANNEL_MEM_DC_COMMAND		42
+#define IPUV3_CHANNEL_MEM_DC_COMMAND2		43
+#define IPUV3_CHANNEL_MEM_DC_OUTPUT_MASK	44
 #define IPUV3_CHANNEL_MEM_ROT_ENC		45
 #define IPUV3_CHANNEL_MEM_ROT_VF		46
 #define IPUV3_CHANNEL_MEM_ROT_PP		47
@@ -117,6 +131,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_CHANNEL_MEM_BG_ASYNC_ALPHA	52
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
-- 
2.1.4


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

* [PATCH v2 1/5] gpu: ipu-v3: Add missing IDMAC channel names
@ 2015-03-18 10:22   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, dri-devel, Hans Verkuil, kernel,
	Steve Longerbeam, Jean-Michel Hautbois

This patch adds the remaining missing IDMAC channel names: all VDIC channels
for deinterlacing and combining, the separate alpha channels for the MEM->IC
and MEM->DC ASYNC channels, and the DC read / command / output mask channels.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 include/video/imx-ipu-v3.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 73390c1..459508e 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -96,20 +96,34 @@ 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_VDI_MEM_RECENT		13
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
 #define IPUV3_CHANNEL_G_MEM_IC_PP		15
+#define IPUV3_CHANNEL_G_MEM_IC_PRP_VF_ALPHA	17
+#define IPUV3_CHANNEL_G_MEM_IC_PP_ALPHA		18
+#define IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB_ALPHA	19
 #define IPUV3_CHANNEL_IC_PRP_ENC_MEM		20
 #define IPUV3_CHANNEL_IC_PRP_VF_MEM		21
 #define IPUV3_CHANNEL_IC_PP_MEM			22
 #define IPUV3_CHANNEL_MEM_BG_SYNC		23
 #define IPUV3_CHANNEL_MEM_BG_ASYNC		24
+#define IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB	25
+#define IPUV3_CHANNEL_MEM_VDI_PLANE3_COMB	26
 #define IPUV3_CHANNEL_MEM_FG_SYNC		27
 #define IPUV3_CHANNEL_MEM_DC_SYNC		28
 #define IPUV3_CHANNEL_MEM_FG_ASYNC		29
 #define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA		31
+#define IPUV3_CHANNEL_MEM_FG_ASYNC_ALPHA	33
+#define IPUV3_CHANNEL_DC_MEM_READ		40
 #define IPUV3_CHANNEL_MEM_DC_ASYNC		41
+#define IPUV3_CHANNEL_MEM_DC_COMMAND		42
+#define IPUV3_CHANNEL_MEM_DC_COMMAND2		43
+#define IPUV3_CHANNEL_MEM_DC_OUTPUT_MASK	44
 #define IPUV3_CHANNEL_MEM_ROT_ENC		45
 #define IPUV3_CHANNEL_MEM_ROT_VF		46
 #define IPUV3_CHANNEL_MEM_ROT_PP		47
@@ -117,6 +131,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_CHANNEL_MEM_BG_ASYNC_ALPHA	52
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
-- 
2.1.4

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

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

* [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
@ 2015-03-18 10:22   ` Philipp Zabel
  2015-03-18 10:22   ` Philipp Zabel
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	kernel, Philipp Zabel, Sascha Hauer, Lucas Stach

This patch adds support for mem2mem scaling and colorspace conversion
using the IC module's post-processing task.

Scaling images larger than 1024x1024 is supported by tiling over multiple
IC scaling runs. Since the IDMAC and IC units have interesting and different
alignment limitations for buffer base addresses (left edges) and burst size
(row lengths), depending on input and output pixel formats, the tile rectangles
and scaling coefficients are chosen to minimize distortion. Due to possible
overlap, the tiles have to be rendered right to left and bottom to top.
Up to 7 pixels (depending on frame sizes and scaling factor) have to be
available after the end of the frame if the width is not burst size aligned.
The tiling code has a parameter to optionally round frame sizes up or down
and avoid overdraw in compositing scenarios.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v1:
 - Removed deinterlacer support left-overs
---
 drivers/gpu/ipu-v3/ipu-ic.c | 787 +++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |  34 +-
 2 files changed, 804 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index ad75588..984f68f 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/spinlock.h>
 #include <linux/bitrev.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/err.h>
 #include "ipu-prv.h"
@@ -96,6 +97,15 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	u8 in;
+	u8 out;
+	u8 rot_in;
+	u8 rot_out;
+	u8 in_prev;
+	u8 in_next;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +148,53 @@ 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] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.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_VDI_CUR,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.in_prev = IPUV3_CHANNEL_MEM_VDI_PREV,
+		.in_next = 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 image_convert_ctx {
+	void (*complete)(void *ctx, int err);
+	void *complete_context;
+
+	struct list_head list;
+	struct ipu_image in;
+	struct ipu_image in_n;
+	struct ipu_image in_p;
+	struct ipu_image out;
+
+	void *freep;
+
+	bool rotate:1;
+
+	u32 rsc;
+};
+
 struct ipu_ic_priv;
 
 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;
@@ -152,6 +203,19 @@ struct ipu_ic {
 	bool in_use;
 
 	struct ipu_ic_priv *priv;
+
+	struct ipuv3_channel *input_channel_p;
+	struct ipuv3_channel *input_channel;
+	struct ipuv3_channel *input_channel_n;
+	struct ipuv3_channel *output_channel;
+	struct ipuv3_channel *rotation_input_channel;
+	struct ipuv3_channel *rotation_output_channel;
+
+	struct list_head image_list;
+
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+	struct completion complete;
 };
 
 struct ipu_ic_priv {
@@ -168,7 +232,8 @@ static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset)
 	return readl(ic->priv->base + offset);
 }
 
-static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset)
+static inline void ipu_ic_write(struct ipu_ic *ic, u32 value,
+				unsigned offset)
 {
 	writel(value, ic->priv->base + offset);
 }
@@ -446,32 +511,35 @@ int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
 		     enum ipu_color_space in_cs,
-		     enum ipu_color_space out_cs)
+		     enum ipu_color_space out_cs,
+		     u32 rsc)
 {
 	struct ipu_ic_priv *priv = ic->priv;
-	u32 reg, downsize_coeff, resize_coeff;
+	u32 downsize_coeff, resize_coeff;
 	unsigned long flags;
 	int ret = 0;
 
-	/* Setup vertical resizing */
-	ret = calc_resize_coeffs(ic, in_height, out_height,
-				 &resize_coeff, &downsize_coeff);
-	if (ret)
-		return ret;
+	if (!rsc) {
+		/* Setup vertical resizing */
+		ret = calc_resize_coeffs(ic, in_height, out_height,
+					 &resize_coeff, &downsize_coeff);
+		if (ret)
+			return ret;
 
-	reg = (downsize_coeff << 30) | (resize_coeff << 16);
+		rsc = (downsize_coeff << 30) | (resize_coeff << 16);
 
-	/* Setup horizontal resizing */
-	ret = calc_resize_coeffs(ic, in_width, out_width,
-				 &resize_coeff, &downsize_coeff);
-	if (ret)
-		return ret;
+		/* Setup horizontal resizing */
+		ret = calc_resize_coeffs(ic, in_width, out_width,
+					 &resize_coeff, &downsize_coeff);
+		if (ret)
+			return ret;
 
-	reg |= (downsize_coeff << 14) | resize_coeff;
+		rsc |= (downsize_coeff << 14) | resize_coeff;
+	}
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	ipu_ic_write(ic, reg, ic->reg->rsc);
+	ipu_ic_write(ic, rsc, ic->reg->rsc);
 
 	/* Setup color space conversion */
 	ic->in_cs = in_cs;
@@ -629,6 +697,675 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+static struct image_convert_ctx *ipu_image_convert_next(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct ipuv3_channel *ch_in = ic->input_channel;
+	struct ipuv3_channel *ch_out = ic->output_channel;
+	struct image_convert_ctx *ctx;
+	struct ipu_image *in_p, *in, *in_n;
+	struct ipu_image *out;
+	int ret;
+	unsigned long flags;
+	unsigned int inburst, outburst;
+	unsigned int in_height;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (list_empty(&ic->image_list)) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return NULL;
+	}
+
+	ctx = list_first_entry(&ic->image_list, struct image_convert_ctx, list);
+
+	list_del(&ctx->list);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	in_p = &ctx->in_p;
+	in = &ctx->in;
+	in_n = &ctx->in_n;
+	out = &ctx->out;
+
+	ipu_cpmem_zero(ch_in);
+	ipu_cpmem_zero(ch_out);
+
+	inburst = in->rect.width & 0xf ? 8 : 16;
+	outburst = out->rect.width & 0xf ? 8 : 16;
+
+	ipu_ic_enable(ic);
+
+	ipu_ic_task_idma_init(ic, ic->input_channel, in->rect.width,
+			      in->rect.height, inburst, IPU_ROTATE_NONE);
+	ipu_ic_task_idma_init(ic, ic->output_channel, out->rect.width,
+			      out->rect.height, outburst, IPU_ROTATE_NONE);
+
+	ipu_cpmem_set_image(ch_in, &ctx->in);
+	ipu_cpmem_set_image(ch_out, &ctx->out);
+
+	ipu_cpmem_set_burstsize(ch_in, inburst);
+	ipu_cpmem_set_burstsize(ch_out, outburst);
+
+	in_height = in->rect.height;
+
+	dev_dbg(priv->ipu->dev, "%s: %dx%d(%dx%d@%d,%d) -> %dx%d(%dx%d@%d,%d)\n",
+		__func__, in->pix.width, in->pix.height,
+		in->rect.width, in->rect.height, in->rect.left, in->rect.top,
+		out->pix.width, out->pix.height,
+		out->rect.width, out->rect.height,
+		out->rect.left, out->rect.top);
+
+	dev_dbg(priv->ipu->dev,
+		"%s: hscale: >>%d, *8192/%d vscale: >>%d, *8192/%d\n",
+		__func__, (ctx->rsc >> 14) & 0x3, (ctx->rsc & 0x3fff),
+		ctx->rsc >> 30, (ctx->rsc >> 16) & 0x3fff);
+
+	ret = ipu_ic_task_init(ic, in->rect.width, in_height,
+			out->rect.width, out->rect.height,
+			ipu_pixelformat_to_colorspace(in->pix.pixelformat),
+			ipu_pixelformat_to_colorspace(out->pix.pixelformat),
+			ctx->rsc);
+	if (ret) {
+		ipu_ic_disable(ic);
+		return ERR_PTR(ret);
+	}
+
+	ipu_idmac_enable_channel(ic->input_channel);
+	ipu_idmac_enable_channel(ic->output_channel);
+
+	ipu_ic_task_enable(ic);
+
+	ipu_idmac_select_buffer(ic->input_channel, 0);
+	ipu_idmac_select_buffer(ic->output_channel, 0);
+
+	return ctx;
+}
+
+static void ipu_image_convert_work(struct work_struct *work)
+{
+	struct ipu_ic *ic = container_of(work, struct ipu_ic, work);
+	struct image_convert_ctx *ctx;
+	int ret;
+
+	while (1) {
+		int task_error = 0;
+
+		ctx = ipu_image_convert_next(ic);
+		if (!ctx)
+			return;
+
+		if (IS_ERR(ctx)) {
+			task_error = PTR_ERR(ctx);
+		} else {
+			ret = wait_for_completion_interruptible_timeout(
+						&ic->complete, 100 * HZ);
+			if (!ret)
+				task_error = -ETIMEDOUT;
+		}
+
+		ipu_ic_task_disable(ic);
+		ipu_ic_disable(ic);
+
+		if (ctx->complete)
+			ctx->complete(ctx->complete_context, task_error);
+		kfree(ctx->freep);
+	}
+}
+
+static irqreturn_t ipu_image_convert_handler(int irq, void *context)
+{
+	struct ipu_ic *ic = context;
+
+	complete(&ic->complete);
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * IDMAC base addresses are 8-byte aligned
+ */
+static int ipu_image_halign(u32 pixfmt)
+{
+	switch (pixfmt) {
+	/* 2 RGB32 pixels correspond to 8 bytes */
+	case V4L2_PIX_FMT_RGB32:
+	case V4L2_PIX_FMT_BGR32:
+		return 2;
+	/* 4 RGB565 or YUYV pixels correspond to 8 bytes */
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_YUYV:
+		return 4;
+	/*
+	 * 8 RGB24 pixels correspond to 24 bytes,
+	 * 8 NV12 pixels correspond to 8 bytes, both in luma and chroma
+	 */
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_BGR24:
+	case V4L2_PIX_FMT_NV12:
+		return 8;
+	/* 16 YUV420 pixels correspond to 16 bytes in luma, 8 bytes in chroma */
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+	case V4L2_PIX_FMT_YUV422P:
+		return 16;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * Vertically chroma-subsampled formats are limited to even heights and vertical
+ * positions
+ */
+static int ipu_image_valign(u32 pixfmt)
+{
+	switch (pixfmt) {
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_BGR24:
+	case V4L2_PIX_FMT_RGB32:
+	case V4L2_PIX_FMT_BGR32:
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YUV422P:
+		return 1;
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		return 2;
+	default:
+		return -EINVAL;
+	}
+}
+
+#define round_closest(x, y) round_down((x) + (y)/2, (y))
+
+struct image_convert_ctx *ipu_image_convert_prepare(struct ipu_soc *ipu,
+		struct ipu_image *in, struct ipu_image *out,
+		enum ipu_image_scale_ctrl ctrl, int *num_tiles)
+{
+	struct image_convert_ctx *ctx, *c;
+	int htiles, vtiles;
+	int in_valign, in_halign, in_burst, out_valign, out_halign, out_burst;
+	int left, top;
+	int x, y;
+	int h_resize_opt, v_resize_opt;
+	u32 v_downsize_coeff = 0, h_downsize_coeff = 0;
+	u32 v_resize_coeff, h_resize_coeff;
+
+	/* validate input */
+	if (in->rect.width < 16 || out->rect.width < 16 ||
+	    (in->rect.width / 8) > out->rect.width)
+		return ERR_PTR(-EINVAL);
+
+	/* tile setup */
+	htiles = DIV_ROUND_UP(out->rect.width, 1024);
+	vtiles = DIV_ROUND_UP(out->rect.height, 1024);
+
+	in_valign = ipu_image_valign(in->pix.pixelformat);
+	in_halign = ipu_image_halign(in->pix.pixelformat);
+	out_valign = ipu_image_valign(out->pix.pixelformat);
+	out_halign = ipu_image_halign(out->pix.pixelformat);
+
+	/* IC bursts are limited to either 8 or 16 pixels */
+	in_burst = 8;
+	out_burst = 8;
+
+	if (in_valign < 0 || in_halign < 0 ||
+	    out_valign < 0 || out_halign < 0) {
+		dev_err(ipu->dev, "unsupported in/out format\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* compute static decimator coefficients */
+	while ((in->rect.width >> h_downsize_coeff) > out->rect.width)
+		h_downsize_coeff++;
+	while ((in->rect.height >> v_downsize_coeff) > out->rect.height)
+		v_downsize_coeff++;
+
+	/* move and crop the output image according to IDMAC limitations */
+	switch (ctrl) {
+	case IPU_IMAGE_SCALE_ROUND_DOWN:
+		left = round_up(in->rect.left, in_halign);
+		top = round_up(in->rect.top, in_valign);
+		in->rect.width = in->rect.width - (left - in->rect.left);
+		in->rect.height = in->rect.height - (top - in->rect.top);
+		in->rect.left = left;
+		in->rect.top = top;
+		left = round_up(out->rect.left, out_halign);
+		top = round_up(out->rect.top, out_valign);
+		out->rect.width = round_down(out->rect.width - (left -
+					     out->rect.left), out_burst);
+		out->rect.height = round_down(out->rect.height - (top -
+					      out->rect.top), out_valign);
+		break;
+	case IPU_IMAGE_SCALE_ROUND_UP:
+		left = round_down(in->rect.left, in_halign);
+		top = round_down(in->rect.top, in_valign);
+		in->rect.width = in->rect.width + in->rect.left - left;
+		in->rect.height = in->rect.height + in->rect.top - top;
+		in->rect.left = left;
+		in->rect.top = top;
+		left = round_down(out->rect.left, out_halign);
+		top = round_down(out->rect.top, out_valign);
+		out->rect.width = round_up(out->rect.width + out->rect.left -
+					   left, out_burst);
+		out->rect.height = round_up(out->rect.height + out->rect.top -
+					    top, out_valign);
+		break;
+	case IPU_IMAGE_SCALE_PIXELPERFECT:
+		left = round_down(in->rect.left, in_halign);
+		top = round_down(in->rect.top, in_valign);
+		in->rect.width = in->rect.width + in->rect.left - left;
+		in->rect.height = in->rect.height + in->rect.top - top;
+		in->rect.left = left;
+		in->rect.top = top;
+		left = round_down(out->rect.left + out_halign / 2, out_halign);
+		top = round_down(out->rect.top + out_valign / 2, out_valign);
+		/*
+		 * don't round width and height to burst size / pixel format
+		 * limitations yet, we do it after determining the scaling
+		 * coefficients
+		 */
+		out->rect.width = out->rect.width + out->rect.left - left;
+		out->rect.height = out->rect.height + out->rect.top - top;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+	out->rect.left = left;
+	out->rect.top = top;
+
+	/* Round input width and height according to decimation */
+	in->rect.width = round_down(in->rect.width, 1 << h_downsize_coeff);
+	in->rect.height = round_down(in->rect.height, 1 << v_downsize_coeff);
+
+	dev_dbg(ipu->dev,
+		"%s: in: %dx%d(%dx%d@%d,%d) -> out: %dx%d(%dx%d@%d,%d)\n",
+		__func__, in->pix.width, in->pix.height, in->rect.width,
+		in->rect.height, in->rect.left, in->rect.top, out->pix.width,
+		out->pix.height, out->rect.width, out->rect.height,
+		out->rect.left, out->rect.top);
+
+	/*
+	 * Compute the bilinear resizing coefficients that can/could be used if
+	 * scaling using a single tile. The bottom right pixel should sample the
+	 * input as close as possible to but not beyond the bottom right input
+	 * pixel out of the decimator:
+	 *
+	 * (out->rect.width - 1) * h_resize / 8192.0 <= (in->rect.width >>
+	 *						 h_downsize_coeff) - 1
+	 * (out->rect.height - 1) * v_resize / 8192.0 <= (in->rect.height >>
+	 *						  v_downsize_coeff) - 1
+	 */
+	h_resize_opt = 8192 * ((in->rect.width >> h_downsize_coeff) - 1) /
+		       (out->rect.width - 1);
+	v_resize_opt = 8192 * ((in->rect.height >> v_downsize_coeff) - 1) /
+		       (out->rect.height - 1);
+
+	dev_dbg(ipu->dev,
+		"%s: hscale: >>%d, *8192/%d vscale: >>%d, *8192/%d, %dx%d tiles\n",
+		__func__, h_downsize_coeff, h_resize_opt, v_downsize_coeff,
+		v_resize_opt, htiles, vtiles);
+
+	ctx = kcalloc(htiles * vtiles, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	c = ctx;
+
+	for (x = htiles - 1; x >= 0; x--) {
+		int in_right, out_right;
+
+		/*
+		 * Since we render tiles right to left, the right edge
+		 * is already known. Depending on tile position and
+		 * scaling mode, we may overshoot it.
+		 */
+		if (x == htiles - 1) {
+			out_right = out->rect.left + out->rect.width;
+			in_right = in->rect.left + in->rect.width;
+		} else {
+			struct image_convert_ctx *c_right = c - vtiles;
+
+			out_right = c_right->out.rect.left;
+			in_right = c_right->in.rect.left;
+		}
+
+		/* Now determine the left edge of this tile column */
+		if (x == 0) {
+			/* For the leftmost column this is trivial */
+			c->out.rect.left = out->rect.left;
+			c->in.rect.left = in->rect.left;
+		} else {
+			int best_left, best_in_left;
+			int min_left, max_left;
+			int min_diff = INT_MAX;
+
+			/*
+			 * Find the best possible left edge. It must be adjusted
+			 * according to IDMAC limitations, and should be
+			 * chosen so that
+			 * (in->rect.left + (c->out.rect.left - out->rect.left)
+			 *  * h_resize_opt / (8192 >> h_downsize_coeff))
+			 * is as close as possible to a valid left edge in the
+			 * input.
+			 */
+			min_left = max(0,
+				       round_up(out_right - 1024, out_halign));
+			max_left = min(round_down(out_right, out_halign),
+				       x * 1024);
+			best_left = min_left;
+			best_in_left = (best_left - out->rect.left) *
+				       h_resize_opt;
+			for (left = min_left; left < max_left;
+			     left += out_halign) {
+				int diff, in_left;
+
+				/*
+				 * In ROUND_UP and ROUND_DOWN modes, for the
+				 * rightmost column, only consider left edges
+				 * that are a multiple of the burst size away
+				 * from the right edge.
+				 */
+				if ((ctrl != IPU_IMAGE_SCALE_PIXELPERFECT) &&
+				    (x == htiles - 1) &&
+				    ((out_right - left) % out_burst))
+					continue;
+				in_left = in->rect.left +
+					  (((left - out->rect.left) *
+					    h_resize_opt) << h_downsize_coeff);
+				diff = abs(in_left -
+					   round_closest(in_left,
+							 8192 * in_halign));
+
+				if (diff < min_diff) {
+					min_diff = diff;
+					best_left = left;
+					best_in_left = in_left;
+				}
+			}
+
+			c->out.rect.left = best_left;
+			c->in.rect.left = DIV_ROUND_CLOSEST(best_in_left, 8192);
+
+			dev_dbg(ipu->dev,
+				"%s: tile(%d,y):\tleft: %d -> %d (instead of %d.%04d -> %d)",
+				__func__, x, c->in.rect.left,
+				c->out.rect.left, best_in_left / 8192,
+				(best_in_left % 8192) * 10000 / 8192,
+				out->rect.left +
+				DIV_ROUND_CLOSEST((c->in.rect.left -
+						   in->rect.left) *
+						  (8192 >> h_downsize_coeff),
+						  h_resize_opt));
+		}
+
+		/* Determine tile width from left and right edges */
+		c->out.rect.width = out_right - c->out.rect.left;
+		c->in.rect.width = in_right - c->in.rect.left;
+
+		/* Now we can determine the actual per-tile scaling factor */
+		if (x == htiles - 1) {
+			/*
+			 * Round down for the right column, since we
+			 * don't want to read beyond the right edge.
+			 */
+			h_resize_coeff = 8192 * ((c->in.rect.width >>
+						 h_downsize_coeff) - 1) /
+					 (c->out.rect.width - 1);
+		} else {
+			/*
+			 * Round to closest for seams between tiles for
+			 * minimal distortion.
+			 */
+			h_resize_coeff = DIV_ROUND_CLOSEST(8192 *
+							   (c->in.rect.width >>
+							    h_downsize_coeff),
+							   c->out.rect.width);
+		}
+
+		/*
+		 * With the scaling factor known, round up output width
+		 * to burst size. In ROUND_UP and ROUND_DOWN scaling mode
+		 * this is a no-op for the right column.
+		 */
+		c->out.rect.width = round_up(c->out.rect.width, out_burst);
+
+		/*
+		 * Calculate input width from the last accessed input pixel
+		 * given output width and scaling coefficients. Round to
+		 * burst size.
+		 */
+		c->in.rect.width = (DIV_ROUND_UP((c->out.rect.width - 1) *
+						 h_resize_coeff, 8192) + 1)
+				   << h_downsize_coeff;
+		c->in.rect.width = round_up(c->in.rect.width, in_burst);
+
+		for (y = vtiles - 1; y >= 0; y--) {
+			int in_bottom, out_bottom;
+
+			memcpy(&c->in.pix, &in->pix,
+			      sizeof(struct v4l2_pix_format));
+
+			if (y == vtiles - 1) {
+				out_bottom = out->rect.top + out->rect.height;
+				in_bottom = in->rect.top + in->rect.height;
+			} else {
+				struct image_convert_ctx *c_below = c - 1;
+
+				out_bottom = c_below->out.rect.top;
+				in_bottom = c_below->in.rect.top;
+
+				/*
+				 * Copy horizontal parameters from the tile
+				 * below
+				 */
+				c->out.rect.left = c_below->out.rect.left;
+				c->out.rect.width = c_below->out.rect.width;
+				c->in.rect.left = c_below->in.rect.left;
+				c->in.rect.width = c_below->in.rect.width;
+			}
+
+			if (y == 0) {
+				c->out.rect.top = out->rect.top;
+				c->in.rect.top = in->rect.top;
+			} else {
+				int best_top, best_in_top;
+				int min_top, max_top;
+				int min_diff = INT_MAX;
+
+				/*
+				 * Find the best possible top edge. It must be
+				 * adjusted according to IDMAC limitations, and
+				 * should be chosen so that
+				 * (in->rect.top + (c->out.rect.top -
+				 *  out->rect.top) * v_resize_opt /
+				 * (8192 >> v_downsize_coeff))
+				 * is as close as possible to a valid top edge
+				 * in the input.
+				 */
+				min_top = max(0,
+					      round_up(out_bottom - 1024,
+						       out_valign));
+				max_top = min(round_down(out_bottom,
+							 out_halign), y * 1024);
+				best_top = min_top;
+				best_in_top = (best_top - out->rect.top) *
+					       v_resize_opt;
+				for (top = min_top; top < max_top;
+				     top += out_valign) {
+					int diff, in_top;
+
+					in_top = in->rect.top +
+						 (((top - out->rect.top) *
+						   v_resize_opt) <<
+						  v_downsize_coeff);
+					diff = abs(in_top -
+						   round_closest(in_top, 8192 *
+								 in_valign));
+
+					if (diff < min_diff) {
+						min_diff = diff;
+						best_top = top;
+						best_in_top = in_top;
+					}
+				}
+
+				c->out.rect.top = best_top;
+				c->in.rect.top = DIV_ROUND_CLOSEST(best_in_top,
+								   8192);
+
+				dev_dbg(ipu->dev,
+					"%s: tile(%d,%d):\ttop: %d -> %d (instead of %d.%04d -> %d)",
+					__func__, x, y, c->in.rect.top,
+					c->out.rect.top, best_in_top / 8192,
+					(best_in_top % 8192) * 10000 / 8192,
+					out->rect.top +
+					DIV_ROUND_CLOSEST((c->in.rect.top -
+							   in->rect.top) * (8192
+							  >> v_downsize_coeff),
+							  v_resize_opt));
+			}
+
+			/* Determine tile height from top and bottom edges */
+			c->out.rect.height = out_bottom - c->out.rect.top;
+			c->in.rect.height = in_bottom - c->in.rect.top;
+
+			/*
+			 * Now we can determine the actual vertical per-tile
+			 * scaling factor
+			 */
+			if (y == vtiles - 1) {
+				/*
+				 * Round down for the bottom row, since we
+				 * don't want to read beyond the lower border.
+				 */
+				v_resize_coeff = 8192 * ((c->in.rect.height >>
+							 v_downsize_coeff) - 1)
+						 / (c->out.rect.height - 1);
+			} else {
+				/*
+				 * Round to closest for seams between tiles for
+				 * minimal distortion.
+				 */
+				v_resize_coeff = DIV_ROUND_CLOSEST(8192 *
+							(c->in.rect.height >>
+							 v_downsize_coeff),
+							c->out.rect.height);
+			}
+
+			/*
+			 * With the scaling factor known, round up output height
+			 * to IDMAC limitations
+			 */
+			c->out.rect.height = round_up(c->out.rect.height,
+						      out_valign);
+
+			/*
+			 * Calculate input height from the last accessed input
+			 * line given output height and scaling coefficients.
+			 */
+			c->in.rect.height = (DIV_ROUND_UP(
+						(c->out.rect.height - 1) *
+						v_resize_coeff, 8192) + 1)
+					    << v_downsize_coeff;
+
+			/* align height according to IDMAC restrictions */
+			c->in.rect.height = round_up(c->in.rect.height,
+				in_valign);
+
+			memcpy(&c->out.pix, &out->pix,
+			       sizeof(struct v4l2_pix_format));
+
+			dev_dbg(ipu->dev,
+				"%s: tile(%d,%d): %dx%d(%dx%d@%d,%d) -> %dx%d(%dx%d@%d,%d), resize: %dx%d\n",
+				__func__, x, y,
+				c->in.pix.width, c->in.pix.height,
+				c->in.rect.width, c->in.rect.height,
+				c->in.rect.left, c->in.rect.top,
+				c->out.pix.width, c->out.pix.height,
+				c->out.rect.width, c->out.rect.height,
+				c->out.rect.left, c->out.rect.top,
+				h_resize_coeff, v_resize_coeff);
+
+			c->rsc = (v_downsize_coeff << 30) |
+				 (v_resize_coeff << 16) |
+				 (h_downsize_coeff << 14) |
+				 h_resize_coeff;
+
+			c++;
+		}
+	}
+
+	*num_tiles = htiles * vtiles;
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+int ipu_image_convert_run(struct ipu_soc *ipu, struct ipu_image *in,
+			  struct ipu_image *out, struct image_convert_ctx *ctx,
+			  int num_tiles, void (*complete)(void *ctx, int err),
+			  void *complete_context, bool free_ctx)
+{
+	struct ipu_ic_priv *priv = ipu->ic_priv;
+	struct ipu_ic *ic = &priv->task[IC_TASK_POST_PROCESSOR];
+	unsigned long flags;
+	int i;
+
+	for (i = 0; i < num_tiles; i++) {
+		ctx[i].in.phys0 = in->phys0;
+		ctx[i].out.phys0 = out->phys0;
+	}
+	ctx[num_tiles - 1].complete = complete;
+	ctx[num_tiles - 1].complete_context = complete_context;
+	if (free_ctx)
+		ctx[num_tiles - 1].freep = ctx;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	for (i = 0; i < num_tiles; i++)
+		list_add_tail(&ctx[i].list, &ic->image_list);
+
+	queue_work(ic->workqueue, &ic->work);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+static int ipu_image_convert_init(struct device *dev, struct ipu_soc *ipu,
+		struct ipu_ic_priv *priv)
+{
+	int ret;
+	struct ipu_ic *ic = ipu_ic_get(ipu, IC_TASK_POST_PROCESSOR);
+	int irq = ipu_idmac_channel_irq(ipu, ic->output_channel,
+					IPU_IRQ_EOF);
+
+	ic->workqueue = create_singlethread_workqueue(dev_name(ipu->dev));
+	if (!ic->workqueue)
+		return -ENOMEM;
+
+	INIT_WORK(&ic->work, ipu_image_convert_work);
+	init_completion(&ic->complete);
+
+	ret = devm_request_threaded_irq(dev, irq, NULL,
+				ipu_image_convert_handler,
+				IRQF_ONESHOT, "IC PP", ic);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	destroy_workqueue(ic->workqueue);
+	return ret;
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -736,12 +1473,30 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
+		INIT_LIST_HEAD(&priv->task[i].image_list);
 		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];
+
+		priv->task[i].input_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].in);
+		priv->task[i].output_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].out);
+		priv->task[i].rotation_input_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].rot_in);
+		priv->task[i].rotation_output_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].rot_out);
+		if (ic_task_ch[i].in_prev) {
+			priv->task[i].input_channel_p = ipu_idmac_get(ipu,
+							ic_task_ch[i].in_prev);
+			priv->task[i].input_channel_n = ipu_idmac_get(ipu,
+							ic_task_ch[i].in_next);
+		}
 	}
 
+	ipu_image_convert_init(dev, ipu, priv);
+
 	return 0;
 }
 
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 459508e..6d98a38 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -316,7 +316,8 @@ int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
 		     enum ipu_color_space in_cs,
-		     enum ipu_color_space out_cs);
+		     enum ipu_color_space out_cs,
+		     u32 rsc);
 int ipu_ic_task_graphics_init(struct ipu_ic *ic,
 			      enum ipu_color_space in_g_cs,
 			      bool galpha_en, u32 galpha,
@@ -362,4 +363,35 @@ struct ipu_client_platformdata {
 	int dma[2];
 };
 
+enum ipu_image_scale_ctrl {
+	IPU_IMAGE_SCALE_ROUND_DOWN,
+	IPU_IMAGE_SCALE_PIXELPERFECT,
+	IPU_IMAGE_SCALE_ROUND_UP,
+};
+
+struct image_convert_ctx;
+
+struct image_convert_ctx *ipu_image_convert_prepare(struct ipu_soc *ipu,
+		struct ipu_image *in, struct ipu_image *out,
+		enum ipu_image_scale_ctrl ctrl, int *num_tiles);
+int ipu_image_convert_run(struct ipu_soc *ipu, struct ipu_image *in,
+		struct ipu_image *out, struct image_convert_ctx *ctx,
+		int num_tiles, void (*complete)(void *ctx, int err),
+		void *complete_context, bool free_ctx);
+
+static inline int ipu_image_convert(struct ipu_soc *ipu, struct ipu_image *in,
+		struct ipu_image *out, void (*complete)(void *ctx, int err),
+		void *complete_context, enum ipu_image_scale_ctrl ctrl)
+{
+	struct image_convert_ctx *ctx;
+	int num_tiles;
+
+	ctx = ipu_image_convert_prepare(ipu, in, out, ctrl, &num_tiles);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	return ipu_image_convert_run(ipu, in, out, ctx, num_tiles, complete,
+				     complete_context, true);
+}
+
 #endif /* __DRM_IPU_H__ */
-- 
2.1.4


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

* [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
@ 2015-03-18 10:22   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, dri-devel, Hans Verkuil, kernel,
	Steve Longerbeam, Jean-Michel Hautbois

This patch adds support for mem2mem scaling and colorspace conversion
using the IC module's post-processing task.

Scaling images larger than 1024x1024 is supported by tiling over multiple
IC scaling runs. Since the IDMAC and IC units have interesting and different
alignment limitations for buffer base addresses (left edges) and burst size
(row lengths), depending on input and output pixel formats, the tile rectangles
and scaling coefficients are chosen to minimize distortion. Due to possible
overlap, the tiles have to be rendered right to left and bottom to top.
Up to 7 pixels (depending on frame sizes and scaling factor) have to be
available after the end of the frame if the width is not burst size aligned.
The tiling code has a parameter to optionally round frame sizes up or down
and avoid overdraw in compositing scenarios.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v1:
 - Removed deinterlacer support left-overs
---
 drivers/gpu/ipu-v3/ipu-ic.c | 787 +++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |  34 +-
 2 files changed, 804 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index ad75588..984f68f 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/spinlock.h>
 #include <linux/bitrev.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/err.h>
 #include "ipu-prv.h"
@@ -96,6 +97,15 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	u8 in;
+	u8 out;
+	u8 rot_in;
+	u8 rot_out;
+	u8 in_prev;
+	u8 in_next;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +148,53 @@ 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] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.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_VDI_CUR,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.in_prev = IPUV3_CHANNEL_MEM_VDI_PREV,
+		.in_next = 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 image_convert_ctx {
+	void (*complete)(void *ctx, int err);
+	void *complete_context;
+
+	struct list_head list;
+	struct ipu_image in;
+	struct ipu_image in_n;
+	struct ipu_image in_p;
+	struct ipu_image out;
+
+	void *freep;
+
+	bool rotate:1;
+
+	u32 rsc;
+};
+
 struct ipu_ic_priv;
 
 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;
@@ -152,6 +203,19 @@ struct ipu_ic {
 	bool in_use;
 
 	struct ipu_ic_priv *priv;
+
+	struct ipuv3_channel *input_channel_p;
+	struct ipuv3_channel *input_channel;
+	struct ipuv3_channel *input_channel_n;
+	struct ipuv3_channel *output_channel;
+	struct ipuv3_channel *rotation_input_channel;
+	struct ipuv3_channel *rotation_output_channel;
+
+	struct list_head image_list;
+
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+	struct completion complete;
 };
 
 struct ipu_ic_priv {
@@ -168,7 +232,8 @@ static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset)
 	return readl(ic->priv->base + offset);
 }
 
-static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset)
+static inline void ipu_ic_write(struct ipu_ic *ic, u32 value,
+				unsigned offset)
 {
 	writel(value, ic->priv->base + offset);
 }
@@ -446,32 +511,35 @@ int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
 		     enum ipu_color_space in_cs,
-		     enum ipu_color_space out_cs)
+		     enum ipu_color_space out_cs,
+		     u32 rsc)
 {
 	struct ipu_ic_priv *priv = ic->priv;
-	u32 reg, downsize_coeff, resize_coeff;
+	u32 downsize_coeff, resize_coeff;
 	unsigned long flags;
 	int ret = 0;
 
-	/* Setup vertical resizing */
-	ret = calc_resize_coeffs(ic, in_height, out_height,
-				 &resize_coeff, &downsize_coeff);
-	if (ret)
-		return ret;
+	if (!rsc) {
+		/* Setup vertical resizing */
+		ret = calc_resize_coeffs(ic, in_height, out_height,
+					 &resize_coeff, &downsize_coeff);
+		if (ret)
+			return ret;
 
-	reg = (downsize_coeff << 30) | (resize_coeff << 16);
+		rsc = (downsize_coeff << 30) | (resize_coeff << 16);
 
-	/* Setup horizontal resizing */
-	ret = calc_resize_coeffs(ic, in_width, out_width,
-				 &resize_coeff, &downsize_coeff);
-	if (ret)
-		return ret;
+		/* Setup horizontal resizing */
+		ret = calc_resize_coeffs(ic, in_width, out_width,
+					 &resize_coeff, &downsize_coeff);
+		if (ret)
+			return ret;
 
-	reg |= (downsize_coeff << 14) | resize_coeff;
+		rsc |= (downsize_coeff << 14) | resize_coeff;
+	}
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	ipu_ic_write(ic, reg, ic->reg->rsc);
+	ipu_ic_write(ic, rsc, ic->reg->rsc);
 
 	/* Setup color space conversion */
 	ic->in_cs = in_cs;
@@ -629,6 +697,675 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+static struct image_convert_ctx *ipu_image_convert_next(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct ipuv3_channel *ch_in = ic->input_channel;
+	struct ipuv3_channel *ch_out = ic->output_channel;
+	struct image_convert_ctx *ctx;
+	struct ipu_image *in_p, *in, *in_n;
+	struct ipu_image *out;
+	int ret;
+	unsigned long flags;
+	unsigned int inburst, outburst;
+	unsigned int in_height;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (list_empty(&ic->image_list)) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return NULL;
+	}
+
+	ctx = list_first_entry(&ic->image_list, struct image_convert_ctx, list);
+
+	list_del(&ctx->list);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	in_p = &ctx->in_p;
+	in = &ctx->in;
+	in_n = &ctx->in_n;
+	out = &ctx->out;
+
+	ipu_cpmem_zero(ch_in);
+	ipu_cpmem_zero(ch_out);
+
+	inburst = in->rect.width & 0xf ? 8 : 16;
+	outburst = out->rect.width & 0xf ? 8 : 16;
+
+	ipu_ic_enable(ic);
+
+	ipu_ic_task_idma_init(ic, ic->input_channel, in->rect.width,
+			      in->rect.height, inburst, IPU_ROTATE_NONE);
+	ipu_ic_task_idma_init(ic, ic->output_channel, out->rect.width,
+			      out->rect.height, outburst, IPU_ROTATE_NONE);
+
+	ipu_cpmem_set_image(ch_in, &ctx->in);
+	ipu_cpmem_set_image(ch_out, &ctx->out);
+
+	ipu_cpmem_set_burstsize(ch_in, inburst);
+	ipu_cpmem_set_burstsize(ch_out, outburst);
+
+	in_height = in->rect.height;
+
+	dev_dbg(priv->ipu->dev, "%s: %dx%d(%dx%d@%d,%d) -> %dx%d(%dx%d@%d,%d)\n",
+		__func__, in->pix.width, in->pix.height,
+		in->rect.width, in->rect.height, in->rect.left, in->rect.top,
+		out->pix.width, out->pix.height,
+		out->rect.width, out->rect.height,
+		out->rect.left, out->rect.top);
+
+	dev_dbg(priv->ipu->dev,
+		"%s: hscale: >>%d, *8192/%d vscale: >>%d, *8192/%d\n",
+		__func__, (ctx->rsc >> 14) & 0x3, (ctx->rsc & 0x3fff),
+		ctx->rsc >> 30, (ctx->rsc >> 16) & 0x3fff);
+
+	ret = ipu_ic_task_init(ic, in->rect.width, in_height,
+			out->rect.width, out->rect.height,
+			ipu_pixelformat_to_colorspace(in->pix.pixelformat),
+			ipu_pixelformat_to_colorspace(out->pix.pixelformat),
+			ctx->rsc);
+	if (ret) {
+		ipu_ic_disable(ic);
+		return ERR_PTR(ret);
+	}
+
+	ipu_idmac_enable_channel(ic->input_channel);
+	ipu_idmac_enable_channel(ic->output_channel);
+
+	ipu_ic_task_enable(ic);
+
+	ipu_idmac_select_buffer(ic->input_channel, 0);
+	ipu_idmac_select_buffer(ic->output_channel, 0);
+
+	return ctx;
+}
+
+static void ipu_image_convert_work(struct work_struct *work)
+{
+	struct ipu_ic *ic = container_of(work, struct ipu_ic, work);
+	struct image_convert_ctx *ctx;
+	int ret;
+
+	while (1) {
+		int task_error = 0;
+
+		ctx = ipu_image_convert_next(ic);
+		if (!ctx)
+			return;
+
+		if (IS_ERR(ctx)) {
+			task_error = PTR_ERR(ctx);
+		} else {
+			ret = wait_for_completion_interruptible_timeout(
+						&ic->complete, 100 * HZ);
+			if (!ret)
+				task_error = -ETIMEDOUT;
+		}
+
+		ipu_ic_task_disable(ic);
+		ipu_ic_disable(ic);
+
+		if (ctx->complete)
+			ctx->complete(ctx->complete_context, task_error);
+		kfree(ctx->freep);
+	}
+}
+
+static irqreturn_t ipu_image_convert_handler(int irq, void *context)
+{
+	struct ipu_ic *ic = context;
+
+	complete(&ic->complete);
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * IDMAC base addresses are 8-byte aligned
+ */
+static int ipu_image_halign(u32 pixfmt)
+{
+	switch (pixfmt) {
+	/* 2 RGB32 pixels correspond to 8 bytes */
+	case V4L2_PIX_FMT_RGB32:
+	case V4L2_PIX_FMT_BGR32:
+		return 2;
+	/* 4 RGB565 or YUYV pixels correspond to 8 bytes */
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_YUYV:
+		return 4;
+	/*
+	 * 8 RGB24 pixels correspond to 24 bytes,
+	 * 8 NV12 pixels correspond to 8 bytes, both in luma and chroma
+	 */
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_BGR24:
+	case V4L2_PIX_FMT_NV12:
+		return 8;
+	/* 16 YUV420 pixels correspond to 16 bytes in luma, 8 bytes in chroma */
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+	case V4L2_PIX_FMT_YUV422P:
+		return 16;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * Vertically chroma-subsampled formats are limited to even heights and vertical
+ * positions
+ */
+static int ipu_image_valign(u32 pixfmt)
+{
+	switch (pixfmt) {
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_BGR24:
+	case V4L2_PIX_FMT_RGB32:
+	case V4L2_PIX_FMT_BGR32:
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YUV422P:
+		return 1;
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		return 2;
+	default:
+		return -EINVAL;
+	}
+}
+
+#define round_closest(x, y) round_down((x) + (y)/2, (y))
+
+struct image_convert_ctx *ipu_image_convert_prepare(struct ipu_soc *ipu,
+		struct ipu_image *in, struct ipu_image *out,
+		enum ipu_image_scale_ctrl ctrl, int *num_tiles)
+{
+	struct image_convert_ctx *ctx, *c;
+	int htiles, vtiles;
+	int in_valign, in_halign, in_burst, out_valign, out_halign, out_burst;
+	int left, top;
+	int x, y;
+	int h_resize_opt, v_resize_opt;
+	u32 v_downsize_coeff = 0, h_downsize_coeff = 0;
+	u32 v_resize_coeff, h_resize_coeff;
+
+	/* validate input */
+	if (in->rect.width < 16 || out->rect.width < 16 ||
+	    (in->rect.width / 8) > out->rect.width)
+		return ERR_PTR(-EINVAL);
+
+	/* tile setup */
+	htiles = DIV_ROUND_UP(out->rect.width, 1024);
+	vtiles = DIV_ROUND_UP(out->rect.height, 1024);
+
+	in_valign = ipu_image_valign(in->pix.pixelformat);
+	in_halign = ipu_image_halign(in->pix.pixelformat);
+	out_valign = ipu_image_valign(out->pix.pixelformat);
+	out_halign = ipu_image_halign(out->pix.pixelformat);
+
+	/* IC bursts are limited to either 8 or 16 pixels */
+	in_burst = 8;
+	out_burst = 8;
+
+	if (in_valign < 0 || in_halign < 0 ||
+	    out_valign < 0 || out_halign < 0) {
+		dev_err(ipu->dev, "unsupported in/out format\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* compute static decimator coefficients */
+	while ((in->rect.width >> h_downsize_coeff) > out->rect.width)
+		h_downsize_coeff++;
+	while ((in->rect.height >> v_downsize_coeff) > out->rect.height)
+		v_downsize_coeff++;
+
+	/* move and crop the output image according to IDMAC limitations */
+	switch (ctrl) {
+	case IPU_IMAGE_SCALE_ROUND_DOWN:
+		left = round_up(in->rect.left, in_halign);
+		top = round_up(in->rect.top, in_valign);
+		in->rect.width = in->rect.width - (left - in->rect.left);
+		in->rect.height = in->rect.height - (top - in->rect.top);
+		in->rect.left = left;
+		in->rect.top = top;
+		left = round_up(out->rect.left, out_halign);
+		top = round_up(out->rect.top, out_valign);
+		out->rect.width = round_down(out->rect.width - (left -
+					     out->rect.left), out_burst);
+		out->rect.height = round_down(out->rect.height - (top -
+					      out->rect.top), out_valign);
+		break;
+	case IPU_IMAGE_SCALE_ROUND_UP:
+		left = round_down(in->rect.left, in_halign);
+		top = round_down(in->rect.top, in_valign);
+		in->rect.width = in->rect.width + in->rect.left - left;
+		in->rect.height = in->rect.height + in->rect.top - top;
+		in->rect.left = left;
+		in->rect.top = top;
+		left = round_down(out->rect.left, out_halign);
+		top = round_down(out->rect.top, out_valign);
+		out->rect.width = round_up(out->rect.width + out->rect.left -
+					   left, out_burst);
+		out->rect.height = round_up(out->rect.height + out->rect.top -
+					    top, out_valign);
+		break;
+	case IPU_IMAGE_SCALE_PIXELPERFECT:
+		left = round_down(in->rect.left, in_halign);
+		top = round_down(in->rect.top, in_valign);
+		in->rect.width = in->rect.width + in->rect.left - left;
+		in->rect.height = in->rect.height + in->rect.top - top;
+		in->rect.left = left;
+		in->rect.top = top;
+		left = round_down(out->rect.left + out_halign / 2, out_halign);
+		top = round_down(out->rect.top + out_valign / 2, out_valign);
+		/*
+		 * don't round width and height to burst size / pixel format
+		 * limitations yet, we do it after determining the scaling
+		 * coefficients
+		 */
+		out->rect.width = out->rect.width + out->rect.left - left;
+		out->rect.height = out->rect.height + out->rect.top - top;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+	out->rect.left = left;
+	out->rect.top = top;
+
+	/* Round input width and height according to decimation */
+	in->rect.width = round_down(in->rect.width, 1 << h_downsize_coeff);
+	in->rect.height = round_down(in->rect.height, 1 << v_downsize_coeff);
+
+	dev_dbg(ipu->dev,
+		"%s: in: %dx%d(%dx%d@%d,%d) -> out: %dx%d(%dx%d@%d,%d)\n",
+		__func__, in->pix.width, in->pix.height, in->rect.width,
+		in->rect.height, in->rect.left, in->rect.top, out->pix.width,
+		out->pix.height, out->rect.width, out->rect.height,
+		out->rect.left, out->rect.top);
+
+	/*
+	 * Compute the bilinear resizing coefficients that can/could be used if
+	 * scaling using a single tile. The bottom right pixel should sample the
+	 * input as close as possible to but not beyond the bottom right input
+	 * pixel out of the decimator:
+	 *
+	 * (out->rect.width - 1) * h_resize / 8192.0 <= (in->rect.width >>
+	 *						 h_downsize_coeff) - 1
+	 * (out->rect.height - 1) * v_resize / 8192.0 <= (in->rect.height >>
+	 *						  v_downsize_coeff) - 1
+	 */
+	h_resize_opt = 8192 * ((in->rect.width >> h_downsize_coeff) - 1) /
+		       (out->rect.width - 1);
+	v_resize_opt = 8192 * ((in->rect.height >> v_downsize_coeff) - 1) /
+		       (out->rect.height - 1);
+
+	dev_dbg(ipu->dev,
+		"%s: hscale: >>%d, *8192/%d vscale: >>%d, *8192/%d, %dx%d tiles\n",
+		__func__, h_downsize_coeff, h_resize_opt, v_downsize_coeff,
+		v_resize_opt, htiles, vtiles);
+
+	ctx = kcalloc(htiles * vtiles, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	c = ctx;
+
+	for (x = htiles - 1; x >= 0; x--) {
+		int in_right, out_right;
+
+		/*
+		 * Since we render tiles right to left, the right edge
+		 * is already known. Depending on tile position and
+		 * scaling mode, we may overshoot it.
+		 */
+		if (x == htiles - 1) {
+			out_right = out->rect.left + out->rect.width;
+			in_right = in->rect.left + in->rect.width;
+		} else {
+			struct image_convert_ctx *c_right = c - vtiles;
+
+			out_right = c_right->out.rect.left;
+			in_right = c_right->in.rect.left;
+		}
+
+		/* Now determine the left edge of this tile column */
+		if (x == 0) {
+			/* For the leftmost column this is trivial */
+			c->out.rect.left = out->rect.left;
+			c->in.rect.left = in->rect.left;
+		} else {
+			int best_left, best_in_left;
+			int min_left, max_left;
+			int min_diff = INT_MAX;
+
+			/*
+			 * Find the best possible left edge. It must be adjusted
+			 * according to IDMAC limitations, and should be
+			 * chosen so that
+			 * (in->rect.left + (c->out.rect.left - out->rect.left)
+			 *  * h_resize_opt / (8192 >> h_downsize_coeff))
+			 * is as close as possible to a valid left edge in the
+			 * input.
+			 */
+			min_left = max(0,
+				       round_up(out_right - 1024, out_halign));
+			max_left = min(round_down(out_right, out_halign),
+				       x * 1024);
+			best_left = min_left;
+			best_in_left = (best_left - out->rect.left) *
+				       h_resize_opt;
+			for (left = min_left; left < max_left;
+			     left += out_halign) {
+				int diff, in_left;
+
+				/*
+				 * In ROUND_UP and ROUND_DOWN modes, for the
+				 * rightmost column, only consider left edges
+				 * that are a multiple of the burst size away
+				 * from the right edge.
+				 */
+				if ((ctrl != IPU_IMAGE_SCALE_PIXELPERFECT) &&
+				    (x == htiles - 1) &&
+				    ((out_right - left) % out_burst))
+					continue;
+				in_left = in->rect.left +
+					  (((left - out->rect.left) *
+					    h_resize_opt) << h_downsize_coeff);
+				diff = abs(in_left -
+					   round_closest(in_left,
+							 8192 * in_halign));
+
+				if (diff < min_diff) {
+					min_diff = diff;
+					best_left = left;
+					best_in_left = in_left;
+				}
+			}
+
+			c->out.rect.left = best_left;
+			c->in.rect.left = DIV_ROUND_CLOSEST(best_in_left, 8192);
+
+			dev_dbg(ipu->dev,
+				"%s: tile(%d,y):\tleft: %d -> %d (instead of %d.%04d -> %d)",
+				__func__, x, c->in.rect.left,
+				c->out.rect.left, best_in_left / 8192,
+				(best_in_left % 8192) * 10000 / 8192,
+				out->rect.left +
+				DIV_ROUND_CLOSEST((c->in.rect.left -
+						   in->rect.left) *
+						  (8192 >> h_downsize_coeff),
+						  h_resize_opt));
+		}
+
+		/* Determine tile width from left and right edges */
+		c->out.rect.width = out_right - c->out.rect.left;
+		c->in.rect.width = in_right - c->in.rect.left;
+
+		/* Now we can determine the actual per-tile scaling factor */
+		if (x == htiles - 1) {
+			/*
+			 * Round down for the right column, since we
+			 * don't want to read beyond the right edge.
+			 */
+			h_resize_coeff = 8192 * ((c->in.rect.width >>
+						 h_downsize_coeff) - 1) /
+					 (c->out.rect.width - 1);
+		} else {
+			/*
+			 * Round to closest for seams between tiles for
+			 * minimal distortion.
+			 */
+			h_resize_coeff = DIV_ROUND_CLOSEST(8192 *
+							   (c->in.rect.width >>
+							    h_downsize_coeff),
+							   c->out.rect.width);
+		}
+
+		/*
+		 * With the scaling factor known, round up output width
+		 * to burst size. In ROUND_UP and ROUND_DOWN scaling mode
+		 * this is a no-op for the right column.
+		 */
+		c->out.rect.width = round_up(c->out.rect.width, out_burst);
+
+		/*
+		 * Calculate input width from the last accessed input pixel
+		 * given output width and scaling coefficients. Round to
+		 * burst size.
+		 */
+		c->in.rect.width = (DIV_ROUND_UP((c->out.rect.width - 1) *
+						 h_resize_coeff, 8192) + 1)
+				   << h_downsize_coeff;
+		c->in.rect.width = round_up(c->in.rect.width, in_burst);
+
+		for (y = vtiles - 1; y >= 0; y--) {
+			int in_bottom, out_bottom;
+
+			memcpy(&c->in.pix, &in->pix,
+			      sizeof(struct v4l2_pix_format));
+
+			if (y == vtiles - 1) {
+				out_bottom = out->rect.top + out->rect.height;
+				in_bottom = in->rect.top + in->rect.height;
+			} else {
+				struct image_convert_ctx *c_below = c - 1;
+
+				out_bottom = c_below->out.rect.top;
+				in_bottom = c_below->in.rect.top;
+
+				/*
+				 * Copy horizontal parameters from the tile
+				 * below
+				 */
+				c->out.rect.left = c_below->out.rect.left;
+				c->out.rect.width = c_below->out.rect.width;
+				c->in.rect.left = c_below->in.rect.left;
+				c->in.rect.width = c_below->in.rect.width;
+			}
+
+			if (y == 0) {
+				c->out.rect.top = out->rect.top;
+				c->in.rect.top = in->rect.top;
+			} else {
+				int best_top, best_in_top;
+				int min_top, max_top;
+				int min_diff = INT_MAX;
+
+				/*
+				 * Find the best possible top edge. It must be
+				 * adjusted according to IDMAC limitations, and
+				 * should be chosen so that
+				 * (in->rect.top + (c->out.rect.top -
+				 *  out->rect.top) * v_resize_opt /
+				 * (8192 >> v_downsize_coeff))
+				 * is as close as possible to a valid top edge
+				 * in the input.
+				 */
+				min_top = max(0,
+					      round_up(out_bottom - 1024,
+						       out_valign));
+				max_top = min(round_down(out_bottom,
+							 out_halign), y * 1024);
+				best_top = min_top;
+				best_in_top = (best_top - out->rect.top) *
+					       v_resize_opt;
+				for (top = min_top; top < max_top;
+				     top += out_valign) {
+					int diff, in_top;
+
+					in_top = in->rect.top +
+						 (((top - out->rect.top) *
+						   v_resize_opt) <<
+						  v_downsize_coeff);
+					diff = abs(in_top -
+						   round_closest(in_top, 8192 *
+								 in_valign));
+
+					if (diff < min_diff) {
+						min_diff = diff;
+						best_top = top;
+						best_in_top = in_top;
+					}
+				}
+
+				c->out.rect.top = best_top;
+				c->in.rect.top = DIV_ROUND_CLOSEST(best_in_top,
+								   8192);
+
+				dev_dbg(ipu->dev,
+					"%s: tile(%d,%d):\ttop: %d -> %d (instead of %d.%04d -> %d)",
+					__func__, x, y, c->in.rect.top,
+					c->out.rect.top, best_in_top / 8192,
+					(best_in_top % 8192) * 10000 / 8192,
+					out->rect.top +
+					DIV_ROUND_CLOSEST((c->in.rect.top -
+							   in->rect.top) * (8192
+							  >> v_downsize_coeff),
+							  v_resize_opt));
+			}
+
+			/* Determine tile height from top and bottom edges */
+			c->out.rect.height = out_bottom - c->out.rect.top;
+			c->in.rect.height = in_bottom - c->in.rect.top;
+
+			/*
+			 * Now we can determine the actual vertical per-tile
+			 * scaling factor
+			 */
+			if (y == vtiles - 1) {
+				/*
+				 * Round down for the bottom row, since we
+				 * don't want to read beyond the lower border.
+				 */
+				v_resize_coeff = 8192 * ((c->in.rect.height >>
+							 v_downsize_coeff) - 1)
+						 / (c->out.rect.height - 1);
+			} else {
+				/*
+				 * Round to closest for seams between tiles for
+				 * minimal distortion.
+				 */
+				v_resize_coeff = DIV_ROUND_CLOSEST(8192 *
+							(c->in.rect.height >>
+							 v_downsize_coeff),
+							c->out.rect.height);
+			}
+
+			/*
+			 * With the scaling factor known, round up output height
+			 * to IDMAC limitations
+			 */
+			c->out.rect.height = round_up(c->out.rect.height,
+						      out_valign);
+
+			/*
+			 * Calculate input height from the last accessed input
+			 * line given output height and scaling coefficients.
+			 */
+			c->in.rect.height = (DIV_ROUND_UP(
+						(c->out.rect.height - 1) *
+						v_resize_coeff, 8192) + 1)
+					    << v_downsize_coeff;
+
+			/* align height according to IDMAC restrictions */
+			c->in.rect.height = round_up(c->in.rect.height,
+				in_valign);
+
+			memcpy(&c->out.pix, &out->pix,
+			       sizeof(struct v4l2_pix_format));
+
+			dev_dbg(ipu->dev,
+				"%s: tile(%d,%d): %dx%d(%dx%d@%d,%d) -> %dx%d(%dx%d@%d,%d), resize: %dx%d\n",
+				__func__, x, y,
+				c->in.pix.width, c->in.pix.height,
+				c->in.rect.width, c->in.rect.height,
+				c->in.rect.left, c->in.rect.top,
+				c->out.pix.width, c->out.pix.height,
+				c->out.rect.width, c->out.rect.height,
+				c->out.rect.left, c->out.rect.top,
+				h_resize_coeff, v_resize_coeff);
+
+			c->rsc = (v_downsize_coeff << 30) |
+				 (v_resize_coeff << 16) |
+				 (h_downsize_coeff << 14) |
+				 h_resize_coeff;
+
+			c++;
+		}
+	}
+
+	*num_tiles = htiles * vtiles;
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+int ipu_image_convert_run(struct ipu_soc *ipu, struct ipu_image *in,
+			  struct ipu_image *out, struct image_convert_ctx *ctx,
+			  int num_tiles, void (*complete)(void *ctx, int err),
+			  void *complete_context, bool free_ctx)
+{
+	struct ipu_ic_priv *priv = ipu->ic_priv;
+	struct ipu_ic *ic = &priv->task[IC_TASK_POST_PROCESSOR];
+	unsigned long flags;
+	int i;
+
+	for (i = 0; i < num_tiles; i++) {
+		ctx[i].in.phys0 = in->phys0;
+		ctx[i].out.phys0 = out->phys0;
+	}
+	ctx[num_tiles - 1].complete = complete;
+	ctx[num_tiles - 1].complete_context = complete_context;
+	if (free_ctx)
+		ctx[num_tiles - 1].freep = ctx;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	for (i = 0; i < num_tiles; i++)
+		list_add_tail(&ctx[i].list, &ic->image_list);
+
+	queue_work(ic->workqueue, &ic->work);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+static int ipu_image_convert_init(struct device *dev, struct ipu_soc *ipu,
+		struct ipu_ic_priv *priv)
+{
+	int ret;
+	struct ipu_ic *ic = ipu_ic_get(ipu, IC_TASK_POST_PROCESSOR);
+	int irq = ipu_idmac_channel_irq(ipu, ic->output_channel,
+					IPU_IRQ_EOF);
+
+	ic->workqueue = create_singlethread_workqueue(dev_name(ipu->dev));
+	if (!ic->workqueue)
+		return -ENOMEM;
+
+	INIT_WORK(&ic->work, ipu_image_convert_work);
+	init_completion(&ic->complete);
+
+	ret = devm_request_threaded_irq(dev, irq, NULL,
+				ipu_image_convert_handler,
+				IRQF_ONESHOT, "IC PP", ic);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	destroy_workqueue(ic->workqueue);
+	return ret;
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -736,12 +1473,30 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
+		INIT_LIST_HEAD(&priv->task[i].image_list);
 		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];
+
+		priv->task[i].input_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].in);
+		priv->task[i].output_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].out);
+		priv->task[i].rotation_input_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].rot_in);
+		priv->task[i].rotation_output_channel = ipu_idmac_get(ipu,
+							ic_task_ch[i].rot_out);
+		if (ic_task_ch[i].in_prev) {
+			priv->task[i].input_channel_p = ipu_idmac_get(ipu,
+							ic_task_ch[i].in_prev);
+			priv->task[i].input_channel_n = ipu_idmac_get(ipu,
+							ic_task_ch[i].in_next);
+		}
 	}
 
+	ipu_image_convert_init(dev, ipu, priv);
+
 	return 0;
 }
 
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 459508e..6d98a38 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -316,7 +316,8 @@ int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
 		     enum ipu_color_space in_cs,
-		     enum ipu_color_space out_cs);
+		     enum ipu_color_space out_cs,
+		     u32 rsc);
 int ipu_ic_task_graphics_init(struct ipu_ic *ic,
 			      enum ipu_color_space in_g_cs,
 			      bool galpha_en, u32 galpha,
@@ -362,4 +363,35 @@ struct ipu_client_platformdata {
 	int dma[2];
 };
 
+enum ipu_image_scale_ctrl {
+	IPU_IMAGE_SCALE_ROUND_DOWN,
+	IPU_IMAGE_SCALE_PIXELPERFECT,
+	IPU_IMAGE_SCALE_ROUND_UP,
+};
+
+struct image_convert_ctx;
+
+struct image_convert_ctx *ipu_image_convert_prepare(struct ipu_soc *ipu,
+		struct ipu_image *in, struct ipu_image *out,
+		enum ipu_image_scale_ctrl ctrl, int *num_tiles);
+int ipu_image_convert_run(struct ipu_soc *ipu, struct ipu_image *in,
+		struct ipu_image *out, struct image_convert_ctx *ctx,
+		int num_tiles, void (*complete)(void *ctx, int err),
+		void *complete_context, bool free_ctx);
+
+static inline int ipu_image_convert(struct ipu_soc *ipu, struct ipu_image *in,
+		struct ipu_image *out, void (*complete)(void *ctx, int err),
+		void *complete_context, enum ipu_image_scale_ctrl ctrl)
+{
+	struct image_convert_ctx *ctx;
+	int num_tiles;
+
+	ctx = ipu_image_convert_prepare(ipu, in, out, ctrl, &num_tiles);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	return ipu_image_convert_run(ipu, in, out, ctx, num_tiles, complete,
+				     complete_context, true);
+}
+
 #endif /* __DRM_IPU_H__ */
-- 
2.1.4

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

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

* [PATCH v2 3/5] gpu: ipu-v3: Register scaler platform device
  2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
@ 2015-03-18 10:22   ` Philipp Zabel
  2015-03-18 10:22   ` Philipp Zabel
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	kernel, Philipp Zabel

This patch registers the scaler device using the IC post-processing task,
to be handled by a mem2mem scaler driver.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/gpu/ipu-v3/ipu-common.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 67bab5c..cf89692 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1026,6 +1026,8 @@ static const struct ipu_platform_reg client_reg[] = {
 		},
 		.reg_offset = IPU_CM_CSI1_REG_OFS,
 		.name = "imx-ipuv3-camera",
+	}, {
+		.name = "imx-ipuv3-scaler",
 	},
 };
 
-- 
2.1.4


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

* [PATCH v2 3/5] gpu: ipu-v3: Register scaler platform device
@ 2015-03-18 10:22   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, dri-devel, Hans Verkuil, kernel,
	Steve Longerbeam, Jean-Michel Hautbois

This patch registers the scaler device using the IC post-processing task,
to be handled by a mem2mem scaler driver.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/gpu/ipu-v3/ipu-common.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 67bab5c..cf89692 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1026,6 +1026,8 @@ static const struct ipu_platform_reg client_reg[] = {
 		},
 		.reg_offset = IPU_CM_CSI1_REG_OFS,
 		.name = "imx-ipuv3-camera",
+	}, {
+		.name = "imx-ipuv3-scaler",
 	},
 };
 
-- 
2.1.4

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

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

* [PATCH v2 4/5] [media] imx-ipu: Add ipu media common code
  2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
@ 2015-03-18 10:22   ` Philipp Zabel
  2015-03-18 10:22   ` Philipp Zabel
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	kernel, Sascha Hauer, Lucas Stach, Philipp Zabel

From: Sascha Hauer <s.hauer@pengutronix.de>

Add video4linux API routines common to drivers for units that
accept or provide video data via the i.MX IPU IDMAC channels,
such as scaler or deinterlacer drivers.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/media/platform/Kconfig       |   2 +
 drivers/media/platform/Makefile      |   1 +
 drivers/media/platform/imx/Kconfig   |   2 +
 drivers/media/platform/imx/Makefile  |   1 +
 drivers/media/platform/imx/imx-ipu.c | 313 +++++++++++++++++++++++++++++++++++
 drivers/media/platform/imx/imx-ipu.h |  35 ++++
 6 files changed, 354 insertions(+)
 create mode 100644 drivers/media/platform/imx/Kconfig
 create mode 100644 drivers/media/platform/imx/Makefile
 create mode 100644 drivers/media/platform/imx/imx-ipu.c
 create mode 100644 drivers/media/platform/imx/imx-ipu.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index d9b872b..650a9a6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -29,6 +29,8 @@ config VIDEO_VIA_CAMERA
 
 source "drivers/media/platform/davinci/Kconfig"
 
+source "drivers/media/platform/imx/Kconfig"
+
 source "drivers/media/platform/omap/Kconfig"
 
 source "drivers/media/platform/blackfin/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 3ec1547..2e35581 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
 
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
 
+obj-y	+= imx/
 obj-y	+= omap/
 
 obj-$(CONFIG_VIDEO_AM437X_VPFE)		+= am437x/
diff --git a/drivers/media/platform/imx/Kconfig b/drivers/media/platform/imx/Kconfig
new file mode 100644
index 0000000..a90c973
--- /dev/null
+++ b/drivers/media/platform/imx/Kconfig
@@ -0,0 +1,2 @@
+config VIDEO_IMX_IPU_COMMON
+	tristate
diff --git a/drivers/media/platform/imx/Makefile b/drivers/media/platform/imx/Makefile
new file mode 100644
index 0000000..5de119c
--- /dev/null
+++ b/drivers/media/platform/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_IMX_IPU_COMMON)	+= imx-ipu.o
diff --git a/drivers/media/platform/imx/imx-ipu.c b/drivers/media/platform/imx/imx-ipu.c
new file mode 100644
index 0000000..c1b8637
--- /dev/null
+++ b/drivers/media/platform/imx/imx-ipu.c
@@ -0,0 +1,313 @@
+/*
+ * i.MX IPUv3 common v4l2 support
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * 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/module.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "imx-ipu.h"
+
+static struct ipu_fmt ipu_fmt_yuv[] = {
+	{
+		.fourcc = V4L2_PIX_FMT_YUV420,
+		.name = "YUV 4:2:0 planar, YCbCr",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_YVU420,
+		.name = "YUV 4:2:0 planar, YCrCb",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.name = "YUV 4:2:2 planar, YCbCr",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.name = "YUV 4:2:0 partial interleaved, YCbCr",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_UYVY,
+		.name = "4:2:2, packed, UYVY",
+		.bytes_per_pixel = 2,
+	}, {
+		.fourcc = V4L2_PIX_FMT_YUYV,
+		.name = "4:2:2, packed, YUYV",
+		.bytes_per_pixel = 2,
+	},
+};
+
+static struct ipu_fmt ipu_fmt_rgb[] = {
+	{
+		.fourcc = V4L2_PIX_FMT_RGB32,
+		.name = "RGB888",
+		.bytes_per_pixel = 4,
+	}, {
+		.fourcc = V4L2_PIX_FMT_RGB24,
+		.name = "RGB24",
+		.bytes_per_pixel = 3,
+	}, {
+		.fourcc = V4L2_PIX_FMT_BGR24,
+		.name = "BGR24",
+		.bytes_per_pixel = 3,
+	}, {
+		.fourcc = V4L2_PIX_FMT_RGB565,
+		.name = "RGB565",
+		.bytes_per_pixel = 2,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_BGR32,
+		.name = "BGR888",
+		.bytes_per_pixel = 4,
+	},
+};
+
+struct ipu_fmt *ipu_find_fmt_yuv(unsigned int pixelformat)
+{
+	struct ipu_fmt *fmt;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_fmt_yuv); i++) {
+		fmt = &ipu_fmt_yuv[i];
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(ipu_find_fmt_yuv);
+
+struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat)
+{
+	struct ipu_fmt *fmt;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_fmt_rgb); i++) {
+		fmt = &ipu_fmt_rgb[i];
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(ipu_find_fmt_rgb);
+
+static struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_yuv(pixelformat);
+	if (fmt)
+		return fmt;
+	fmt = ipu_find_fmt_rgb(pixelformat);
+
+	return fmt;
+}
+EXPORT_SYMBOL_GPL(ipu_find_fmt);
+
+int ipu_try_fmt(struct file *file, void *fh,
+		struct v4l2_format *f)
+{
+	struct ipu_fmt *fmt;
+
+	v4l_bound_align_image(&f->fmt.pix.width, 8, 4096, 2,
+			      &f->fmt.pix.height, 2, 4096, 1, 0);
+
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+
+	fmt = ipu_find_fmt(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	f->fmt.pix.bytesperline = f->fmt.pix.width * fmt->bytes_per_pixel;
+	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+	if (fmt->fourcc == V4L2_PIX_FMT_YUV420 ||
+	    fmt->fourcc == V4L2_PIX_FMT_YVU420 ||
+	    fmt->fourcc == V4L2_PIX_FMT_NV12)
+		f->fmt.pix.sizeimage = f->fmt.pix.sizeimage * 3 / 2;
+	else if (fmt->fourcc == V4L2_PIX_FMT_YUV422P)
+		f->fmt.pix.sizeimage *= 2;
+
+	f->fmt.pix.priv = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_try_fmt);
+
+int ipu_try_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_rgb(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_try_fmt(file, fh, f);
+}
+EXPORT_SYMBOL_GPL(ipu_try_fmt_rgb);
+
+int ipu_try_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_yuv(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_try_fmt(file, fh, f);
+}
+EXPORT_SYMBOL_GPL(ipu_try_fmt_yuv);
+
+int ipu_enum_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f)
+{
+	struct ipu_fmt *fmt;
+
+	if (f->index >= ARRAY_SIZE(ipu_fmt_rgb))
+		return -EINVAL;
+
+	fmt = &ipu_fmt_rgb[f->index];
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_fmt_rgb);
+
+int ipu_enum_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f)
+{
+	struct ipu_fmt *fmt;
+
+	if (f->index >= ARRAY_SIZE(ipu_fmt_yuv))
+		return -EINVAL;
+
+	fmt = &ipu_fmt_yuv[f->index];
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_fmt_yuv);
+
+int ipu_enum_fmt(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f)
+{
+	struct ipu_fmt *fmt;
+	int index = f->index;
+
+	if (index >= ARRAY_SIZE(ipu_fmt_yuv)) {
+		index -= ARRAY_SIZE(ipu_fmt_yuv);
+		if (index >= ARRAY_SIZE(ipu_fmt_rgb))
+			return -EINVAL;
+		fmt = &ipu_fmt_rgb[index];
+	} else {
+		fmt = &ipu_fmt_yuv[index];
+	}
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_fmt);
+
+int ipu_s_fmt(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	int ret;
+
+	ret = ipu_try_fmt(file, fh, f);
+	if (ret)
+		return ret;
+
+	pix->width = f->fmt.pix.width;
+	pix->height = f->fmt.pix.height;
+	pix->pixelformat = f->fmt.pix.pixelformat;
+	pix->bytesperline = f->fmt.pix.bytesperline;
+	pix->sizeimage = f->fmt.pix.sizeimage;
+	pix->colorspace = f->fmt.pix.colorspace;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_s_fmt);
+
+int ipu_s_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_rgb(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_s_fmt(file, fh, f, pix);
+}
+EXPORT_SYMBOL_GPL(ipu_s_fmt_rgb);
+
+int ipu_s_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_yuv(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_s_fmt(file, fh, f, pix);
+}
+EXPORT_SYMBOL_GPL(ipu_s_fmt_yuv);
+
+int ipu_g_fmt(struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.pixelformat = pix->pixelformat;
+	f->fmt.pix.bytesperline = pix->bytesperline;
+	f->fmt.pix.width = pix->width;
+	f->fmt.pix.height = pix->height;
+	f->fmt.pix.sizeimage = pix->sizeimage;
+	f->fmt.pix.colorspace = pix->colorspace;
+	f->fmt.pix.priv = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_g_fmt);
+
+int ipu_enum_framesizes(struct file *file, void *fh,
+			struct v4l2_frmsizeenum *fsize)
+{
+	struct ipu_fmt *fmt;
+
+	if (fsize->index != 0)
+		return -EINVAL;
+
+	fmt = ipu_find_fmt(fsize->pixel_format);
+	if (!fmt)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+	fsize->stepwise.min_width = 1;
+	fsize->stepwise.min_height = 1;
+	fsize->stepwise.max_width = 4096;
+	fsize->stepwise.max_height = 4096;
+	fsize->stepwise.step_width = fsize->stepwise.step_height = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_framesizes);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/imx/imx-ipu.h b/drivers/media/platform/imx/imx-ipu.h
new file mode 100644
index 0000000..51c0982
--- /dev/null
+++ b/drivers/media/platform/imx/imx-ipu.h
@@ -0,0 +1,35 @@
+#ifndef __MEDIA_IMX_IPU_H
+#define __MEDIA_IMX_IPU_H
+#include <linux/videodev2.h>
+
+struct ipu_fmt {
+	u32 fourcc;
+	const char *name;
+	int bytes_per_pixel;
+};
+
+int ipu_enum_fmt(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f);
+int ipu_enum_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f);
+int ipu_enum_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f);
+struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat);
+struct ipu_fmt *ipu_find_fmt_yuv(unsigned int pixelformat);
+int ipu_try_fmt(struct file *file, void *fh,
+		struct v4l2_format *f);
+int ipu_try_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f);
+int ipu_try_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f);
+int ipu_s_fmt(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_s_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_s_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_g_fmt(struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_enum_framesizes(struct file *file, void *fh,
+			struct v4l2_frmsizeenum *fsize);
+
+#endif /* __MEDIA_IMX_IPU_H */
-- 
2.1.4


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

* [PATCH v2 4/5] [media] imx-ipu: Add ipu media common code
@ 2015-03-18 10:22   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, dri-devel, Hans Verkuil, kernel,
	Steve Longerbeam, Jean-Michel Hautbois

From: Sascha Hauer <s.hauer@pengutronix.de>

Add video4linux API routines common to drivers for units that
accept or provide video data via the i.MX IPU IDMAC channels,
such as scaler or deinterlacer drivers.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/media/platform/Kconfig       |   2 +
 drivers/media/platform/Makefile      |   1 +
 drivers/media/platform/imx/Kconfig   |   2 +
 drivers/media/platform/imx/Makefile  |   1 +
 drivers/media/platform/imx/imx-ipu.c | 313 +++++++++++++++++++++++++++++++++++
 drivers/media/platform/imx/imx-ipu.h |  35 ++++
 6 files changed, 354 insertions(+)
 create mode 100644 drivers/media/platform/imx/Kconfig
 create mode 100644 drivers/media/platform/imx/Makefile
 create mode 100644 drivers/media/platform/imx/imx-ipu.c
 create mode 100644 drivers/media/platform/imx/imx-ipu.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index d9b872b..650a9a6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -29,6 +29,8 @@ config VIDEO_VIA_CAMERA
 
 source "drivers/media/platform/davinci/Kconfig"
 
+source "drivers/media/platform/imx/Kconfig"
+
 source "drivers/media/platform/omap/Kconfig"
 
 source "drivers/media/platform/blackfin/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 3ec1547..2e35581 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
 
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
 
+obj-y	+= imx/
 obj-y	+= omap/
 
 obj-$(CONFIG_VIDEO_AM437X_VPFE)		+= am437x/
diff --git a/drivers/media/platform/imx/Kconfig b/drivers/media/platform/imx/Kconfig
new file mode 100644
index 0000000..a90c973
--- /dev/null
+++ b/drivers/media/platform/imx/Kconfig
@@ -0,0 +1,2 @@
+config VIDEO_IMX_IPU_COMMON
+	tristate
diff --git a/drivers/media/platform/imx/Makefile b/drivers/media/platform/imx/Makefile
new file mode 100644
index 0000000..5de119c
--- /dev/null
+++ b/drivers/media/platform/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_IMX_IPU_COMMON)	+= imx-ipu.o
diff --git a/drivers/media/platform/imx/imx-ipu.c b/drivers/media/platform/imx/imx-ipu.c
new file mode 100644
index 0000000..c1b8637
--- /dev/null
+++ b/drivers/media/platform/imx/imx-ipu.c
@@ -0,0 +1,313 @@
+/*
+ * i.MX IPUv3 common v4l2 support
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * 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/module.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "imx-ipu.h"
+
+static struct ipu_fmt ipu_fmt_yuv[] = {
+	{
+		.fourcc = V4L2_PIX_FMT_YUV420,
+		.name = "YUV 4:2:0 planar, YCbCr",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_YVU420,
+		.name = "YUV 4:2:0 planar, YCrCb",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.name = "YUV 4:2:2 planar, YCbCr",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.name = "YUV 4:2:0 partial interleaved, YCbCr",
+		.bytes_per_pixel = 1,
+	}, {
+		.fourcc = V4L2_PIX_FMT_UYVY,
+		.name = "4:2:2, packed, UYVY",
+		.bytes_per_pixel = 2,
+	}, {
+		.fourcc = V4L2_PIX_FMT_YUYV,
+		.name = "4:2:2, packed, YUYV",
+		.bytes_per_pixel = 2,
+	},
+};
+
+static struct ipu_fmt ipu_fmt_rgb[] = {
+	{
+		.fourcc = V4L2_PIX_FMT_RGB32,
+		.name = "RGB888",
+		.bytes_per_pixel = 4,
+	}, {
+		.fourcc = V4L2_PIX_FMT_RGB24,
+		.name = "RGB24",
+		.bytes_per_pixel = 3,
+	}, {
+		.fourcc = V4L2_PIX_FMT_BGR24,
+		.name = "BGR24",
+		.bytes_per_pixel = 3,
+	}, {
+		.fourcc = V4L2_PIX_FMT_RGB565,
+		.name = "RGB565",
+		.bytes_per_pixel = 2,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_BGR32,
+		.name = "BGR888",
+		.bytes_per_pixel = 4,
+	},
+};
+
+struct ipu_fmt *ipu_find_fmt_yuv(unsigned int pixelformat)
+{
+	struct ipu_fmt *fmt;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_fmt_yuv); i++) {
+		fmt = &ipu_fmt_yuv[i];
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(ipu_find_fmt_yuv);
+
+struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat)
+{
+	struct ipu_fmt *fmt;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_fmt_rgb); i++) {
+		fmt = &ipu_fmt_rgb[i];
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(ipu_find_fmt_rgb);
+
+static struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_yuv(pixelformat);
+	if (fmt)
+		return fmt;
+	fmt = ipu_find_fmt_rgb(pixelformat);
+
+	return fmt;
+}
+EXPORT_SYMBOL_GPL(ipu_find_fmt);
+
+int ipu_try_fmt(struct file *file, void *fh,
+		struct v4l2_format *f)
+{
+	struct ipu_fmt *fmt;
+
+	v4l_bound_align_image(&f->fmt.pix.width, 8, 4096, 2,
+			      &f->fmt.pix.height, 2, 4096, 1, 0);
+
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+
+	fmt = ipu_find_fmt(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	f->fmt.pix.bytesperline = f->fmt.pix.width * fmt->bytes_per_pixel;
+	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+	if (fmt->fourcc == V4L2_PIX_FMT_YUV420 ||
+	    fmt->fourcc == V4L2_PIX_FMT_YVU420 ||
+	    fmt->fourcc == V4L2_PIX_FMT_NV12)
+		f->fmt.pix.sizeimage = f->fmt.pix.sizeimage * 3 / 2;
+	else if (fmt->fourcc == V4L2_PIX_FMT_YUV422P)
+		f->fmt.pix.sizeimage *= 2;
+
+	f->fmt.pix.priv = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_try_fmt);
+
+int ipu_try_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_rgb(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_try_fmt(file, fh, f);
+}
+EXPORT_SYMBOL_GPL(ipu_try_fmt_rgb);
+
+int ipu_try_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_yuv(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_try_fmt(file, fh, f);
+}
+EXPORT_SYMBOL_GPL(ipu_try_fmt_yuv);
+
+int ipu_enum_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f)
+{
+	struct ipu_fmt *fmt;
+
+	if (f->index >= ARRAY_SIZE(ipu_fmt_rgb))
+		return -EINVAL;
+
+	fmt = &ipu_fmt_rgb[f->index];
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_fmt_rgb);
+
+int ipu_enum_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f)
+{
+	struct ipu_fmt *fmt;
+
+	if (f->index >= ARRAY_SIZE(ipu_fmt_yuv))
+		return -EINVAL;
+
+	fmt = &ipu_fmt_yuv[f->index];
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_fmt_yuv);
+
+int ipu_enum_fmt(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f)
+{
+	struct ipu_fmt *fmt;
+	int index = f->index;
+
+	if (index >= ARRAY_SIZE(ipu_fmt_yuv)) {
+		index -= ARRAY_SIZE(ipu_fmt_yuv);
+		if (index >= ARRAY_SIZE(ipu_fmt_rgb))
+			return -EINVAL;
+		fmt = &ipu_fmt_rgb[index];
+	} else {
+		fmt = &ipu_fmt_yuv[index];
+	}
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_fmt);
+
+int ipu_s_fmt(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	int ret;
+
+	ret = ipu_try_fmt(file, fh, f);
+	if (ret)
+		return ret;
+
+	pix->width = f->fmt.pix.width;
+	pix->height = f->fmt.pix.height;
+	pix->pixelformat = f->fmt.pix.pixelformat;
+	pix->bytesperline = f->fmt.pix.bytesperline;
+	pix->sizeimage = f->fmt.pix.sizeimage;
+	pix->colorspace = f->fmt.pix.colorspace;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_s_fmt);
+
+int ipu_s_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_rgb(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_s_fmt(file, fh, f, pix);
+}
+EXPORT_SYMBOL_GPL(ipu_s_fmt_rgb);
+
+int ipu_s_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	struct ipu_fmt *fmt;
+
+	fmt = ipu_find_fmt_yuv(f->fmt.pix.pixelformat);
+	if (!fmt)
+		return -EINVAL;
+
+	return ipu_s_fmt(file, fh, f, pix);
+}
+EXPORT_SYMBOL_GPL(ipu_s_fmt_yuv);
+
+int ipu_g_fmt(struct v4l2_format *f, struct v4l2_pix_format *pix)
+{
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.pixelformat = pix->pixelformat;
+	f->fmt.pix.bytesperline = pix->bytesperline;
+	f->fmt.pix.width = pix->width;
+	f->fmt.pix.height = pix->height;
+	f->fmt.pix.sizeimage = pix->sizeimage;
+	f->fmt.pix.colorspace = pix->colorspace;
+	f->fmt.pix.priv = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_g_fmt);
+
+int ipu_enum_framesizes(struct file *file, void *fh,
+			struct v4l2_frmsizeenum *fsize)
+{
+	struct ipu_fmt *fmt;
+
+	if (fsize->index != 0)
+		return -EINVAL;
+
+	fmt = ipu_find_fmt(fsize->pixel_format);
+	if (!fmt)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+	fsize->stepwise.min_width = 1;
+	fsize->stepwise.min_height = 1;
+	fsize->stepwise.max_width = 4096;
+	fsize->stepwise.max_height = 4096;
+	fsize->stepwise.step_width = fsize->stepwise.step_height = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_enum_framesizes);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/imx/imx-ipu.h b/drivers/media/platform/imx/imx-ipu.h
new file mode 100644
index 0000000..51c0982
--- /dev/null
+++ b/drivers/media/platform/imx/imx-ipu.h
@@ -0,0 +1,35 @@
+#ifndef __MEDIA_IMX_IPU_H
+#define __MEDIA_IMX_IPU_H
+#include <linux/videodev2.h>
+
+struct ipu_fmt {
+	u32 fourcc;
+	const char *name;
+	int bytes_per_pixel;
+};
+
+int ipu_enum_fmt(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f);
+int ipu_enum_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f);
+int ipu_enum_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_fmtdesc *f);
+struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat);
+struct ipu_fmt *ipu_find_fmt_yuv(unsigned int pixelformat);
+int ipu_try_fmt(struct file *file, void *fh,
+		struct v4l2_format *f);
+int ipu_try_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f);
+int ipu_try_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f);
+int ipu_s_fmt(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_s_fmt_rgb(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_s_fmt_yuv(struct file *file, void *fh,
+		struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_g_fmt(struct v4l2_format *f, struct v4l2_pix_format *pix);
+int ipu_enum_framesizes(struct file *file, void *fh,
+			struct v4l2_frmsizeenum *fsize);
+
+#endif /* __MEDIA_IMX_IPU_H */
-- 
2.1.4

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

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

* [PATCH v2 5/5] [media] imx-ipu: Add i.MX IPUv3 scaler driver
  2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
@ 2015-03-18 10:22   ` Philipp Zabel
  2015-03-18 10:22   ` Philipp Zabel
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	kernel, Sascha Hauer, Michael Olbrich, Philipp Zabel

From: Sascha Hauer <s.hauer@pengutronix.de>

This patch adds support for hardware accelerated scaling and color
space conversion between memory buffers using the IPUv3 IC.
Since the maximum output size of the IC unit is 1024x1024 pixels, multiple
IC tasks with overlapping tiles are used internally to scale and convert
larger frames.

The IC operates with a burst size of at least 8 pixels. Depending on the
frame width and scaling factor, up to 7 junk pixels may be written after
the end of the frame. The sizeimage is increased accordingly.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v1:
 - Removed removal of deinterlacer support left-overs
---
 drivers/media/platform/imx/Kconfig          |   9 +
 drivers/media/platform/imx/Makefile         |   1 +
 drivers/media/platform/imx/imx-ipu-scaler.c | 869 ++++++++++++++++++++++++++++
 drivers/media/platform/imx/imx-ipu.c        |   2 +-
 drivers/media/platform/imx/imx-ipu.h        |   1 +
 5 files changed, 881 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/platform/imx/imx-ipu-scaler.c

diff --git a/drivers/media/platform/imx/Kconfig b/drivers/media/platform/imx/Kconfig
index a90c973..4694367 100644
--- a/drivers/media/platform/imx/Kconfig
+++ b/drivers/media/platform/imx/Kconfig
@@ -1,2 +1,11 @@
 config VIDEO_IMX_IPU_COMMON
 	tristate
+
+config VIDEO_IMX_IPU_SCALER
+	tristate "i.MX5/6 IPUv3 based image scaler driver"
+	depends on VIDEO_DEV && IMX_IPUV3_CORE
+	select VIDEOBUF2_DMA_CONTIG
+	select VIDEO_IMX_IPU_COMMON
+	select V4L2_MEM2MEM_DEV
+	---help---
+	  This is a v4l2 scaler video driver for the IPUv3 on i.MX5/6.
diff --git a/drivers/media/platform/imx/Makefile b/drivers/media/platform/imx/Makefile
index 5de119c..f20aa0b 100644
--- a/drivers/media/platform/imx/Makefile
+++ b/drivers/media/platform/imx/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_VIDEO_IMX_IPU_COMMON)	+= imx-ipu.o
+obj-$(CONFIG_VIDEO_IMX_IPU_SCALER)	+= imx-ipu-scaler.o
diff --git a/drivers/media/platform/imx/imx-ipu-scaler.c b/drivers/media/platform/imx/imx-ipu-scaler.c
new file mode 100644
index 0000000..9cf8a70
--- /dev/null
+++ b/drivers/media/platform/imx/imx-ipu-scaler.c
@@ -0,0 +1,869 @@
+/*
+ * i.MX IPUv3 scaler driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * based on the mem2mem test driver
+ *
+ * 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/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <video/imx-ipu-v3.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "imx-ipu.h"
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 4096
+#define MAX_H 4096
+#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE	(1 << 0)
+#define MEM2MEM_OUTPUT	(1 << 1)
+
+#define MEM2MEM_NAME		"imx-ipuv3-scale"
+
+/* Per queue */
+#define MEM2MEM_DEF_NUM_BUFS	VIDEO_MAX_FRAME
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT	(64 * 1024 * 1024)
+
+#define fh_to_ctx(__fh)	container_of(__fh, struct ipu_scale_ctx, fh)
+
+enum {
+	V4L2_M2M_SRC = 0,
+	V4L2_M2M_DST = 1,
+};
+
+struct ipu_scale_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd;
+	struct device		*dev;
+	struct ipu_soc		*ipu;
+
+	atomic_t		num_inst;
+	spinlock_t		irqlock;
+
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct mutex		dev_mutex;
+};
+
+/* Per-queue, driver-specific private data */
+struct ipu_scale_q_data {
+	struct v4l2_pix_format	cur_fmt;
+	struct v4l2_rect	rect;
+};
+
+struct ipu_scale_ctx {
+	struct ipu_scale_dev	*ipu_scaler;
+
+	struct v4l2_fh		fh;
+	struct vb2_alloc_ctx	*alloc_ctx;
+	struct ipu_scale_q_data	q_data[2];
+	struct work_struct	work;
+	struct completion	completion;
+	struct work_struct	skip_run;
+	int			error;
+	int			aborting;
+	enum ipu_image_scale_ctrl ctrl;
+	struct image_convert_ctx *icc;
+	int			num_tiles;
+};
+
+static struct ipu_scale_q_data *get_q_data(struct ipu_scale_ctx *ctx,
+					   enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		return &ctx->q_data[V4L2_M2M_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &ctx->q_data[V4L2_M2M_DST];
+	default:
+		BUG();
+	}
+	return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+	struct ipu_scale_ctx *ctx = priv;
+
+	if (ctx->aborting)
+		return 0;
+
+	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < 1
+	    || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < 1) {
+		dev_dbg(ctx->ipu_scaler->dev, "Not enough buffers available\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static void job_abort(void *priv)
+{
+	struct ipu_scale_ctx *ctx = priv;
+
+	ctx->aborting = 1;
+}
+
+static void ipu_complete(void *priv, int err)
+{
+	struct ipu_scale_dev *ipu_scaler = priv;
+	struct ipu_scale_ctx *curr_ctx;
+
+	curr_ctx = v4l2_m2m_get_curr_priv(ipu_scaler->m2m_dev);
+
+	if (NULL == curr_ctx) {
+		dev_dbg(ipu_scaler->dev,
+			"Instance released before the end of transaction\n");
+		return;
+	}
+
+	curr_ctx->error = err;
+	complete(&curr_ctx->completion);
+}
+
+static void device_run(void *priv)
+{
+	struct ipu_scale_ctx *ctx = priv;
+
+	schedule_work(&ctx->work);
+}
+
+static void ipu_scaler_work(struct work_struct *work)
+{
+	struct ipu_scale_ctx *ctx = container_of(work, struct ipu_scale_ctx,
+						 work);
+	struct ipu_scale_dev *ipu_scaler = ctx->ipu_scaler;
+	struct vb2_buffer *src_buf, *dst_buf;
+	struct ipu_scale_q_data *q_data;
+	struct v4l2_pix_format *pix;
+	struct ipu_image in, out;
+	int err = -ETIMEDOUT;
+	unsigned long flags;
+
+	/*
+	 * If streamoff dequeued all buffers before we could get the lock,
+	 * just bail out immediately.
+	 */
+	if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) ||
+		!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
+		WARN_ON(1);
+		schedule_work(&ctx->skip_run);
+		return;
+	}
+
+	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	in.phys0 = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+	out.phys0 = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+
+	if (!ctx->num_tiles) {
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		pix = &q_data->cur_fmt;
+
+		in.pix.width = pix->width;
+		in.pix.height = pix->height;
+		in.pix.bytesperline = pix->bytesperline;
+		in.pix.pixelformat = pix->pixelformat;
+		in.rect = q_data->rect;
+
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		pix = &q_data->cur_fmt;
+
+		out.pix.width = pix->width;
+		out.pix.height = pix->height;
+		out.pix.bytesperline = pix->bytesperline;
+		out.pix.pixelformat = pix->pixelformat;
+		out.rect = q_data->rect;
+
+		kfree(ctx->icc);
+		ctx->icc = ipu_image_convert_prepare(ipu_scaler->ipu, &in,
+						     &out, ctx->ctrl,
+						     &ctx->num_tiles);
+		if (IS_ERR(ctx->icc)) {
+			schedule_work(&ctx->skip_run);
+			return;
+		}
+	}
+
+	ipu_image_convert_run(ipu_scaler->ipu, &in, &out, ctx->icc,
+			      ctx->num_tiles, ipu_complete, ipu_scaler, false);
+
+	if (!wait_for_completion_timeout(&ctx->completion,
+					 msecs_to_jiffies(300))) {
+		dev_err(ipu_scaler->dev,
+			"Timeout waiting for scaling result\n");
+		err = -ETIMEDOUT;
+	} else {
+		err = ctx->error;
+	}
+
+	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+	dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+	dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
+
+	spin_lock_irqsave(&ipu_scaler->irqlock, flags);
+	v4l2_m2m_buf_done(src_buf, err ? VB2_BUF_STATE_ERROR :
+					 VB2_BUF_STATE_DONE);
+	v4l2_m2m_buf_done(dst_buf, err ? VB2_BUF_STATE_ERROR :
+					 VB2_BUF_STATE_DONE);
+	spin_unlock_irqrestore(&ipu_scaler->irqlock, flags);
+
+	v4l2_m2m_job_finish(ipu_scaler->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+	strncpy(cap->bus_info, "platform:" MEM2MEM_NAME,
+		sizeof(cap->bus_info) - 1);
+	/*
+	 * This is only a mem-to-mem video device. The capture and output
+	 * device capability flags are left for backward compatibility and
+	 * are scheduled for removal.
+	 */
+	cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	return ipu_enum_fmt(file, priv, f);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	return ipu_enum_fmt(file, priv, f);
+}
+
+static int vidioc_g_fmt(struct ipu_scale_ctx *ctx, struct v4l2_format *f)
+{
+	struct vb2_queue *vq;
+	struct ipu_scale_q_data *q_data;
+	struct v4l2_pix_format *pix;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+	pix = &q_data->cur_fmt;
+
+	return ipu_g_fmt(f, pix);
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	int ret;
+
+	ret = ipu_try_fmt(file, priv, f);
+
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	f->fmt.pix.sizeimage += 7 * 4;
+
+	return ret;
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	f->fmt.pix.width &= ~0x7;
+	return ipu_try_fmt(file, priv, f);
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct ipu_scale_q_data *q_data;
+	struct vb2_queue *vq;
+	struct ipu_scale_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		v4l2_err(&ctx->ipu_scaler->v4l2_dev, "%s queue busy\n",
+			 __func__);
+		return -EBUSY;
+	}
+
+	ret = ipu_s_fmt(file, priv, f, &q_data->cur_fmt);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	q_data->cur_fmt.sizeimage = f->fmt.pix.sizeimage;
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		f->fmt.pix.sizeimage += 7 * 4;
+
+	/* Reset cropping/composing rectangle */
+	q_data->rect.left = 0;
+	q_data->rect.top = 0;
+	q_data->rect.width = q_data->cur_fmt.width;
+	q_data->rect.height = q_data->cur_fmt.height;
+
+	/* reset scaling ctrl */
+	ctx->ctrl = IPU_IMAGE_SCALE_PIXELPERFECT;
+
+	return 0;
+}
+
+static int vidioc_g_selection(struct file *file, void *priv,
+			      struct v4l2_selection *s)
+{
+	struct ipu_scale_ctx *ctx = fh_to_ctx(priv);
+	struct ipu_scale_q_data *q_data;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = q_data->cur_fmt.width;
+		s->r.height = q_data->cur_fmt.height;
+		break;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = q_data->cur_fmt.width;
+		s->r.height = q_data->cur_fmt.height;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		s->r = q_data->rect;
+		break;
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_PADDED:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = q_data->cur_fmt.width;
+		s->r.height = q_data->cur_fmt.height;
+		break;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_selection(struct file *file, void *priv,
+			      struct v4l2_selection *s)
+{
+	struct ipu_scale_ctx *ctx = fh_to_ctx(priv);
+	struct ipu_scale_q_data *q_data;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		ctx->ctrl = IPU_IMAGE_SCALE_ROUND_DOWN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	q_data = get_q_data(ctx, s->type);
+
+	/* The input's frame width to the IC must be a multiple of 8 pixels
+	 * When performing resizing the frame width must be multiple of burst
+	 * size - 8 or 16 pixels as defined by CB#_BURST_16 parameter.
+	 */
+	if (s->flags & V4L2_SEL_FLAG_GE)
+		s->r.width = round_up(s->r.width, 8);
+	if (s->flags & V4L2_SEL_FLAG_LE)
+		s->r.width = round_down(s->r.width, 8);
+	s->r.width = clamp_t(unsigned int, s->r.width, 8,
+			     round_down(q_data->cur_fmt.width, 8));
+	s->r.height = clamp_t(unsigned int, s->r.height, 1,
+			      q_data->cur_fmt.height);
+	s->r.left = clamp_t(unsigned int, s->r.left, 0,
+			    q_data->cur_fmt.width - s->r.width);
+	s->r.top = clamp_t(unsigned int, s->r.top, 0,
+			   q_data->cur_fmt.height - s->r.height);
+
+	/* V4L2_SEL_FLAG_KEEP_CONFIG is only valid for subdevices */
+	q_data->rect = s->r;
+	ctx->num_tiles = 0;
+
+	return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	return ipu_enum_framesizes(file, fh, fsize);
+}
+
+static const struct v4l2_ioctl_ops ipu_scale_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt,
+
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out	= vidioc_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out	= vidioc_s_fmt,
+
+	.vidioc_g_selection	= vidioc_g_selection,
+	.vidioc_s_selection	= vidioc_s_selection,
+
+	.vidioc_reqbufs		= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_querybuf	= v4l2_m2m_ioctl_querybuf,
+
+	.vidioc_qbuf		= v4l2_m2m_ioctl_qbuf,
+	.vidioc_expbuf		= v4l2_m2m_ioctl_expbuf,
+	.vidioc_dqbuf		= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_create_bufs	= v4l2_m2m_ioctl_create_bufs,
+
+	.vidioc_streamon	= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff	= v4l2_m2m_ioctl_streamoff,
+
+	.vidioc_enum_framesizes = vidioc_enum_framesizes,
+};
+
+static void ipu_scale_skip_run(struct work_struct *work)
+{
+	struct ipu_scale_ctx *ctx = container_of(work, struct ipu_scale_ctx,
+						 skip_run);
+
+	v4l2_m2m_job_finish(ctx->ipu_scaler->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+
+/*
+ * Queue operations
+ */
+
+static int ipu_scale_queue_setup(struct vb2_queue *vq,
+		const struct v4l2_format *fmt,
+		unsigned int *nbuffers,
+		unsigned int *nplanes, unsigned int sizes[],
+		void *alloc_ctxs[])
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(vq);
+	struct ipu_scale_q_data *q_data;
+	unsigned int size, count = *nbuffers;
+	struct v4l2_pix_format *pix;
+
+	q_data = get_q_data(ctx, vq->type);
+	pix = &q_data->cur_fmt;
+
+	size = pix->sizeimage;
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		size += 7 * 4;
+
+	while (size * count > MEM2MEM_VID_MEM_LIMIT)
+		(count)--;
+
+	*nplanes = 1;
+	*nbuffers = count;
+	sizes[0] = size;
+
+	ctx->alloc_ctx = vb2_dma_contig_init_ctx(ctx->ipu_scaler->dev);
+	if (IS_ERR(ctx->alloc_ctx))
+		return PTR_ERR(ctx->alloc_ctx);
+
+	alloc_ctxs[0] = ctx->alloc_ctx;
+
+	dev_dbg(ctx->ipu_scaler->dev, "get %d buffer(s) of size %d each.\n",
+		count, size);
+
+	return 0;
+}
+
+static int ipu_scale_buf_prepare(struct vb2_buffer *vb)
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct ipu_scale_q_data *q_data;
+	struct v4l2_pix_format *pix;
+	unsigned int plane_size;
+
+	dev_dbg(ctx->ipu_scaler->dev, "type: %d\n", vb->vb2_queue->type);
+
+	q_data = get_q_data(ctx, vb->vb2_queue->type);
+	pix = &q_data->cur_fmt;
+	plane_size = pix->sizeimage;
+
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		plane_size += 7 * 4;
+	if (vb2_plane_size(vb, 0) < plane_size) {
+		dev_dbg(ctx->ipu_scaler->dev,
+				"%s data will not fit into plane (%lu < %lu)\n",
+				__func__, vb2_plane_size(vb, 0),
+				(long)plane_size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, pix->sizeimage);
+
+	return 0;
+}
+
+static void ipu_scale_buf_queue(struct vb2_buffer *vb)
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+}
+
+static void ipu_scale_stop_streaming(struct vb2_queue *q)
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(q);
+	struct vb2_buffer *buf;
+
+	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+	} else {
+		while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+	}
+}
+
+static struct vb2_ops ipu_scale_qops = {
+	.queue_setup	= ipu_scale_queue_setup,
+	.buf_prepare	= ipu_scale_buf_prepare,
+	.buf_queue	= ipu_scale_buf_queue,
+	.wait_prepare	= vb2_ops_wait_prepare,
+	.wait_finish	= vb2_ops_wait_finish,
+	.stop_streaming = ipu_scale_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct ipu_scale_ctx *ctx = priv;
+	int ret;
+
+	memset(src_vq, 0, sizeof(*src_vq));
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->ops = &ipu_scale_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &ctx->ipu_scaler->dev_mutex;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	memset(dst_vq, 0, sizeof(*dst_vq));
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->ops = &ipu_scale_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &ctx->ipu_scaler->dev_mutex;
+
+	return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int ipu_scale_open(struct file *file)
+{
+	struct ipu_scale_dev *ipu_scaler = video_drvdata(file);
+	struct ipu_scale_ctx *ctx = NULL;
+	const int width = 720;
+	const int height = 576;
+	int i;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	INIT_WORK(&ctx->skip_run, ipu_scale_skip_run);
+	INIT_WORK(&ctx->work, ipu_scaler_work);
+	init_completion(&ctx->completion);
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+	ctx->ipu_scaler = ipu_scaler;
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ipu_scaler->m2m_dev, ctx,
+					    &queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		int ret = PTR_ERR(ctx->fh.m2m_ctx);
+
+		kfree(ctx);
+		return ret;
+	}
+
+	for (i = 0; i < 2; i++) {
+		ctx->q_data[i].cur_fmt.width = width;
+		ctx->q_data[i].cur_fmt.height = height;
+		ctx->q_data[i].cur_fmt.bytesperline = width;
+		ctx->q_data[i].cur_fmt.pixelformat = V4L2_PIX_FMT_YUV420;
+		ctx->q_data[i].cur_fmt.sizeimage = width * height * 3 / 2;
+		ctx->q_data[i].cur_fmt.colorspace = V4L2_COLORSPACE_REC709;
+		ctx->q_data[i].rect.left = 0;
+		ctx->q_data[i].rect.top = 0;
+		ctx->q_data[i].rect.width = width;
+		ctx->q_data[i].rect.height = height;
+	}
+
+	ctx->ctrl = IPU_IMAGE_SCALE_PIXELPERFECT;
+	ctx->num_tiles = 0;
+
+	atomic_inc(&ipu_scaler->num_inst);
+
+	dev_dbg(ipu_scaler->dev, "Created instance %p, m2m_ctx: %p\n",
+			ctx, ctx->fh.m2m_ctx);
+
+	return 0;
+}
+
+static int ipu_scale_release(struct file *file)
+{
+	struct ipu_scale_dev *ipu_scaler = video_drvdata(file);
+	struct ipu_scale_ctx *ctx = fh_to_ctx(file->private_data);
+
+	dev_dbg(ipu_scaler->dev, "Releasing instance %p\n", ctx);
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx->icc);
+	kfree(ctx);
+
+	atomic_dec(&ipu_scaler->num_inst);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations ipu_scale_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ipu_scale_open,
+	.release	= ipu_scale_release,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+};
+
+static struct video_device ipu_scale_videodev = {
+	.name		= MEM2MEM_NAME,
+	.fops		= &ipu_scale_fops,
+	.ioctl_ops	= &ipu_scale_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.vfl_dir	= VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= device_run,
+	.job_ready	= job_ready,
+	.job_abort	= job_abort,
+};
+
+static u64 vout_dmamask = ~(u32)0;
+
+static int ipu_scale_probe(struct platform_device *pdev)
+{
+	struct ipu_scale_dev *ipu_scaler;
+	struct video_device *vfd;
+	struct ipu_soc *ipu = dev_get_drvdata(pdev->dev.parent);
+	int ret;
+
+	pdev->dev.dma_mask = &vout_dmamask;
+	pdev->dev.coherent_dma_mask = 0xffffffff;
+
+	ipu_scaler = devm_kzalloc(&pdev->dev, sizeof(*ipu_scaler), GFP_KERNEL);
+	if (!ipu_scaler)
+		return -ENOMEM;
+
+	ipu_scaler->ipu = ipu;
+	ipu_scaler->dev = &pdev->dev;
+
+	spin_lock_init(&ipu_scaler->irqlock);
+	mutex_init(&ipu_scaler->dev_mutex);
+
+	ret = v4l2_device_register(&pdev->dev, &ipu_scaler->v4l2_dev);
+	if (ret)
+		return ret;
+
+	atomic_set(&ipu_scaler->num_inst, 0);
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		dev_err(ipu_scaler->dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+
+	*vfd = ipu_scale_videodev;
+	vfd->lock = &ipu_scaler->dev_mutex;
+	vfd->v4l2_dev = &ipu_scaler->v4l2_dev;
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		dev_err(ipu_scaler->dev, "Failed to register video device\n");
+		goto rel_vdev;
+	}
+
+	video_set_drvdata(vfd, ipu_scaler);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", ipu_scale_videodev.name);
+	ipu_scaler->vfd = vfd;
+	dev_dbg(ipu_scaler->dev, "Device registered as /dev/video%d\n",
+		vfd->num);
+
+	platform_set_drvdata(pdev, ipu_scaler);
+
+	ipu_scaler->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(ipu_scaler->m2m_dev)) {
+		dev_err(ipu_scaler->dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(ipu_scaler->m2m_dev);
+		goto err_m2m;
+	}
+
+	return 0;
+
+	v4l2_m2m_release(ipu_scaler->m2m_dev);
+err_m2m:
+	video_unregister_device(ipu_scaler->vfd);
+rel_vdev:
+	video_device_release(vfd);
+unreg_dev:
+	v4l2_device_unregister(&ipu_scaler->v4l2_dev);
+
+	return ret;
+}
+
+static int ipu_scale_remove(struct platform_device *pdev)
+{
+	struct ipu_scale_dev *ipu_scaler =
+		(struct ipu_scale_dev *)platform_get_drvdata(pdev);
+
+	v4l2_m2m_release(ipu_scaler->m2m_dev);
+	video_unregister_device(ipu_scaler->vfd);
+	v4l2_device_unregister(&ipu_scaler->v4l2_dev);
+	kfree(ipu_scaler);
+
+	return 0;
+}
+
+static const struct platform_device_id ipu_scale_id[] = {
+	{ "imx-ipuv3-scaler" },
+	{}
+};
+MODULE_DEVICE_TABLE(platform, ipu_scale_id);
+
+static struct platform_driver ipu_scale_pdrv = {
+	.probe		= ipu_scale_probe,
+	.remove		= ipu_scale_remove,
+	.driver		= {
+		.name	= "imx-ipuv3-scaler",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static void __exit ipu_scale_exit(void)
+{
+	platform_driver_unregister(&ipu_scale_pdrv);
+}
+
+static int __init ipu_scale_init(void)
+{
+	return  platform_driver_register(&ipu_scale_pdrv);
+}
+
+module_init(ipu_scale_init);
+module_exit(ipu_scale_exit);
+
+MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/imx/imx-ipu.c b/drivers/media/platform/imx/imx-ipu.c
index c1b8637..402cc8b 100644
--- a/drivers/media/platform/imx/imx-ipu.c
+++ b/drivers/media/platform/imx/imx-ipu.c
@@ -102,7 +102,7 @@ struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat)
 }
 EXPORT_SYMBOL_GPL(ipu_find_fmt_rgb);
 
-static struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat)
+struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat)
 {
 	struct ipu_fmt *fmt;
 
diff --git a/drivers/media/platform/imx/imx-ipu.h b/drivers/media/platform/imx/imx-ipu.h
index 51c0982..288ee79 100644
--- a/drivers/media/platform/imx/imx-ipu.h
+++ b/drivers/media/platform/imx/imx-ipu.h
@@ -16,6 +16,7 @@ int ipu_enum_fmt_yuv(struct file *file, void *fh,
 		struct v4l2_fmtdesc *f);
 struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat);
 struct ipu_fmt *ipu_find_fmt_yuv(unsigned int pixelformat);
+struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat);
 int ipu_try_fmt(struct file *file, void *fh,
 		struct v4l2_format *f);
 int ipu_try_fmt_rgb(struct file *file, void *fh,
-- 
2.1.4


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

* [PATCH v2 5/5] [media] imx-ipu: Add i.MX IPUv3 scaler driver
@ 2015-03-18 10:22   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-03-18 10:22 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, dri-devel, Hans Verkuil, kernel,
	Steve Longerbeam, Jean-Michel Hautbois, Michael Olbrich

From: Sascha Hauer <s.hauer@pengutronix.de>

This patch adds support for hardware accelerated scaling and color
space conversion between memory buffers using the IPUv3 IC.
Since the maximum output size of the IC unit is 1024x1024 pixels, multiple
IC tasks with overlapping tiles are used internally to scale and convert
larger frames.

The IC operates with a burst size of at least 8 pixels. Depending on the
frame width and scaling factor, up to 7 junk pixels may be written after
the end of the frame. The sizeimage is increased accordingly.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v1:
 - Removed removal of deinterlacer support left-overs
---
 drivers/media/platform/imx/Kconfig          |   9 +
 drivers/media/platform/imx/Makefile         |   1 +
 drivers/media/platform/imx/imx-ipu-scaler.c | 869 ++++++++++++++++++++++++++++
 drivers/media/platform/imx/imx-ipu.c        |   2 +-
 drivers/media/platform/imx/imx-ipu.h        |   1 +
 5 files changed, 881 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/platform/imx/imx-ipu-scaler.c

diff --git a/drivers/media/platform/imx/Kconfig b/drivers/media/platform/imx/Kconfig
index a90c973..4694367 100644
--- a/drivers/media/platform/imx/Kconfig
+++ b/drivers/media/platform/imx/Kconfig
@@ -1,2 +1,11 @@
 config VIDEO_IMX_IPU_COMMON
 	tristate
+
+config VIDEO_IMX_IPU_SCALER
+	tristate "i.MX5/6 IPUv3 based image scaler driver"
+	depends on VIDEO_DEV && IMX_IPUV3_CORE
+	select VIDEOBUF2_DMA_CONTIG
+	select VIDEO_IMX_IPU_COMMON
+	select V4L2_MEM2MEM_DEV
+	---help---
+	  This is a v4l2 scaler video driver for the IPUv3 on i.MX5/6.
diff --git a/drivers/media/platform/imx/Makefile b/drivers/media/platform/imx/Makefile
index 5de119c..f20aa0b 100644
--- a/drivers/media/platform/imx/Makefile
+++ b/drivers/media/platform/imx/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_VIDEO_IMX_IPU_COMMON)	+= imx-ipu.o
+obj-$(CONFIG_VIDEO_IMX_IPU_SCALER)	+= imx-ipu-scaler.o
diff --git a/drivers/media/platform/imx/imx-ipu-scaler.c b/drivers/media/platform/imx/imx-ipu-scaler.c
new file mode 100644
index 0000000..9cf8a70
--- /dev/null
+++ b/drivers/media/platform/imx/imx-ipu-scaler.c
@@ -0,0 +1,869 @@
+/*
+ * i.MX IPUv3 scaler driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * based on the mem2mem test driver
+ *
+ * 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/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <video/imx-ipu-v3.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "imx-ipu.h"
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 4096
+#define MAX_H 4096
+#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE	(1 << 0)
+#define MEM2MEM_OUTPUT	(1 << 1)
+
+#define MEM2MEM_NAME		"imx-ipuv3-scale"
+
+/* Per queue */
+#define MEM2MEM_DEF_NUM_BUFS	VIDEO_MAX_FRAME
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT	(64 * 1024 * 1024)
+
+#define fh_to_ctx(__fh)	container_of(__fh, struct ipu_scale_ctx, fh)
+
+enum {
+	V4L2_M2M_SRC = 0,
+	V4L2_M2M_DST = 1,
+};
+
+struct ipu_scale_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd;
+	struct device		*dev;
+	struct ipu_soc		*ipu;
+
+	atomic_t		num_inst;
+	spinlock_t		irqlock;
+
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct mutex		dev_mutex;
+};
+
+/* Per-queue, driver-specific private data */
+struct ipu_scale_q_data {
+	struct v4l2_pix_format	cur_fmt;
+	struct v4l2_rect	rect;
+};
+
+struct ipu_scale_ctx {
+	struct ipu_scale_dev	*ipu_scaler;
+
+	struct v4l2_fh		fh;
+	struct vb2_alloc_ctx	*alloc_ctx;
+	struct ipu_scale_q_data	q_data[2];
+	struct work_struct	work;
+	struct completion	completion;
+	struct work_struct	skip_run;
+	int			error;
+	int			aborting;
+	enum ipu_image_scale_ctrl ctrl;
+	struct image_convert_ctx *icc;
+	int			num_tiles;
+};
+
+static struct ipu_scale_q_data *get_q_data(struct ipu_scale_ctx *ctx,
+					   enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		return &ctx->q_data[V4L2_M2M_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &ctx->q_data[V4L2_M2M_DST];
+	default:
+		BUG();
+	}
+	return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+	struct ipu_scale_ctx *ctx = priv;
+
+	if (ctx->aborting)
+		return 0;
+
+	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < 1
+	    || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < 1) {
+		dev_dbg(ctx->ipu_scaler->dev, "Not enough buffers available\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static void job_abort(void *priv)
+{
+	struct ipu_scale_ctx *ctx = priv;
+
+	ctx->aborting = 1;
+}
+
+static void ipu_complete(void *priv, int err)
+{
+	struct ipu_scale_dev *ipu_scaler = priv;
+	struct ipu_scale_ctx *curr_ctx;
+
+	curr_ctx = v4l2_m2m_get_curr_priv(ipu_scaler->m2m_dev);
+
+	if (NULL == curr_ctx) {
+		dev_dbg(ipu_scaler->dev,
+			"Instance released before the end of transaction\n");
+		return;
+	}
+
+	curr_ctx->error = err;
+	complete(&curr_ctx->completion);
+}
+
+static void device_run(void *priv)
+{
+	struct ipu_scale_ctx *ctx = priv;
+
+	schedule_work(&ctx->work);
+}
+
+static void ipu_scaler_work(struct work_struct *work)
+{
+	struct ipu_scale_ctx *ctx = container_of(work, struct ipu_scale_ctx,
+						 work);
+	struct ipu_scale_dev *ipu_scaler = ctx->ipu_scaler;
+	struct vb2_buffer *src_buf, *dst_buf;
+	struct ipu_scale_q_data *q_data;
+	struct v4l2_pix_format *pix;
+	struct ipu_image in, out;
+	int err = -ETIMEDOUT;
+	unsigned long flags;
+
+	/*
+	 * If streamoff dequeued all buffers before we could get the lock,
+	 * just bail out immediately.
+	 */
+	if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) ||
+		!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
+		WARN_ON(1);
+		schedule_work(&ctx->skip_run);
+		return;
+	}
+
+	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	in.phys0 = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+	out.phys0 = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+
+	if (!ctx->num_tiles) {
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		pix = &q_data->cur_fmt;
+
+		in.pix.width = pix->width;
+		in.pix.height = pix->height;
+		in.pix.bytesperline = pix->bytesperline;
+		in.pix.pixelformat = pix->pixelformat;
+		in.rect = q_data->rect;
+
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		pix = &q_data->cur_fmt;
+
+		out.pix.width = pix->width;
+		out.pix.height = pix->height;
+		out.pix.bytesperline = pix->bytesperline;
+		out.pix.pixelformat = pix->pixelformat;
+		out.rect = q_data->rect;
+
+		kfree(ctx->icc);
+		ctx->icc = ipu_image_convert_prepare(ipu_scaler->ipu, &in,
+						     &out, ctx->ctrl,
+						     &ctx->num_tiles);
+		if (IS_ERR(ctx->icc)) {
+			schedule_work(&ctx->skip_run);
+			return;
+		}
+	}
+
+	ipu_image_convert_run(ipu_scaler->ipu, &in, &out, ctx->icc,
+			      ctx->num_tiles, ipu_complete, ipu_scaler, false);
+
+	if (!wait_for_completion_timeout(&ctx->completion,
+					 msecs_to_jiffies(300))) {
+		dev_err(ipu_scaler->dev,
+			"Timeout waiting for scaling result\n");
+		err = -ETIMEDOUT;
+	} else {
+		err = ctx->error;
+	}
+
+	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+	dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+	dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
+
+	spin_lock_irqsave(&ipu_scaler->irqlock, flags);
+	v4l2_m2m_buf_done(src_buf, err ? VB2_BUF_STATE_ERROR :
+					 VB2_BUF_STATE_DONE);
+	v4l2_m2m_buf_done(dst_buf, err ? VB2_BUF_STATE_ERROR :
+					 VB2_BUF_STATE_DONE);
+	spin_unlock_irqrestore(&ipu_scaler->irqlock, flags);
+
+	v4l2_m2m_job_finish(ipu_scaler->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+	strncpy(cap->bus_info, "platform:" MEM2MEM_NAME,
+		sizeof(cap->bus_info) - 1);
+	/*
+	 * This is only a mem-to-mem video device. The capture and output
+	 * device capability flags are left for backward compatibility and
+	 * are scheduled for removal.
+	 */
+	cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	return ipu_enum_fmt(file, priv, f);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	return ipu_enum_fmt(file, priv, f);
+}
+
+static int vidioc_g_fmt(struct ipu_scale_ctx *ctx, struct v4l2_format *f)
+{
+	struct vb2_queue *vq;
+	struct ipu_scale_q_data *q_data;
+	struct v4l2_pix_format *pix;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+	pix = &q_data->cur_fmt;
+
+	return ipu_g_fmt(f, pix);
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	int ret;
+
+	ret = ipu_try_fmt(file, priv, f);
+
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	f->fmt.pix.sizeimage += 7 * 4;
+
+	return ret;
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	f->fmt.pix.width &= ~0x7;
+	return ipu_try_fmt(file, priv, f);
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct ipu_scale_q_data *q_data;
+	struct vb2_queue *vq;
+	struct ipu_scale_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		v4l2_err(&ctx->ipu_scaler->v4l2_dev, "%s queue busy\n",
+			 __func__);
+		return -EBUSY;
+	}
+
+	ret = ipu_s_fmt(file, priv, f, &q_data->cur_fmt);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	q_data->cur_fmt.sizeimage = f->fmt.pix.sizeimage;
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		f->fmt.pix.sizeimage += 7 * 4;
+
+	/* Reset cropping/composing rectangle */
+	q_data->rect.left = 0;
+	q_data->rect.top = 0;
+	q_data->rect.width = q_data->cur_fmt.width;
+	q_data->rect.height = q_data->cur_fmt.height;
+
+	/* reset scaling ctrl */
+	ctx->ctrl = IPU_IMAGE_SCALE_PIXELPERFECT;
+
+	return 0;
+}
+
+static int vidioc_g_selection(struct file *file, void *priv,
+			      struct v4l2_selection *s)
+{
+	struct ipu_scale_ctx *ctx = fh_to_ctx(priv);
+	struct ipu_scale_q_data *q_data;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = q_data->cur_fmt.width;
+		s->r.height = q_data->cur_fmt.height;
+		break;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = q_data->cur_fmt.width;
+		s->r.height = q_data->cur_fmt.height;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		s->r = q_data->rect;
+		break;
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_PADDED:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = q_data->cur_fmt.width;
+		s->r.height = q_data->cur_fmt.height;
+		break;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_selection(struct file *file, void *priv,
+			      struct v4l2_selection *s)
+{
+	struct ipu_scale_ctx *ctx = fh_to_ctx(priv);
+	struct ipu_scale_q_data *q_data;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		ctx->ctrl = IPU_IMAGE_SCALE_ROUND_DOWN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	q_data = get_q_data(ctx, s->type);
+
+	/* The input's frame width to the IC must be a multiple of 8 pixels
+	 * When performing resizing the frame width must be multiple of burst
+	 * size - 8 or 16 pixels as defined by CB#_BURST_16 parameter.
+	 */
+	if (s->flags & V4L2_SEL_FLAG_GE)
+		s->r.width = round_up(s->r.width, 8);
+	if (s->flags & V4L2_SEL_FLAG_LE)
+		s->r.width = round_down(s->r.width, 8);
+	s->r.width = clamp_t(unsigned int, s->r.width, 8,
+			     round_down(q_data->cur_fmt.width, 8));
+	s->r.height = clamp_t(unsigned int, s->r.height, 1,
+			      q_data->cur_fmt.height);
+	s->r.left = clamp_t(unsigned int, s->r.left, 0,
+			    q_data->cur_fmt.width - s->r.width);
+	s->r.top = clamp_t(unsigned int, s->r.top, 0,
+			   q_data->cur_fmt.height - s->r.height);
+
+	/* V4L2_SEL_FLAG_KEEP_CONFIG is only valid for subdevices */
+	q_data->rect = s->r;
+	ctx->num_tiles = 0;
+
+	return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	return ipu_enum_framesizes(file, fh, fsize);
+}
+
+static const struct v4l2_ioctl_ops ipu_scale_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt,
+
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out	= vidioc_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out	= vidioc_s_fmt,
+
+	.vidioc_g_selection	= vidioc_g_selection,
+	.vidioc_s_selection	= vidioc_s_selection,
+
+	.vidioc_reqbufs		= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_querybuf	= v4l2_m2m_ioctl_querybuf,
+
+	.vidioc_qbuf		= v4l2_m2m_ioctl_qbuf,
+	.vidioc_expbuf		= v4l2_m2m_ioctl_expbuf,
+	.vidioc_dqbuf		= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_create_bufs	= v4l2_m2m_ioctl_create_bufs,
+
+	.vidioc_streamon	= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff	= v4l2_m2m_ioctl_streamoff,
+
+	.vidioc_enum_framesizes = vidioc_enum_framesizes,
+};
+
+static void ipu_scale_skip_run(struct work_struct *work)
+{
+	struct ipu_scale_ctx *ctx = container_of(work, struct ipu_scale_ctx,
+						 skip_run);
+
+	v4l2_m2m_job_finish(ctx->ipu_scaler->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+
+/*
+ * Queue operations
+ */
+
+static int ipu_scale_queue_setup(struct vb2_queue *vq,
+		const struct v4l2_format *fmt,
+		unsigned int *nbuffers,
+		unsigned int *nplanes, unsigned int sizes[],
+		void *alloc_ctxs[])
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(vq);
+	struct ipu_scale_q_data *q_data;
+	unsigned int size, count = *nbuffers;
+	struct v4l2_pix_format *pix;
+
+	q_data = get_q_data(ctx, vq->type);
+	pix = &q_data->cur_fmt;
+
+	size = pix->sizeimage;
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		size += 7 * 4;
+
+	while (size * count > MEM2MEM_VID_MEM_LIMIT)
+		(count)--;
+
+	*nplanes = 1;
+	*nbuffers = count;
+	sizes[0] = size;
+
+	ctx->alloc_ctx = vb2_dma_contig_init_ctx(ctx->ipu_scaler->dev);
+	if (IS_ERR(ctx->alloc_ctx))
+		return PTR_ERR(ctx->alloc_ctx);
+
+	alloc_ctxs[0] = ctx->alloc_ctx;
+
+	dev_dbg(ctx->ipu_scaler->dev, "get %d buffer(s) of size %d each.\n",
+		count, size);
+
+	return 0;
+}
+
+static int ipu_scale_buf_prepare(struct vb2_buffer *vb)
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct ipu_scale_q_data *q_data;
+	struct v4l2_pix_format *pix;
+	unsigned int plane_size;
+
+	dev_dbg(ctx->ipu_scaler->dev, "type: %d\n", vb->vb2_queue->type);
+
+	q_data = get_q_data(ctx, vb->vb2_queue->type);
+	pix = &q_data->cur_fmt;
+	plane_size = pix->sizeimage;
+
+	/*
+	 * Leave enough output space for worst-case overhead caused by 8 pixel
+	 * burst size: 7 RGBA pixels.
+	 */
+	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		plane_size += 7 * 4;
+	if (vb2_plane_size(vb, 0) < plane_size) {
+		dev_dbg(ctx->ipu_scaler->dev,
+				"%s data will not fit into plane (%lu < %lu)\n",
+				__func__, vb2_plane_size(vb, 0),
+				(long)plane_size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, pix->sizeimage);
+
+	return 0;
+}
+
+static void ipu_scale_buf_queue(struct vb2_buffer *vb)
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+}
+
+static void ipu_scale_stop_streaming(struct vb2_queue *q)
+{
+	struct ipu_scale_ctx *ctx = vb2_get_drv_priv(q);
+	struct vb2_buffer *buf;
+
+	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+	} else {
+		while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+	}
+}
+
+static struct vb2_ops ipu_scale_qops = {
+	.queue_setup	= ipu_scale_queue_setup,
+	.buf_prepare	= ipu_scale_buf_prepare,
+	.buf_queue	= ipu_scale_buf_queue,
+	.wait_prepare	= vb2_ops_wait_prepare,
+	.wait_finish	= vb2_ops_wait_finish,
+	.stop_streaming = ipu_scale_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct ipu_scale_ctx *ctx = priv;
+	int ret;
+
+	memset(src_vq, 0, sizeof(*src_vq));
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->ops = &ipu_scale_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &ctx->ipu_scaler->dev_mutex;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	memset(dst_vq, 0, sizeof(*dst_vq));
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->ops = &ipu_scale_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &ctx->ipu_scaler->dev_mutex;
+
+	return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int ipu_scale_open(struct file *file)
+{
+	struct ipu_scale_dev *ipu_scaler = video_drvdata(file);
+	struct ipu_scale_ctx *ctx = NULL;
+	const int width = 720;
+	const int height = 576;
+	int i;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	INIT_WORK(&ctx->skip_run, ipu_scale_skip_run);
+	INIT_WORK(&ctx->work, ipu_scaler_work);
+	init_completion(&ctx->completion);
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+	ctx->ipu_scaler = ipu_scaler;
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ipu_scaler->m2m_dev, ctx,
+					    &queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		int ret = PTR_ERR(ctx->fh.m2m_ctx);
+
+		kfree(ctx);
+		return ret;
+	}
+
+	for (i = 0; i < 2; i++) {
+		ctx->q_data[i].cur_fmt.width = width;
+		ctx->q_data[i].cur_fmt.height = height;
+		ctx->q_data[i].cur_fmt.bytesperline = width;
+		ctx->q_data[i].cur_fmt.pixelformat = V4L2_PIX_FMT_YUV420;
+		ctx->q_data[i].cur_fmt.sizeimage = width * height * 3 / 2;
+		ctx->q_data[i].cur_fmt.colorspace = V4L2_COLORSPACE_REC709;
+		ctx->q_data[i].rect.left = 0;
+		ctx->q_data[i].rect.top = 0;
+		ctx->q_data[i].rect.width = width;
+		ctx->q_data[i].rect.height = height;
+	}
+
+	ctx->ctrl = IPU_IMAGE_SCALE_PIXELPERFECT;
+	ctx->num_tiles = 0;
+
+	atomic_inc(&ipu_scaler->num_inst);
+
+	dev_dbg(ipu_scaler->dev, "Created instance %p, m2m_ctx: %p\n",
+			ctx, ctx->fh.m2m_ctx);
+
+	return 0;
+}
+
+static int ipu_scale_release(struct file *file)
+{
+	struct ipu_scale_dev *ipu_scaler = video_drvdata(file);
+	struct ipu_scale_ctx *ctx = fh_to_ctx(file->private_data);
+
+	dev_dbg(ipu_scaler->dev, "Releasing instance %p\n", ctx);
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx->icc);
+	kfree(ctx);
+
+	atomic_dec(&ipu_scaler->num_inst);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations ipu_scale_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ipu_scale_open,
+	.release	= ipu_scale_release,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+};
+
+static struct video_device ipu_scale_videodev = {
+	.name		= MEM2MEM_NAME,
+	.fops		= &ipu_scale_fops,
+	.ioctl_ops	= &ipu_scale_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.vfl_dir	= VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= device_run,
+	.job_ready	= job_ready,
+	.job_abort	= job_abort,
+};
+
+static u64 vout_dmamask = ~(u32)0;
+
+static int ipu_scale_probe(struct platform_device *pdev)
+{
+	struct ipu_scale_dev *ipu_scaler;
+	struct video_device *vfd;
+	struct ipu_soc *ipu = dev_get_drvdata(pdev->dev.parent);
+	int ret;
+
+	pdev->dev.dma_mask = &vout_dmamask;
+	pdev->dev.coherent_dma_mask = 0xffffffff;
+
+	ipu_scaler = devm_kzalloc(&pdev->dev, sizeof(*ipu_scaler), GFP_KERNEL);
+	if (!ipu_scaler)
+		return -ENOMEM;
+
+	ipu_scaler->ipu = ipu;
+	ipu_scaler->dev = &pdev->dev;
+
+	spin_lock_init(&ipu_scaler->irqlock);
+	mutex_init(&ipu_scaler->dev_mutex);
+
+	ret = v4l2_device_register(&pdev->dev, &ipu_scaler->v4l2_dev);
+	if (ret)
+		return ret;
+
+	atomic_set(&ipu_scaler->num_inst, 0);
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		dev_err(ipu_scaler->dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+
+	*vfd = ipu_scale_videodev;
+	vfd->lock = &ipu_scaler->dev_mutex;
+	vfd->v4l2_dev = &ipu_scaler->v4l2_dev;
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		dev_err(ipu_scaler->dev, "Failed to register video device\n");
+		goto rel_vdev;
+	}
+
+	video_set_drvdata(vfd, ipu_scaler);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", ipu_scale_videodev.name);
+	ipu_scaler->vfd = vfd;
+	dev_dbg(ipu_scaler->dev, "Device registered as /dev/video%d\n",
+		vfd->num);
+
+	platform_set_drvdata(pdev, ipu_scaler);
+
+	ipu_scaler->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(ipu_scaler->m2m_dev)) {
+		dev_err(ipu_scaler->dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(ipu_scaler->m2m_dev);
+		goto err_m2m;
+	}
+
+	return 0;
+
+	v4l2_m2m_release(ipu_scaler->m2m_dev);
+err_m2m:
+	video_unregister_device(ipu_scaler->vfd);
+rel_vdev:
+	video_device_release(vfd);
+unreg_dev:
+	v4l2_device_unregister(&ipu_scaler->v4l2_dev);
+
+	return ret;
+}
+
+static int ipu_scale_remove(struct platform_device *pdev)
+{
+	struct ipu_scale_dev *ipu_scaler =
+		(struct ipu_scale_dev *)platform_get_drvdata(pdev);
+
+	v4l2_m2m_release(ipu_scaler->m2m_dev);
+	video_unregister_device(ipu_scaler->vfd);
+	v4l2_device_unregister(&ipu_scaler->v4l2_dev);
+	kfree(ipu_scaler);
+
+	return 0;
+}
+
+static const struct platform_device_id ipu_scale_id[] = {
+	{ "imx-ipuv3-scaler" },
+	{}
+};
+MODULE_DEVICE_TABLE(platform, ipu_scale_id);
+
+static struct platform_driver ipu_scale_pdrv = {
+	.probe		= ipu_scale_probe,
+	.remove		= ipu_scale_remove,
+	.driver		= {
+		.name	= "imx-ipuv3-scaler",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static void __exit ipu_scale_exit(void)
+{
+	platform_driver_unregister(&ipu_scale_pdrv);
+}
+
+static int __init ipu_scale_init(void)
+{
+	return  platform_driver_register(&ipu_scale_pdrv);
+}
+
+module_init(ipu_scale_init);
+module_exit(ipu_scale_exit);
+
+MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/imx/imx-ipu.c b/drivers/media/platform/imx/imx-ipu.c
index c1b8637..402cc8b 100644
--- a/drivers/media/platform/imx/imx-ipu.c
+++ b/drivers/media/platform/imx/imx-ipu.c
@@ -102,7 +102,7 @@ struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat)
 }
 EXPORT_SYMBOL_GPL(ipu_find_fmt_rgb);
 
-static struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat)
+struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat)
 {
 	struct ipu_fmt *fmt;
 
diff --git a/drivers/media/platform/imx/imx-ipu.h b/drivers/media/platform/imx/imx-ipu.h
index 51c0982..288ee79 100644
--- a/drivers/media/platform/imx/imx-ipu.h
+++ b/drivers/media/platform/imx/imx-ipu.h
@@ -16,6 +16,7 @@ int ipu_enum_fmt_yuv(struct file *file, void *fh,
 		struct v4l2_fmtdesc *f);
 struct ipu_fmt *ipu_find_fmt_rgb(unsigned int pixelformat);
 struct ipu_fmt *ipu_find_fmt_yuv(unsigned int pixelformat);
+struct ipu_fmt *ipu_find_fmt(unsigned long pixelformat);
 int ipu_try_fmt(struct file *file, void *fh,
 		struct v4l2_format *f);
 int ipu_try_fmt_rgb(struct file *file, void *fh,
-- 
2.1.4

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

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

* Re: [PATCH v2 0/5] i.MX5/6 mem2mem scaler
  2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
@ 2015-04-10 13:03   ` Jean-Michel Hautbois
  2015-03-18 10:22   ` Philipp Zabel
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 23+ messages in thread
From: Jean-Michel Hautbois @ 2015-04-10 13:03 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Linux Media Mailing List, Mauro Carvalho Chehab, ML dri-devel,
	Hans Verkuil, Sascha Hauer, Steve Longerbeam

Hi Philipp,

Thank you for the series !

2015-03-18 11:22 GMT+01:00 Philipp Zabel <p.zabel@pengutronix.de>:
> Hi,
>
> this series uses the IPU IC post-processing task, to implement
> a mem2mem device for scaling and colorspace conversion. The first
> version had a fixup applied to the wrong patch.
>
> Changes since v1:
>  - Removed deinterlacer support left-overs
>
> regards
> Philipp
>
> Philipp Zabel (3):
>   gpu: ipu-v3: Add missing IDMAC channel names
>   gpu: ipu-v3: Add mem2mem image conversion support to IC
>   gpu: ipu-v3: Register scaler platform device
>
> Sascha Hauer (2):
>   [media] imx-ipu: Add ipu media common code
>   [media] imx-ipu: Add i.MX IPUv3 scaler driver
>
>  drivers/gpu/ipu-v3/ipu-common.c             |   2 +
>  drivers/gpu/ipu-v3/ipu-ic.c                 | 787 ++++++++++++++++++++++++-
>  drivers/media/platform/Kconfig              |   2 +
>  drivers/media/platform/Makefile             |   1 +
>  drivers/media/platform/imx/Kconfig          |  11 +
>  drivers/media/platform/imx/Makefile         |   2 +
>  drivers/media/platform/imx/imx-ipu-scaler.c | 869 ++++++++++++++++++++++++++++
>  drivers/media/platform/imx/imx-ipu.c        | 313 ++++++++++
>  drivers/media/platform/imx/imx-ipu.h        |  36 ++
>  include/video/imx-ipu-v3.h                  |  49 +-
>  10 files changed, 2055 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/media/platform/imx/Kconfig
>  create mode 100644 drivers/media/platform/imx/Makefile
>  create mode 100644 drivers/media/platform/imx/imx-ipu-scaler.c
>  create mode 100644 drivers/media/platform/imx/imx-ipu.c
>  create mode 100644 drivers/media/platform/imx/imx-ipu.h

It works here, even if v4l2-compliance seems a bit disappointed :
fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(425):
unknown pixelformat 00000000 for buftype 1
        test VIDIOC_G_FMT: FAIL
        test VIDIOC_TRY_FMT: OK (Not Supported)
        test VIDIOC_S_FMT: OK (Not Supported)
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1200):
doioctl(node, VIDIOC_G_SELECTION, &sel_crop)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1270):
testBasicCrop(node, V4L2_BUF_TYPE_VIDEO_CAPTURE)
        test Cropping: FAIL
        test Composing: OK (Not Supported)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1381):
doioctl(node, VIDIOC_S_FMT, &fmt)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1478):
doioctl(node, VIDIOC_S_FMT, &fmt)
        test Scaling: FAIL

    Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

    Buffer ioctls:
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-buffers.cpp(548):
q.has_expbuf(node)
        test VIDIOC_EXPBUF: FAIL

Maybe should it be corrected before acceptation ?
Nothing to worry, though...

JM

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

* Re: [PATCH v2 0/5] i.MX5/6 mem2mem scaler
@ 2015-04-10 13:03   ` Jean-Michel Hautbois
  0 siblings, 0 replies; 23+ messages in thread
From: Jean-Michel Hautbois @ 2015-04-10 13:03 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Mauro Carvalho Chehab, ML dri-devel, Hans Verkuil, Sascha Hauer,
	Steve Longerbeam, Linux Media Mailing List

Hi Philipp,

Thank you for the series !

2015-03-18 11:22 GMT+01:00 Philipp Zabel <p.zabel@pengutronix.de>:
> Hi,
>
> this series uses the IPU IC post-processing task, to implement
> a mem2mem device for scaling and colorspace conversion. The first
> version had a fixup applied to the wrong patch.
>
> Changes since v1:
>  - Removed deinterlacer support left-overs
>
> regards
> Philipp
>
> Philipp Zabel (3):
>   gpu: ipu-v3: Add missing IDMAC channel names
>   gpu: ipu-v3: Add mem2mem image conversion support to IC
>   gpu: ipu-v3: Register scaler platform device
>
> Sascha Hauer (2):
>   [media] imx-ipu: Add ipu media common code
>   [media] imx-ipu: Add i.MX IPUv3 scaler driver
>
>  drivers/gpu/ipu-v3/ipu-common.c             |   2 +
>  drivers/gpu/ipu-v3/ipu-ic.c                 | 787 ++++++++++++++++++++++++-
>  drivers/media/platform/Kconfig              |   2 +
>  drivers/media/platform/Makefile             |   1 +
>  drivers/media/platform/imx/Kconfig          |  11 +
>  drivers/media/platform/imx/Makefile         |   2 +
>  drivers/media/platform/imx/imx-ipu-scaler.c | 869 ++++++++++++++++++++++++++++
>  drivers/media/platform/imx/imx-ipu.c        | 313 ++++++++++
>  drivers/media/platform/imx/imx-ipu.h        |  36 ++
>  include/video/imx-ipu-v3.h                  |  49 +-
>  10 files changed, 2055 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/media/platform/imx/Kconfig
>  create mode 100644 drivers/media/platform/imx/Makefile
>  create mode 100644 drivers/media/platform/imx/imx-ipu-scaler.c
>  create mode 100644 drivers/media/platform/imx/imx-ipu.c
>  create mode 100644 drivers/media/platform/imx/imx-ipu.h

It works here, even if v4l2-compliance seems a bit disappointed :
fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(425):
unknown pixelformat 00000000 for buftype 1
        test VIDIOC_G_FMT: FAIL
        test VIDIOC_TRY_FMT: OK (Not Supported)
        test VIDIOC_S_FMT: OK (Not Supported)
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1200):
doioctl(node, VIDIOC_G_SELECTION, &sel_crop)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1270):
testBasicCrop(node, V4L2_BUF_TYPE_VIDEO_CAPTURE)
        test Cropping: FAIL
        test Composing: OK (Not Supported)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1381):
doioctl(node, VIDIOC_S_FMT, &fmt)
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-formats.cpp(1478):
doioctl(node, VIDIOC_S_FMT, &fmt)
        test Scaling: FAIL

    Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

    Buffer ioctls:
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
        fail: /run/media/jm/SSD_JM/Projets/vodabox3/poky/build/tmp/work/cortexa9hf-vfp-neon-poky-linux-gnueabi/v4l-utils/git-r0/git/utils/v4l2-compliance/v4l2-test-buffers.cpp(548):
q.has_expbuf(node)
        test VIDIOC_EXPBUF: FAIL

Maybe should it be corrected before acceptation ?
Nothing to worry, though...

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

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-03-18 10:22   ` Philipp Zabel
  (?)
@ 2015-05-27 18:42   ` Jean-Michel Hautbois
  2015-05-28  9:00     ` Enrico Weigelt, metux IT consult
  2015-05-28 10:35     ` Philipp Zabel
  -1 siblings, 2 replies; 23+ messages in thread
From: Jean-Michel Hautbois @ 2015-05-27 18:42 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Linux Media Mailing List, ML dri-devel, David Airlie,
	Mauro Carvalho Chehab, Steve Longerbeam, Hans Verkuil,
	Kamil Debski, Ian Molton, Jean-Michel Hautbois, Sascha Hauer,
	Sascha Hauer, Lucas Stach

Hi Philipp, Lucas and Sascha,

Thanks for that patch series.

2015-03-18 11:22 GMT+01:00 Philipp Zabel <p.zabel@pengutronix.de>:
>
> This patch adds support for mem2mem scaling and colorspace conversion
> using the IC module's post-processing task.
>
> Scaling images larger than 1024x1024 is supported by tiling over multiple
> IC scaling runs. Since the IDMAC and IC units have interesting and different
> alignment limitations for buffer base addresses (left edges) and burst size
> (row lengths), depending on input and output pixel formats, the tile rectangles
> and scaling coefficients are chosen to minimize distortion. Due to possible
> overlap, the tiles have to be rendered right to left and bottom to top.
> Up to 7 pixels (depending on frame sizes and scaling factor) have to be
> available after the end of the frame if the width is not burst size aligned.
> The tiling code has a parameter to optionally round frame sizes up or down
> and avoid overdraw in compositing scenarios.

Can you detail what you call "compositing scenarios" ?

>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> ---
> Changes since v1:
>  - Removed deinterlacer support left-overs
> ---
>  drivers/gpu/ipu-v3/ipu-ic.c | 787 +++++++++++++++++++++++++++++++++++++++++++-
>  include/video/imx-ipu-v3.h  |  34 +-
>  2 files changed, 804 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index ad75588..984f68f 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -15,6 +15,7 @@
>  #include <linux/errno.h>
>  #include <linux/spinlock.h>
>  #include <linux/bitrev.h>
> +#include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/err.h>
>  #include "ipu-prv.h"
> @@ -96,6 +97,15 @@ struct ic_task_bitfields {
>         u32 ic_cmb_galpha_bit;
>  };
>
> +struct ic_task_channels {
> +       u8 in;
> +       u8 out;
> +       u8 rot_in;
> +       u8 rot_out;
> +       u8 in_prev;
> +       u8 in_next;
> +};
> +
>  static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
>         [IC_TASK_ENCODER] = {
>                 .rsc = IC_PRP_ENC_RSC,
> @@ -138,12 +148,53 @@ 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] = {
> +               .in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
> +               .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_VDI_CUR,
> +               .out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
> +               .rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
> +               .rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
> +               .in_prev = IPUV3_CHANNEL_MEM_VDI_PREV,
> +               .in_next = 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 image_convert_ctx {
> +       void (*complete)(void *ctx, int err);
> +       void *complete_context;
> +
> +       struct list_head list;
> +       struct ipu_image in;
> +       struct ipu_image in_n;
> +       struct ipu_image in_p;
> +       struct ipu_image out;
> +
> +       void *freep;
> +
> +       bool rotate:1;
> +
> +       u32 rsc;
> +};
> +
>  struct ipu_ic_priv;
>
>  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;
> @@ -152,6 +203,19 @@ struct ipu_ic {
>         bool in_use;
>
>         struct ipu_ic_priv *priv;
> +
> +       struct ipuv3_channel *input_channel_p;
> +       struct ipuv3_channel *input_channel;
> +       struct ipuv3_channel *input_channel_n;
> +       struct ipuv3_channel *output_channel;
> +       struct ipuv3_channel *rotation_input_channel;
> +       struct ipuv3_channel *rotation_output_channel;
> +
> +       struct list_head image_list;
> +
> +       struct workqueue_struct *workqueue;
> +       struct work_struct work;
> +       struct completion complete;
>  };

As this is a workqueue, it can sleep, and you don't know when it is
called exactly.
Can we be sure that it is "real-time" compatible ? If you have this
scaler after a capture source, and before the coda driver, you can be
starved of buffers ?
And you can even have multiple instances of the scaler, so you
probably can get into troubles if there is not enough buffers on the
capture and output queues, right ?
I have played with it a bit and have been successful having two
instances on IPU1 and two other on IPU2.
But I don't know if there can be side effects...

JM

>
>  struct ipu_ic_priv {
> @@ -168,7 +232,8 @@ static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset)
>         return readl(ic->priv->base + offset);
>  }
>
> -static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset)
> +static inline void ipu_ic_write(struct ipu_ic *ic, u32 value,
> +                               unsigned offset)
>  {
>         writel(value, ic->priv->base + offset);
>  }
> @@ -446,32 +511,35 @@ int ipu_ic_task_init(struct ipu_ic *ic,
>                      int in_width, int in_height,
>                      int out_width, int out_height,
>                      enum ipu_color_space in_cs,
> -                    enum ipu_color_space out_cs)
> +                    enum ipu_color_space out_cs,
> +                    u32 rsc)
>  {
>         struct ipu_ic_priv *priv = ic->priv;
> -       u32 reg, downsize_coeff, resize_coeff;
> +       u32 downsize_coeff, resize_coeff;
>         unsigned long flags;
>         int ret = 0;
>
> -       /* Setup vertical resizing */
> -       ret = calc_resize_coeffs(ic, in_height, out_height,
> -                                &resize_coeff, &downsize_coeff);
> -       if (ret)
> -               return ret;
> +       if (!rsc) {
> +               /* Setup vertical resizing */
> +               ret = calc_resize_coeffs(ic, in_height, out_height,
> +                                        &resize_coeff, &downsize_coeff);
> +               if (ret)
> +                       return ret;
>
> -       reg = (downsize_coeff << 30) | (resize_coeff << 16);
> +               rsc = (downsize_coeff << 30) | (resize_coeff << 16);
>
> -       /* Setup horizontal resizing */
> -       ret = calc_resize_coeffs(ic, in_width, out_width,
> -                                &resize_coeff, &downsize_coeff);
> -       if (ret)
> -               return ret;
> +               /* Setup horizontal resizing */
> +               ret = calc_resize_coeffs(ic, in_width, out_width,
> +                                        &resize_coeff, &downsize_coeff);
> +               if (ret)
> +                       return ret;
>
> -       reg |= (downsize_coeff << 14) | resize_coeff;
> +               rsc |= (downsize_coeff << 14) | resize_coeff;
> +       }
>
>         spin_lock_irqsave(&priv->lock, flags);
>
> -       ipu_ic_write(ic, reg, ic->reg->rsc);
> +       ipu_ic_write(ic, rsc, ic->reg->rsc);
>
>         /* Setup color space conversion */
>         ic->in_cs = in_cs;
> @@ -629,6 +697,675 @@ unlock:
>  }
>  EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
>
> +static struct image_convert_ctx *ipu_image_convert_next(struct ipu_ic *ic)
> +{
> +       struct ipu_ic_priv *priv = ic->priv;
> +       struct ipuv3_channel *ch_in = ic->input_channel;
> +       struct ipuv3_channel *ch_out = ic->output_channel;
> +       struct image_convert_ctx *ctx;
> +       struct ipu_image *in_p, *in, *in_n;
> +       struct ipu_image *out;
> +       int ret;
> +       unsigned long flags;
> +       unsigned int inburst, outburst;
> +       unsigned int in_height;
> +
> +       spin_lock_irqsave(&priv->lock, flags);
> +
> +       if (list_empty(&ic->image_list)) {
> +               spin_unlock_irqrestore(&priv->lock, flags);
> +               return NULL;
> +       }
> +
> +       ctx = list_first_entry(&ic->image_list, struct image_convert_ctx, list);
> +
> +       list_del(&ctx->list);
> +
> +       spin_unlock_irqrestore(&priv->lock, flags);
> +
> +       in_p = &ctx->in_p;
> +       in = &ctx->in;
> +       in_n = &ctx->in_n;
> +       out = &ctx->out;
> +
> +       ipu_cpmem_zero(ch_in);
> +       ipu_cpmem_zero(ch_out);
> +
> +       inburst = in->rect.width & 0xf ? 8 : 16;
> +       outburst = out->rect.width & 0xf ? 8 : 16;
> +
> +       ipu_ic_enable(ic);
> +
> +       ipu_ic_task_idma_init(ic, ic->input_channel, in->rect.width,
> +                             in->rect.height, inburst, IPU_ROTATE_NONE);
> +       ipu_ic_task_idma_init(ic, ic->output_channel, out->rect.width,
> +                             out->rect.height, outburst, IPU_ROTATE_NONE);
> +
> +       ipu_cpmem_set_image(ch_in, &ctx->in);
> +       ipu_cpmem_set_image(ch_out, &ctx->out);
> +
> +       ipu_cpmem_set_burstsize(ch_in, inburst);
> +       ipu_cpmem_set_burstsize(ch_out, outburst);
> +
> +       in_height = in->rect.height;
> +
> +       dev_dbg(priv->ipu->dev, "%s: %dx%d(%dx%d@%d,%d) -> %dx%d(%dx%d@%d,%d)\n",
> +               __func__, in->pix.width, in->pix.height,
> +               in->rect.width, in->rect.height, in->rect.left, in->rect.top,
> +               out->pix.width, out->pix.height,
> +               out->rect.width, out->rect.height,
> +               out->rect.left, out->rect.top);
> +
> +       dev_dbg(priv->ipu->dev,
> +               "%s: hscale: >>%d, *8192/%d vscale: >>%d, *8192/%d\n",
> +               __func__, (ctx->rsc >> 14) & 0x3, (ctx->rsc & 0x3fff),
> +               ctx->rsc >> 30, (ctx->rsc >> 16) & 0x3fff);
> +
> +       ret = ipu_ic_task_init(ic, in->rect.width, in_height,
> +                       out->rect.width, out->rect.height,
> +                       ipu_pixelformat_to_colorspace(in->pix.pixelformat),
> +                       ipu_pixelformat_to_colorspace(out->pix.pixelformat),
> +                       ctx->rsc);
> +       if (ret) {
> +               ipu_ic_disable(ic);
> +               return ERR_PTR(ret);
> +       }
> +
> +       ipu_idmac_enable_channel(ic->input_channel);
> +       ipu_idmac_enable_channel(ic->output_channel);
> +
> +       ipu_ic_task_enable(ic);
> +
> +       ipu_idmac_select_buffer(ic->input_channel, 0);
> +       ipu_idmac_select_buffer(ic->output_channel, 0);
> +
> +       return ctx;
> +}
> +
> +static void ipu_image_convert_work(struct work_struct *work)
> +{
> +       struct ipu_ic *ic = container_of(work, struct ipu_ic, work);
> +       struct image_convert_ctx *ctx;
> +       int ret;
> +
> +       while (1) {
> +               int task_error = 0;
> +
> +               ctx = ipu_image_convert_next(ic);
> +               if (!ctx)
> +                       return;
> +
> +               if (IS_ERR(ctx)) {
> +                       task_error = PTR_ERR(ctx);
> +               } else {
> +                       ret = wait_for_completion_interruptible_timeout(
> +                                               &ic->complete, 100 * HZ);
> +                       if (!ret)
> +                               task_error = -ETIMEDOUT;
> +               }
> +
> +               ipu_ic_task_disable(ic);
> +               ipu_ic_disable(ic);
> +
> +               if (ctx->complete)
> +                       ctx->complete(ctx->complete_context, task_error);
> +               kfree(ctx->freep);
> +       }
> +}
> +
> +static irqreturn_t ipu_image_convert_handler(int irq, void *context)
> +{
> +       struct ipu_ic *ic = context;
> +
> +       complete(&ic->complete);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +
> +/*
> + * IDMAC base addresses are 8-byte aligned
> + */
> +static int ipu_image_halign(u32 pixfmt)
> +{
> +       switch (pixfmt) {
> +       /* 2 RGB32 pixels correspond to 8 bytes */
> +       case V4L2_PIX_FMT_RGB32:
> +       case V4L2_PIX_FMT_BGR32:
> +               return 2;
> +       /* 4 RGB565 or YUYV pixels correspond to 8 bytes */
> +       case V4L2_PIX_FMT_RGB565:
> +       case V4L2_PIX_FMT_UYVY:
> +       case V4L2_PIX_FMT_YUYV:
> +               return 4;
> +       /*
> +        * 8 RGB24 pixels correspond to 24 bytes,
> +        * 8 NV12 pixels correspond to 8 bytes, both in luma and chroma
> +        */
> +       case V4L2_PIX_FMT_RGB24:
> +       case V4L2_PIX_FMT_BGR24:
> +       case V4L2_PIX_FMT_NV12:
> +               return 8;
> +       /* 16 YUV420 pixels correspond to 16 bytes in luma, 8 bytes in chroma */
> +       case V4L2_PIX_FMT_YUV420:
> +       case V4L2_PIX_FMT_YVU420:
> +       case V4L2_PIX_FMT_YUV422P:
> +               return 16;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +/*
> + * Vertically chroma-subsampled formats are limited to even heights and vertical
> + * positions
> + */
> +static int ipu_image_valign(u32 pixfmt)
> +{
> +       switch (pixfmt) {
> +       case V4L2_PIX_FMT_RGB24:
> +       case V4L2_PIX_FMT_BGR24:
> +       case V4L2_PIX_FMT_RGB32:
> +       case V4L2_PIX_FMT_BGR32:
> +       case V4L2_PIX_FMT_RGB565:
> +       case V4L2_PIX_FMT_UYVY:
> +       case V4L2_PIX_FMT_YUYV:
> +       case V4L2_PIX_FMT_YUV422P:
> +               return 1;
> +       case V4L2_PIX_FMT_NV12:
> +       case V4L2_PIX_FMT_YUV420:
> +       case V4L2_PIX_FMT_YVU420:
> +               return 2;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +#define round_closest(x, y) round_down((x) + (y)/2, (y))
> +
> +struct image_convert_ctx *ipu_image_convert_prepare(struct ipu_soc *ipu,
> +               struct ipu_image *in, struct ipu_image *out,
> +               enum ipu_image_scale_ctrl ctrl, int *num_tiles)
> +{
> +       struct image_convert_ctx *ctx, *c;
> +       int htiles, vtiles;
> +       int in_valign, in_halign, in_burst, out_valign, out_halign, out_burst;
> +       int left, top;
> +       int x, y;
> +       int h_resize_opt, v_resize_opt;
> +       u32 v_downsize_coeff = 0, h_downsize_coeff = 0;
> +       u32 v_resize_coeff, h_resize_coeff;
> +
> +       /* validate input */
> +       if (in->rect.width < 16 || out->rect.width < 16 ||
> +           (in->rect.width / 8) > out->rect.width)
> +               return ERR_PTR(-EINVAL);
> +
> +       /* tile setup */
> +       htiles = DIV_ROUND_UP(out->rect.width, 1024);
> +       vtiles = DIV_ROUND_UP(out->rect.height, 1024);
> +
> +       in_valign = ipu_image_valign(in->pix.pixelformat);
> +       in_halign = ipu_image_halign(in->pix.pixelformat);
> +       out_valign = ipu_image_valign(out->pix.pixelformat);
> +       out_halign = ipu_image_halign(out->pix.pixelformat);
> +
> +       /* IC bursts are limited to either 8 or 16 pixels */
> +       in_burst = 8;
> +       out_burst = 8;
> +
> +       if (in_valign < 0 || in_halign < 0 ||
> +           out_valign < 0 || out_halign < 0) {
> +               dev_err(ipu->dev, "unsupported in/out format\n");
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       /* compute static decimator coefficients */
> +       while ((in->rect.width >> h_downsize_coeff) > out->rect.width)
> +               h_downsize_coeff++;
> +       while ((in->rect.height >> v_downsize_coeff) > out->rect.height)
> +               v_downsize_coeff++;
> +
> +       /* move and crop the output image according to IDMAC limitations */
> +       switch (ctrl) {
> +       case IPU_IMAGE_SCALE_ROUND_DOWN:
> +               left = round_up(in->rect.left, in_halign);
> +               top = round_up(in->rect.top, in_valign);
> +               in->rect.width = in->rect.width - (left - in->rect.left);
> +               in->rect.height = in->rect.height - (top - in->rect.top);
> +               in->rect.left = left;
> +               in->rect.top = top;
> +               left = round_up(out->rect.left, out_halign);
> +               top = round_up(out->rect.top, out_valign);
> +               out->rect.width = round_down(out->rect.width - (left -
> +                                            out->rect.left), out_burst);
> +               out->rect.height = round_down(out->rect.height - (top -
> +                                             out->rect.top), out_valign);
> +               break;
> +       case IPU_IMAGE_SCALE_ROUND_UP:
> +               left = round_down(in->rect.left, in_halign);
> +               top = round_down(in->rect.top, in_valign);
> +               in->rect.width = in->rect.width + in->rect.left - left;
> +               in->rect.height = in->rect.height + in->rect.top - top;
> +               in->rect.left = left;
> +               in->rect.top = top;
> +               left = round_down(out->rect.left, out_halign);
> +               top = round_down(out->rect.top, out_valign);
> +               out->rect.width = round_up(out->rect.width + out->rect.left -
> +                                          left, out_burst);
> +               out->rect.height = round_up(out->rect.height + out->rect.top -
> +                                           top, out_valign);
> +               break;
> +       case IPU_IMAGE_SCALE_PIXELPERFECT:
> +               left = round_down(in->rect.left, in_halign);
> +               top = round_down(in->rect.top, in_valign);
> +               in->rect.width = in->rect.width + in->rect.left - left;
> +               in->rect.height = in->rect.height + in->rect.top - top;
> +               in->rect.left = left;
> +               in->rect.top = top;
> +               left = round_down(out->rect.left + out_halign / 2, out_halign);
> +               top = round_down(out->rect.top + out_valign / 2, out_valign);
> +               /*
> +                * don't round width and height to burst size / pixel format
> +                * limitations yet, we do it after determining the scaling
> +                * coefficients
> +                */
> +               out->rect.width = out->rect.width + out->rect.left - left;
> +               out->rect.height = out->rect.height + out->rect.top - top;
> +               break;
> +       default:
> +               return ERR_PTR(-EINVAL);
> +       }
> +       out->rect.left = left;
> +       out->rect.top = top;
> +
> +       /* Round input width and height according to decimation */
> +       in->rect.width = round_down(in->rect.width, 1 << h_downsize_coeff);
> +       in->rect.height = round_down(in->rect.height, 1 << v_downsize_coeff);
> +
> +       dev_dbg(ipu->dev,
> +               "%s: in: %dx%d(%dx%d@%d,%d) -> out: %dx%d(%dx%d@%d,%d)\n",
> +               __func__, in->pix.width, in->pix.height, in->rect.width,
> +               in->rect.height, in->rect.left, in->rect.top, out->pix.width,
> +               out->pix.height, out->rect.width, out->rect.height,
> +               out->rect.left, out->rect.top);
> +
> +       /*
> +        * Compute the bilinear resizing coefficients that can/could be used if
> +        * scaling using a single tile. The bottom right pixel should sample the
> +        * input as close as possible to but not beyond the bottom right input
> +        * pixel out of the decimator:
> +        *
> +        * (out->rect.width - 1) * h_resize / 8192.0 <= (in->rect.width >>
> +        *                                               h_downsize_coeff) - 1
> +        * (out->rect.height - 1) * v_resize / 8192.0 <= (in->rect.height >>
> +        *                                                v_downsize_coeff) - 1
> +        */
> +       h_resize_opt = 8192 * ((in->rect.width >> h_downsize_coeff) - 1) /
> +                      (out->rect.width - 1);
> +       v_resize_opt = 8192 * ((in->rect.height >> v_downsize_coeff) - 1) /
> +                      (out->rect.height - 1);
> +
> +       dev_dbg(ipu->dev,
> +               "%s: hscale: >>%d, *8192/%d vscale: >>%d, *8192/%d, %dx%d tiles\n",
> +               __func__, h_downsize_coeff, h_resize_opt, v_downsize_coeff,
> +               v_resize_opt, htiles, vtiles);
> +
> +       ctx = kcalloc(htiles * vtiles, sizeof(*ctx), GFP_KERNEL);
> +       if (!ctx)
> +               return ERR_PTR(-ENOMEM);
> +
> +       c = ctx;
> +
> +       for (x = htiles - 1; x >= 0; x--) {
> +               int in_right, out_right;
> +
> +               /*
> +                * Since we render tiles right to left, the right edge
> +                * is already known. Depending on tile position and
> +                * scaling mode, we may overshoot it.
> +                */
> +               if (x == htiles - 1) {
> +                       out_right = out->rect.left + out->rect.width;
> +                       in_right = in->rect.left + in->rect.width;
> +               } else {
> +                       struct image_convert_ctx *c_right = c - vtiles;
> +
> +                       out_right = c_right->out.rect.left;
> +                       in_right = c_right->in.rect.left;
> +               }
> +
> +               /* Now determine the left edge of this tile column */
> +               if (x == 0) {
> +                       /* For the leftmost column this is trivial */
> +                       c->out.rect.left = out->rect.left;
> +                       c->in.rect.left = in->rect.left;
> +               } else {
> +                       int best_left, best_in_left;
> +                       int min_left, max_left;
> +                       int min_diff = INT_MAX;
> +
> +                       /*
> +                        * Find the best possible left edge. It must be adjusted
> +                        * according to IDMAC limitations, and should be
> +                        * chosen so that
> +                        * (in->rect.left + (c->out.rect.left - out->rect.left)
> +                        *  * h_resize_opt / (8192 >> h_downsize_coeff))
> +                        * is as close as possible to a valid left edge in the
> +                        * input.
> +                        */
> +                       min_left = max(0,
> +                                      round_up(out_right - 1024, out_halign));
> +                       max_left = min(round_down(out_right, out_halign),
> +                                      x * 1024);
> +                       best_left = min_left;
> +                       best_in_left = (best_left - out->rect.left) *
> +                                      h_resize_opt;
> +                       for (left = min_left; left < max_left;
> +                            left += out_halign) {
> +                               int diff, in_left;
> +
> +                               /*
> +                                * In ROUND_UP and ROUND_DOWN modes, for the
> +                                * rightmost column, only consider left edges
> +                                * that are a multiple of the burst size away
> +                                * from the right edge.
> +                                */
> +                               if ((ctrl != IPU_IMAGE_SCALE_PIXELPERFECT) &&
> +                                   (x == htiles - 1) &&
> +                                   ((out_right - left) % out_burst))
> +                                       continue;
> +                               in_left = in->rect.left +
> +                                         (((left - out->rect.left) *
> +                                           h_resize_opt) << h_downsize_coeff);
> +                               diff = abs(in_left -
> +                                          round_closest(in_left,
> +                                                        8192 * in_halign));
> +
> +                               if (diff < min_diff) {
> +                                       min_diff = diff;
> +                                       best_left = left;
> +                                       best_in_left = in_left;
> +                               }
> +                       }
> +
> +                       c->out.rect.left = best_left;
> +                       c->in.rect.left = DIV_ROUND_CLOSEST(best_in_left, 8192);
> +
> +                       dev_dbg(ipu->dev,
> +                               "%s: tile(%d,y):\tleft: %d -> %d (instead of %d.%04d -> %d)",
> +                               __func__, x, c->in.rect.left,
> +                               c->out.rect.left, best_in_left / 8192,
> +                               (best_in_left % 8192) * 10000 / 8192,
> +                               out->rect.left +
> +                               DIV_ROUND_CLOSEST((c->in.rect.left -
> +                                                  in->rect.left) *
> +                                                 (8192 >> h_downsize_coeff),
> +                                                 h_resize_opt));
> +               }
> +
> +               /* Determine tile width from left and right edges */
> +               c->out.rect.width = out_right - c->out.rect.left;
> +               c->in.rect.width = in_right - c->in.rect.left;
> +
> +               /* Now we can determine the actual per-tile scaling factor */
> +               if (x == htiles - 1) {
> +                       /*
> +                        * Round down for the right column, since we
> +                        * don't want to read beyond the right edge.
> +                        */
> +                       h_resize_coeff = 8192 * ((c->in.rect.width >>
> +                                                h_downsize_coeff) - 1) /
> +                                        (c->out.rect.width - 1);
> +               } else {
> +                       /*
> +                        * Round to closest for seams between tiles for
> +                        * minimal distortion.
> +                        */
> +                       h_resize_coeff = DIV_ROUND_CLOSEST(8192 *
> +                                                          (c->in.rect.width >>
> +                                                           h_downsize_coeff),
> +                                                          c->out.rect.width);
> +               }
> +
> +               /*
> +                * With the scaling factor known, round up output width
> +                * to burst size. In ROUND_UP and ROUND_DOWN scaling mode
> +                * this is a no-op for the right column.
> +                */
> +               c->out.rect.width = round_up(c->out.rect.width, out_burst);
> +
> +               /*
> +                * Calculate input width from the last accessed input pixel
> +                * given output width and scaling coefficients. Round to
> +                * burst size.
> +                */
> +               c->in.rect.width = (DIV_ROUND_UP((c->out.rect.width - 1) *
> +                                                h_resize_coeff, 8192) + 1)
> +                                  << h_downsize_coeff;
> +               c->in.rect.width = round_up(c->in.rect.width, in_burst);
> +
> +               for (y = vtiles - 1; y >= 0; y--) {
> +                       int in_bottom, out_bottom;
> +
> +                       memcpy(&c->in.pix, &in->pix,
> +                             sizeof(struct v4l2_pix_format));
> +
> +                       if (y == vtiles - 1) {
> +                               out_bottom = out->rect.top + out->rect.height;
> +                               in_bottom = in->rect.top + in->rect.height;
> +                       } else {
> +                               struct image_convert_ctx *c_below = c - 1;
> +
> +                               out_bottom = c_below->out.rect.top;
> +                               in_bottom = c_below->in.rect.top;
> +
> +                               /*
> +                                * Copy horizontal parameters from the tile
> +                                * below
> +                                */
> +                               c->out.rect.left = c_below->out.rect.left;
> +                               c->out.rect.width = c_below->out.rect.width;
> +                               c->in.rect.left = c_below->in.rect.left;
> +                               c->in.rect.width = c_below->in.rect.width;
> +                       }
> +
> +                       if (y == 0) {
> +                               c->out.rect.top = out->rect.top;
> +                               c->in.rect.top = in->rect.top;
> +                       } else {
> +                               int best_top, best_in_top;
> +                               int min_top, max_top;
> +                               int min_diff = INT_MAX;
> +
> +                               /*
> +                                * Find the best possible top edge. It must be
> +                                * adjusted according to IDMAC limitations, and
> +                                * should be chosen so that
> +                                * (in->rect.top + (c->out.rect.top -
> +                                *  out->rect.top) * v_resize_opt /
> +                                * (8192 >> v_downsize_coeff))
> +                                * is as close as possible to a valid top edge
> +                                * in the input.
> +                                */
> +                               min_top = max(0,
> +                                             round_up(out_bottom - 1024,
> +                                                      out_valign));
> +                               max_top = min(round_down(out_bottom,
> +                                                        out_halign), y * 1024);
> +                               best_top = min_top;
> +                               best_in_top = (best_top - out->rect.top) *
> +                                              v_resize_opt;
> +                               for (top = min_top; top < max_top;
> +                                    top += out_valign) {
> +                                       int diff, in_top;
> +
> +                                       in_top = in->rect.top +
> +                                                (((top - out->rect.top) *
> +                                                  v_resize_opt) <<
> +                                                 v_downsize_coeff);
> +                                       diff = abs(in_top -
> +                                                  round_closest(in_top, 8192 *
> +                                                                in_valign));
> +
> +                                       if (diff < min_diff) {
> +                                               min_diff = diff;
> +                                               best_top = top;
> +                                               best_in_top = in_top;
> +                                       }
> +                               }
> +
> +                               c->out.rect.top = best_top;
> +                               c->in.rect.top = DIV_ROUND_CLOSEST(best_in_top,
> +                                                                  8192);
> +
> +                               dev_dbg(ipu->dev,
> +                                       "%s: tile(%d,%d):\ttop: %d -> %d (instead of %d.%04d -> %d)",
> +                                       __func__, x, y, c->in.rect.top,
> +                                       c->out.rect.top, best_in_top / 8192,
> +                                       (best_in_top % 8192) * 10000 / 8192,
> +                                       out->rect.top +
> +                                       DIV_ROUND_CLOSEST((c->in.rect.top -
> +                                                          in->rect.top) * (8192
> +                                                         >> v_downsize_coeff),
> +                                                         v_resize_opt));
> +                       }
> +
> +                       /* Determine tile height from top and bottom edges */
> +                       c->out.rect.height = out_bottom - c->out.rect.top;
> +                       c->in.rect.height = in_bottom - c->in.rect.top;
> +
> +                       /*
> +                        * Now we can determine the actual vertical per-tile
> +                        * scaling factor
> +                        */
> +                       if (y == vtiles - 1) {
> +                               /*
> +                                * Round down for the bottom row, since we
> +                                * don't want to read beyond the lower border.
> +                                */
> +                               v_resize_coeff = 8192 * ((c->in.rect.height >>
> +                                                        v_downsize_coeff) - 1)
> +                                                / (c->out.rect.height - 1);
> +                       } else {
> +                               /*
> +                                * Round to closest for seams between tiles for
> +                                * minimal distortion.
> +                                */
> +                               v_resize_coeff = DIV_ROUND_CLOSEST(8192 *
> +                                                       (c->in.rect.height >>
> +                                                        v_downsize_coeff),
> +                                                       c->out.rect.height);
> +                       }
> +
> +                       /*
> +                        * With the scaling factor known, round up output height
> +                        * to IDMAC limitations
> +                        */
> +                       c->out.rect.height = round_up(c->out.rect.height,
> +                                                     out_valign);
> +
> +                       /*
> +                        * Calculate input height from the last accessed input
> +                        * line given output height and scaling coefficients.
> +                        */
> +                       c->in.rect.height = (DIV_ROUND_UP(
> +                                               (c->out.rect.height - 1) *
> +                                               v_resize_coeff, 8192) + 1)
> +                                           << v_downsize_coeff;
> +
> +                       /* align height according to IDMAC restrictions */
> +                       c->in.rect.height = round_up(c->in.rect.height,
> +                               in_valign);
> +
> +                       memcpy(&c->out.pix, &out->pix,
> +                              sizeof(struct v4l2_pix_format));
> +
> +                       dev_dbg(ipu->dev,
> +                               "%s: tile(%d,%d): %dx%d(%dx%d@%d,%d) -> %dx%d(%dx%d@%d,%d), resize: %dx%d\n",
> +                               __func__, x, y,
> +                               c->in.pix.width, c->in.pix.height,
> +                               c->in.rect.width, c->in.rect.height,
> +                               c->in.rect.left, c->in.rect.top,
> +                               c->out.pix.width, c->out.pix.height,
> +                               c->out.rect.width, c->out.rect.height,
> +                               c->out.rect.left, c->out.rect.top,
> +                               h_resize_coeff, v_resize_coeff);
> +
> +                       c->rsc = (v_downsize_coeff << 30) |
> +                                (v_resize_coeff << 16) |
> +                                (h_downsize_coeff << 14) |
> +                                h_resize_coeff;
> +
> +                       c++;
> +               }
> +       }
> +
> +       *num_tiles = htiles * vtiles;
> +
> +       return ctx;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
> +
> +int ipu_image_convert_run(struct ipu_soc *ipu, struct ipu_image *in,
> +                         struct ipu_image *out, struct image_convert_ctx *ctx,
> +                         int num_tiles, void (*complete)(void *ctx, int err),
> +                         void *complete_context, bool free_ctx)
> +{
> +       struct ipu_ic_priv *priv = ipu->ic_priv;
> +       struct ipu_ic *ic = &priv->task[IC_TASK_POST_PROCESSOR];
> +       unsigned long flags;
> +       int i;
> +
> +       for (i = 0; i < num_tiles; i++) {
> +               ctx[i].in.phys0 = in->phys0;
> +               ctx[i].out.phys0 = out->phys0;
> +       }
> +       ctx[num_tiles - 1].complete = complete;
> +       ctx[num_tiles - 1].complete_context = complete_context;
> +       if (free_ctx)
> +               ctx[num_tiles - 1].freep = ctx;
> +
> +       spin_lock_irqsave(&priv->lock, flags);
> +
> +       for (i = 0; i < num_tiles; i++)
> +               list_add_tail(&ctx[i].list, &ic->image_list);
> +
> +       queue_work(ic->workqueue, &ic->work);
> +
> +       spin_unlock_irqrestore(&priv->lock, flags);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_run);
> +
> +static int ipu_image_convert_init(struct device *dev, struct ipu_soc *ipu,
> +               struct ipu_ic_priv *priv)
> +{
> +       int ret;
> +       struct ipu_ic *ic = ipu_ic_get(ipu, IC_TASK_POST_PROCESSOR);
> +       int irq = ipu_idmac_channel_irq(ipu, ic->output_channel,
> +                                       IPU_IRQ_EOF);
> +
> +       ic->workqueue = create_singlethread_workqueue(dev_name(ipu->dev));
> +       if (!ic->workqueue)
> +               return -ENOMEM;
> +
> +       INIT_WORK(&ic->work, ipu_image_convert_work);
> +       init_completion(&ic->complete);
> +
> +       ret = devm_request_threaded_irq(dev, irq, NULL,
> +                               ipu_image_convert_handler,
> +                               IRQF_ONESHOT, "IC PP", ic);
> +       if (ret)
> +               goto err;
> +
> +       return 0;
> +err:
> +       destroy_workqueue(ic->workqueue);
> +       return ret;
> +}
> +
>  int ipu_ic_enable(struct ipu_ic *ic)
>  {
>         struct ipu_ic_priv *priv = ic->priv;
> @@ -736,12 +1473,30 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
>         priv->ipu = ipu;
>
>         for (i = 0; i < IC_NUM_TASKS; i++) {
> +               INIT_LIST_HEAD(&priv->task[i].image_list);
>                 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];
> +
> +               priv->task[i].input_channel = ipu_idmac_get(ipu,
> +                                                       ic_task_ch[i].in);
> +               priv->task[i].output_channel = ipu_idmac_get(ipu,
> +                                                       ic_task_ch[i].out);
> +               priv->task[i].rotation_input_channel = ipu_idmac_get(ipu,
> +                                                       ic_task_ch[i].rot_in);
> +               priv->task[i].rotation_output_channel = ipu_idmac_get(ipu,
> +                                                       ic_task_ch[i].rot_out);
> +               if (ic_task_ch[i].in_prev) {
> +                       priv->task[i].input_channel_p = ipu_idmac_get(ipu,
> +                                                       ic_task_ch[i].in_prev);
> +                       priv->task[i].input_channel_n = ipu_idmac_get(ipu,
> +                                                       ic_task_ch[i].in_next);
> +               }
>         }
>
> +       ipu_image_convert_init(dev, ipu, priv);
> +
>         return 0;
>  }
>
> diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
> index 459508e..6d98a38 100644
> --- a/include/video/imx-ipu-v3.h
> +++ b/include/video/imx-ipu-v3.h
> @@ -316,7 +316,8 @@ int ipu_ic_task_init(struct ipu_ic *ic,
>                      int in_width, int in_height,
>                      int out_width, int out_height,
>                      enum ipu_color_space in_cs,
> -                    enum ipu_color_space out_cs);
> +                    enum ipu_color_space out_cs,
> +                    u32 rsc);
>  int ipu_ic_task_graphics_init(struct ipu_ic *ic,
>                               enum ipu_color_space in_g_cs,
>                               bool galpha_en, u32 galpha,
> @@ -362,4 +363,35 @@ struct ipu_client_platformdata {
>         int dma[2];
>  };
>
> +enum ipu_image_scale_ctrl {
> +       IPU_IMAGE_SCALE_ROUND_DOWN,
> +       IPU_IMAGE_SCALE_PIXELPERFECT,
> +       IPU_IMAGE_SCALE_ROUND_UP,
> +};
> +
> +struct image_convert_ctx;
> +
> +struct image_convert_ctx *ipu_image_convert_prepare(struct ipu_soc *ipu,
> +               struct ipu_image *in, struct ipu_image *out,
> +               enum ipu_image_scale_ctrl ctrl, int *num_tiles);
> +int ipu_image_convert_run(struct ipu_soc *ipu, struct ipu_image *in,
> +               struct ipu_image *out, struct image_convert_ctx *ctx,
> +               int num_tiles, void (*complete)(void *ctx, int err),
> +               void *complete_context, bool free_ctx);
> +
> +static inline int ipu_image_convert(struct ipu_soc *ipu, struct ipu_image *in,
> +               struct ipu_image *out, void (*complete)(void *ctx, int err),
> +               void *complete_context, enum ipu_image_scale_ctrl ctrl)
> +{
> +       struct image_convert_ctx *ctx;
> +       int num_tiles;
> +
> +       ctx = ipu_image_convert_prepare(ipu, in, out, ctrl, &num_tiles);
> +       if (IS_ERR(ctx))
> +               return PTR_ERR(ctx);
> +
> +       return ipu_image_convert_run(ipu, in, out, ctx, num_tiles, complete,
> +                                    complete_context, true);
> +}
> +
>  #endif /* __DRM_IPU_H__ */
> --
> 2.1.4
>

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-27 18:42   ` Jean-Michel Hautbois
@ 2015-05-28  9:00     ` Enrico Weigelt, metux IT consult
  2015-05-28 10:44         ` Philipp Zabel
  2015-05-28 10:35     ` Philipp Zabel
  1 sibling, 1 reply; 23+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2015-05-28  9:00 UTC (permalink / raw)
  To: Jean-Michel Hautbois, Philipp Zabel
  Cc: Linux Media Mailing List, ML dri-devel, David Airlie,
	Mauro Carvalho Chehab, Steve Longerbeam, Hans Verkuil,
	Kamil Debski, Ian Molton, Jean-Michel Hautbois, Sascha Hauer,
	Sascha Hauer, Lucas Stach

Am 27.05.2015 um 20:42 schrieb Jean-Michel Hautbois:

<snip>

@Phillip,

I've missed the previous mails (just subscribed here yesterday) ...

Are these patches same as in your git branch tmp/imx-ipu-scaler ?
I've got them running on 4.0.4 and currently trying on 4.1-rc*

Yet another question:

when using it w/ gst for video playback, can be directly pass buffers
between VPU, IPU and FB (or let them directly write into shared
buffers), so CPU doesn't need to act on each frame for each step
in the decoding pipeline ?
Playing an 800x400 mp4 still produces about 70..75%.


cu
--
Enrico Weigelt, metux IT consult
+49-151-27565287
MELAG Medizintechnik oHG Sitz Berlin Registergericht AG Charlottenburg HRA 21333 B

Wichtiger Hinweis: Diese Nachricht kann vertrauliche oder nur für einen begrenzten Personenkreis bestimmte Informationen enthalten. Sie ist ausschließlich für denjenigen bestimmt, an den sie gerichtet worden ist. Wenn Sie nicht der Adressat dieser E-Mail sind, dürfen Sie diese nicht kopieren, weiterleiten, weitergeben oder sie ganz oder teilweise in irgendeiner Weise nutzen. Sollten Sie diese E-Mail irrtümlich erhalten haben, so benachrichtigen Sie bitte den Absender, indem Sie auf diese Nachricht antworten. Bitte löschen Sie in diesem Fall diese Nachricht und alle Anhänge, ohne eine Kopie zu behalten.
Important Notice: This message may contain confidential or privileged information. It is intended only for the person it was addressed to. If you are not the intended recipient of this email you may not copy, forward, disclose or otherwise use it or any part of it in any form whatsoever. If you received this email in error please notify the sender by replying and delete this message and any attachments without retaining a copy.

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-27 18:42   ` Jean-Michel Hautbois
  2015-05-28  9:00     ` Enrico Weigelt, metux IT consult
@ 2015-05-28 10:35     ` Philipp Zabel
  1 sibling, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-05-28 10:35 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Linux Media Mailing List, ML dri-devel, David Airlie,
	Mauro Carvalho Chehab, Steve Longerbeam, Hans Verkuil,
	Kamil Debski, Ian Molton, Jean-Michel Hautbois, Sascha Hauer,
	Sascha Hauer, Lucas Stach

Hi Jean-Michel,

Am Mittwoch, den 27.05.2015, 20:42 +0200 schrieb Jean-Michel Hautbois:
[...]
> > The tiling code has a parameter to optionally round frame sizes up or down
> > and avoid overdraw in compositing scenarios.
> 
> Can you detail what you call "compositing scenarios" ?

I meant using the v4l2 selection API to draw the scaled result into a
compose rectangle in a larger v4l2 capture buffer. To avoid overdrawing
pixels outside of the rectangle, its right edge must be aligned with the
burst size of the rightmost tiles.

[...]
> > @@ -152,6 +203,19 @@ struct ipu_ic {
> >         bool in_use;
> >
> >         struct ipu_ic_priv *priv;
> > +
> > +       struct ipuv3_channel *input_channel_p;
> > +       struct ipuv3_channel *input_channel;
> > +       struct ipuv3_channel *input_channel_n;
> > +       struct ipuv3_channel *output_channel;
> > +       struct ipuv3_channel *rotation_input_channel;
> > +       struct ipuv3_channel *rotation_output_channel;
> > +
> > +       struct list_head image_list;
> > +
> > +       struct workqueue_struct *workqueue;
> > +       struct work_struct work;
> > +       struct completion complete;
> >  };
> 
> As this is a workqueue, it can sleep, and you don't know when it is
> called exactly.
> Can we be sure that it is "real-time" compatible ? If you have this
> scaler after a capture source, and before the coda driver, you can be
> starved of buffers ?
> And you can even have multiple instances of the scaler, so you
> probably can get into troubles if there is not enough buffers on the
> capture and output queues, right ?

When there are no buffers available, the m2m scaler won't run. What do
you mean by "real-time" compatible? We can't make any guarantees that
scaling will be finished in a certain timeframe in general, as the
scaler competes with other hardware units for memory bandwidth, which
often is the limiting factor.
If multiple scaling instances are run on the same IPU, they will take
turns using the IC.

> I have played with it a bit and have been successful having two
> instances on IPU1 and two other on IPU2.
> But I don't know if there can be side effects...

regards
Philipp


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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-28  9:00     ` Enrico Weigelt, metux IT consult
@ 2015-05-28 10:44         ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-05-28 10:44 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Jean-Michel Hautbois, Linux Media Mailing List, ML dri-devel,
	David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	Sascha Hauer, Sascha Hauer, Lucas Stach

Hi Enrico,

Am Donnerstag, den 28.05.2015, 11:00 +0200 schrieb Enrico Weigelt, metux
IT consult:
> Am 27.05.2015 um 20:42 schrieb Jean-Michel Hautbois:
> 
> <snip>
> 
> @Phillip,
> 
> I've missed the previous mails (just subscribed here yesterday) ...
>
> Are these patches same as in your git branch tmp/imx-ipu-scaler ?

No, that is an older version.

> I've got them running on 4.0.4 and currently trying on 4.1-rc*
> 
> Yet another question:
> 
> when using it w/ gst for video playback, can be directly pass buffers
> between VPU, IPU and FB (or let them directly write into shared
> buffers), so CPU doesn't need to act on each frame for each step
> in the decoding pipeline ?

Check out the (capture/output-)io-mode parameters, that's what the
dmabuf/dmabuf-import option pairs are for.

regards
Philipp


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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
@ 2015-05-28 10:44         ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2015-05-28 10:44 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Mauro Carvalho Chehab, Jean-Michel Hautbois, ML dri-devel,
	Hans Verkuil, Ian Molton, Sascha Hauer, Steve Longerbeam,
	Jean-Michel Hautbois, Linux Media Mailing List

Hi Enrico,

Am Donnerstag, den 28.05.2015, 11:00 +0200 schrieb Enrico Weigelt, metux
IT consult:
> Am 27.05.2015 um 20:42 schrieb Jean-Michel Hautbois:
> 
> <snip>
> 
> @Phillip,
> 
> I've missed the previous mails (just subscribed here yesterday) ...
>
> Are these patches same as in your git branch tmp/imx-ipu-scaler ?

No, that is an older version.

> I've got them running on 4.0.4 and currently trying on 4.1-rc*
> 
> Yet another question:
> 
> when using it w/ gst for video playback, can be directly pass buffers
> between VPU, IPU and FB (or let them directly write into shared
> buffers), so CPU doesn't need to act on each frame for each step
> in the decoding pipeline ?

Check out the (capture/output-)io-mode parameters, that's what the
dmabuf/dmabuf-import option pairs are for.

regards
Philipp

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

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-28 10:44         ` Philipp Zabel
  (?)
@ 2015-05-28 11:31         ` Enrico Weigelt, metux IT consult
  2015-05-28 11:59           ` Philipp Zabel
  -1 siblings, 1 reply; 23+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2015-05-28 11:31 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Jean-Michel Hautbois, Linux Media Mailing List, ML dri-devel,
	David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	Sascha Hauer, Sascha Hauer, Lucas Stach

Am 28.05.2015 um 12:44 schrieb Philipp Zabel:

Hi,

 >> Are these patches same as in your git branch tmp/imx-ipu-scaler ?
>
> No, that is an older version.

Where can I get the recent ones ?
Could you push it to your public repo ?

>> when using it w/ gst for video playback, can be directly pass buffers
>> between VPU, IPU and FB (or let them directly write into shared
>> buffers), so CPU doesn't need to act on each frame for each step
>> in the decoding pipeline ?
>
> Check out the (capture/output-)io-mode parameters, that's what the
> dmabuf/dmabuf-import option pairs are for.

Tried dmabuf, but load stays at the same (77..80% CPU, 1.2 loadavg).
dmabuf-import doesnt run at all:

root@KoMo:/usr/share/videos/komo gst-launch-1.0 filesrc
location=montage.mp4 \! qtdemux \! h264parse \! v4l2video4dec
output-io-mode=5 \! v4l2video0convert capture-io-mode=5 output-io-mode=4
\! fbdevsink

Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
ERROR: from element
/GstPipeline:pipeline0/v4l2video0convert:v4l2video0convert0: No
downstream pool to import from.
Additional debug info:
gstv4l2object.c(3441): gst_v4l2_object_decide_allocation ():
/GstPipeline:pipeline0/v4l2video0convert:v4l2video0convert0:
When importing DMABUF or USERPTR, we need a pool to import from
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
Freeing pipeline ...


Perhaps not implemented yet in the old version of the patches ?

By the way: do you have any idea whether the proprietary driver
(or the gpus itself) might talk to ipu and vpu ?


cu
--
Enrico Weigelt, metux IT consult
+49-151-27565287
MELAG Medizintechnik oHG Sitz Berlin Registergericht AG Charlottenburg HRA 21333 B

Wichtiger Hinweis: Diese Nachricht kann vertrauliche oder nur für einen begrenzten Personenkreis bestimmte Informationen enthalten. Sie ist ausschließlich für denjenigen bestimmt, an den sie gerichtet worden ist. Wenn Sie nicht der Adressat dieser E-Mail sind, dürfen Sie diese nicht kopieren, weiterleiten, weitergeben oder sie ganz oder teilweise in irgendeiner Weise nutzen. Sollten Sie diese E-Mail irrtümlich erhalten haben, so benachrichtigen Sie bitte den Absender, indem Sie auf diese Nachricht antworten. Bitte löschen Sie in diesem Fall diese Nachricht und alle Anhänge, ohne eine Kopie zu behalten.
Important Notice: This message may contain confidential or privileged information. It is intended only for the person it was addressed to. If you are not the intended recipient of this email you may not copy, forward, disclose or otherwise use it or any part of it in any form whatsoever. If you received this email in error please notify the sender by replying and delete this message and any attachments without retaining a copy.

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-28 11:31         ` Enrico Weigelt, metux IT consult
@ 2015-05-28 11:59           ` Philipp Zabel
  2015-05-28 17:38             ` Enrico Weigelt, metux IT consult
  0 siblings, 1 reply; 23+ messages in thread
From: Philipp Zabel @ 2015-05-28 11:59 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Jean-Michel Hautbois, Linux Media Mailing List, ML dri-devel,
	David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	Sascha Hauer, Sascha Hauer, Lucas Stach

Am Donnerstag, den 28.05.2015, 13:31 +0200 schrieb Enrico Weigelt, metux
IT consult:
> Am 28.05.2015 um 12:44 schrieb Philipp Zabel:
> 
> Hi,
> 
>  >> Are these patches same as in your git branch tmp/imx-ipu-scaler ?
> >
> > No, that is an older version.
> 
> Where can I get the recent ones ?
> Could you push it to your public repo ?

I've updated the tmp/imx-ipu-scaler branch.

> >> when using it w/ gst for video playback, can be directly pass buffers
> >> between VPU, IPU and FB (or let them directly write into shared
> >> buffers), so CPU doesn't need to act on each frame for each step
> >> in the decoding pipeline ?
> >
> > Check out the (capture/output-)io-mode parameters, that's what the
> > dmabuf/dmabuf-import option pairs are for.
> 
> Tried dmabuf, but load stays at the same (77..80% CPU, 1.2 loadavg).
> dmabuf-import doesnt run at all:
> 
> root@KoMo:/usr/share/videos/komo gst-launch-1.0 filesrc
> location=montage.mp4 \! qtdemux \! h264parse \! v4l2video4dec
> output-io-mode=5 \! v4l2video0convert capture-io-mode=5 output-io-mode=4
> \! fbdevsink

That should be capture-io-mode=dmabuf for the decoder and
output-io-mode=dmabuf-import for the converter element. h264parse
doesn't provide and fbdevsink can't handle dmabufs, so the decoder's
output-io-mode and the converter's capture-io-mode should be kept as
mmio.

[...]
> By the way: do you have any idea whether the proprietary driver
> (or the gpus itself) might talk to ipu and vpu ?

Not that I am aware of.

regards
Philipp


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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-28 11:59           ` Philipp Zabel
@ 2015-05-28 17:38             ` Enrico Weigelt, metux IT consult
  2015-05-28 17:54               ` Robert Schwebel
  0 siblings, 1 reply; 23+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2015-05-28 17:38 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Jean-Michel Hautbois, Linux Media Mailing List, ML dri-devel,
	David Airlie, Mauro Carvalho Chehab, Steve Longerbeam,
	Hans Verkuil, Kamil Debski, Ian Molton, Jean-Michel Hautbois,
	Sascha Hauer, Sascha Hauer, Lucas Stach

Am 28.05.2015 um 13:59 schrieb Philipp Zabel:

>> Where can I get the recent ones ?
>> Could you push it to your public repo ?
>
> I've updated the tmp/imx-ipu-scaler branch.

Thx. already integrated it into my tree - works fine :)

By the way: i still have some your older patches (2012) in my tree,
eg. some mediabus, camara, display timing stuff, etc ... not sure
whether I really need them for my device.

Should I post them to linux-media list for review ?

Oh, and I also still have your famous DRM_IOCTL_MODE_MAP_DUMB hack
(the "Reluctantly-signed-off-by:" one ;-), meanwhile rebased / adapted
into 4.x. Do you have any idea, what the amd-gpu driver/library exactly
does with the retrieved address ? Send it directly to the gpu ?

> That should be capture-io-mode=dmabuf for the decoder and
> output-io-mode=dmabuf-import for the converter element. h264parse
> doesn't provide and fbdevsink can't handle dmabufs, so the decoder's
> output-io-mode and the converter's capture-io-mode should be kept as
> mmio.

I played around a little bit - this command line only takes 55% cpu:

gst-launch-1.0 filesrc location=montage.mp4 \!
qtdemux \! h264parse \! v4l2video4dec output-io-mode=4 capture-io-mode=4
\! v4l2
video0convert capture-io-mode=4 output-io-mode=5 \! fbdevsink

By the way: what's the exact difference between dmabuf and
dmabuf-import ?

 > > By the way: do you have any idea whether the proprietary driver
 > > (or the gpus itself) might talk to ipu and vpu ?
 >
 > Not that I am aware of.

Well, you perhaps can imagine - I dont trust these guys ...



--mtx

ps: greetings from Bene ... you won't guess where I met him
last weekend ;-)
--
Enrico Weigelt, metux IT consult
+49-151-27565287
MELAG Medizintechnik oHG Sitz Berlin Registergericht AG Charlottenburg HRA 21333 B

Wichtiger Hinweis: Diese Nachricht kann vertrauliche oder nur für einen begrenzten Personenkreis bestimmte Informationen enthalten. Sie ist ausschließlich für denjenigen bestimmt, an den sie gerichtet worden ist. Wenn Sie nicht der Adressat dieser E-Mail sind, dürfen Sie diese nicht kopieren, weiterleiten, weitergeben oder sie ganz oder teilweise in irgendeiner Weise nutzen. Sollten Sie diese E-Mail irrtümlich erhalten haben, so benachrichtigen Sie bitte den Absender, indem Sie auf diese Nachricht antworten. Bitte löschen Sie in diesem Fall diese Nachricht und alle Anhänge, ohne eine Kopie zu behalten.
Important Notice: This message may contain confidential or privileged information. It is intended only for the person it was addressed to. If you are not the intended recipient of this email you may not copy, forward, disclose or otherwise use it or any part of it in any form whatsoever. If you received this email in error please notify the sender by replying and delete this message and any attachments without retaining a copy.

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-28 17:38             ` Enrico Weigelt, metux IT consult
@ 2015-05-28 17:54               ` Robert Schwebel
  2015-05-29  9:02                 ` Enrico Weigelt, metux IT consult
  0 siblings, 1 reply; 23+ messages in thread
From: Robert Schwebel @ 2015-05-28 17:54 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Philipp Zabel, ML dri-devel, Linux Media Mailing List

On Thu, May 28, 2015 at 07:38:20PM +0200, Enrico Weigelt, metux IT consult wrote:
> Thx. already integrated it into my tree - works fine :)
> 
> By the way: i still have some your older patches (2012) in my tree,
> eg. some mediabus, camara, display timing stuff, etc ... not sure
> whether I really need them for my device.
> 
> Should I post them to linux-media list for review?

No. That's all old stuff and has developed quite a lot since then. We'll
post new series here on the lists when they are ready for mainline.

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-05-28 17:54               ` Robert Schwebel
@ 2015-05-29  9:02                 ` Enrico Weigelt, metux IT consult
  0 siblings, 0 replies; 23+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2015-05-29  9:02 UTC (permalink / raw)
  To: Robert Schwebel; +Cc: Philipp Zabel, ML dri-devel, Linux Media Mailing List

Am 28.05.2015 um 19:54 schrieb Robert Schwebel:

>> By the way: i still have some your older patches (2012) in my tree,
>> eg. some mediabus, camara, display timing stuff, etc ... not sure
>> whether I really need them for my device.
>>
>> Should I post them to linux-media list for review?
>
> No. That's all old stuff and has developed quite a lot since then. We'll
> post new series here on the lists when they are ready for mainline.

Great :)

Do you have them on some public repo, so I can give 'em a try ?


--mtx

--
Enrico Weigelt, metux IT consult
+49-151-27565287
MELAG Medizintechnik oHG Sitz Berlin Registergericht AG Charlottenburg HRA 21333 B

Wichtiger Hinweis: Diese Nachricht kann vertrauliche oder nur für einen begrenzten Personenkreis bestimmte Informationen enthalten. Sie ist ausschließlich für denjenigen bestimmt, an den sie gerichtet worden ist. Wenn Sie nicht der Adressat dieser E-Mail sind, dürfen Sie diese nicht kopieren, weiterleiten, weitergeben oder sie ganz oder teilweise in irgendeiner Weise nutzen. Sollten Sie diese E-Mail irrtümlich erhalten haben, so benachrichtigen Sie bitte den Absender, indem Sie auf diese Nachricht antworten. Bitte löschen Sie in diesem Fall diese Nachricht und alle Anhänge, ohne eine Kopie zu behalten.
Important Notice: This message may contain confidential or privileged information. It is intended only for the person it was addressed to. If you are not the intended recipient of this email you may not copy, forward, disclose or otherwise use it or any part of it in any form whatsoever. If you received this email in error please notify the sender by replying and delete this message and any attachments without retaining a copy.

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

end of thread, other threads:[~2015-05-29  9:02 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-18 10:22 [PATCH v2 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
2015-03-18 10:22 ` [PATCH v2 1/5] gpu: ipu-v3: Add missing IDMAC channel names Philipp Zabel
2015-03-18 10:22   ` Philipp Zabel
2015-03-18 10:22 ` [PATCH v2 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC Philipp Zabel
2015-03-18 10:22   ` Philipp Zabel
2015-05-27 18:42   ` Jean-Michel Hautbois
2015-05-28  9:00     ` Enrico Weigelt, metux IT consult
2015-05-28 10:44       ` Philipp Zabel
2015-05-28 10:44         ` Philipp Zabel
2015-05-28 11:31         ` Enrico Weigelt, metux IT consult
2015-05-28 11:59           ` Philipp Zabel
2015-05-28 17:38             ` Enrico Weigelt, metux IT consult
2015-05-28 17:54               ` Robert Schwebel
2015-05-29  9:02                 ` Enrico Weigelt, metux IT consult
2015-05-28 10:35     ` Philipp Zabel
2015-03-18 10:22 ` [PATCH v2 3/5] gpu: ipu-v3: Register scaler platform device Philipp Zabel
2015-03-18 10:22   ` Philipp Zabel
2015-03-18 10:22 ` [PATCH v2 4/5] [media] imx-ipu: Add ipu media common code Philipp Zabel
2015-03-18 10:22   ` Philipp Zabel
2015-03-18 10:22 ` [PATCH v2 5/5] [media] imx-ipu: Add i.MX IPUv3 scaler driver Philipp Zabel
2015-03-18 10:22   ` Philipp Zabel
2015-04-10 13:03 ` [PATCH v2 0/5] i.MX5/6 mem2mem scaler Jean-Michel Hautbois
2015-04-10 13:03   ` Jean-Michel Hautbois

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.