All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] i.MX5/6 mem2mem scaler
@ 2015-03-17 15:48 ` Philipp Zabel
  0 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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.

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] 17+ messages in thread

* [PATCH 0/5] i.MX5/6 mem2mem scaler
@ 2015-03-17 15:48 ` Philipp Zabel
  0 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 UTC (permalink / raw)
  To: linux-media
  Cc: Mauro Carvalho Chehab, dri-devel, Hans Verkuil, kernel,
	Steve Longerbeam, Jean-Michel Hautbois

Hi,

this series uses the IPU IC post-processing task, to implement
a mem2mem device for scaling and colorspace conversion.

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

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

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

* [PATCH 1/5] gpu: ipu-v3: Add missing IDMAC channel names
  2015-03-17 15:48 ` Philipp Zabel
  (?)
@ 2015-03-17 15:48 ` Philipp Zabel
  -1 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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] 17+ messages in thread

* [PATCH 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC
  2015-03-17 15:48 ` Philipp Zabel
  (?)
  (?)
@ 2015-03-17 15:48 ` Philipp Zabel
  -1 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 813 +++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |  34 +-
 2 files changed, 830 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index ad75588..39ee388 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,701 @@ 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_in_p, *ch_in_n;
+	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);
+
+	if (ctx->deinterlace) {
+		ch_in_p = ic->input_channel_p;
+		ch_in_n = ic->input_channel_n;
+
+		ipu_cpmem_zero(ch_in_p);
+		ipu_cpmem_zero(ch_in_n);
+
+		ipu_ic_task_idma_init(ic, ic->input_channel_p,
+				      in_p->rect.width, in_p->rect.height,
+				      inburst, IPU_ROTATE_NONE);
+		ipu_ic_task_idma_init(ic, ic->input_channel_n,
+				      in_n->rect.width, in_n->rect.height,
+				      inburst, IPU_ROTATE_NONE);
+
+		ipu_cpmem_set_image(ch_in_p, &ctx->in_p);
+		ipu_cpmem_set_image(ch_in_n, &ctx->in_n);
+
+		ipu_cpmem_set_burstsize(ch_in_p, inburst);
+		ipu_cpmem_set_burstsize(ch_in_n, inburst);
+	}
+
+
+	if (ctx->deinterlace)
+		in_height = in->rect.height * 2;
+	else
+		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 +1499,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] 17+ messages in thread

* [PATCH 3/5] gpu: ipu-v3: Register scaler platform device
  2015-03-17 15:48 ` Philipp Zabel
@ 2015-03-17 15:48   ` Philipp Zabel
  -1 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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] 17+ messages in thread

* [PATCH 3/5] gpu: ipu-v3: Register scaler platform device
@ 2015-03-17 15:48   ` Philipp Zabel
  0 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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] 17+ messages in thread

* [PATCH 4/5] [media] imx-ipu: Add ipu media common code
  2015-03-17 15:48 ` Philipp Zabel
@ 2015-03-17 15:48   ` Philipp Zabel
  -1 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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] 17+ messages in thread

* [PATCH 4/5] [media] imx-ipu: Add ipu media common code
@ 2015-03-17 15:48   ` Philipp Zabel
  0 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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] 17+ messages in thread

* [PATCH 5/5] [media] imx-ipu: Add i.MX IPUv3 scaler driver
  2015-03-17 15:48 ` Philipp Zabel
                   ` (4 preceding siblings ...)
  (?)
@ 2015-03-17 15:48 ` Philipp Zabel
  2015-04-10 14:22     ` Kamil Debski
  -1 siblings, 1 reply; 17+ messages in thread
From: Philipp Zabel @ 2015-03-17 15:48 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>
---
 drivers/gpu/ipu-v3/ipu-ic.c                 |  28 +-
 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 +
 6 files changed, 882 insertions(+), 28 deletions(-)
 create mode 100644 drivers/media/platform/imx/imx-ipu-scaler.c

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 39ee388..984f68f 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -701,7 +701,6 @@ 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_in_p, *ch_in_n;
 	struct ipuv3_channel *ch_out = ic->output_channel;
 	struct image_convert_ctx *ctx;
 	struct ipu_image *in_p, *in, *in_n;
@@ -748,32 +747,7 @@ static struct image_convert_ctx *ipu_image_convert_next(struct ipu_ic *ic)
 	ipu_cpmem_set_burstsize(ch_in, inburst);
 	ipu_cpmem_set_burstsize(ch_out, outburst);
 
-	if (ctx->deinterlace) {
-		ch_in_p = ic->input_channel_p;
-		ch_in_n = ic->input_channel_n;
-
-		ipu_cpmem_zero(ch_in_p);
-		ipu_cpmem_zero(ch_in_n);
-
-		ipu_ic_task_idma_init(ic, ic->input_channel_p,
-				      in_p->rect.width, in_p->rect.height,
-				      inburst, IPU_ROTATE_NONE);
-		ipu_ic_task_idma_init(ic, ic->input_channel_n,
-				      in_n->rect.width, in_n->rect.height,
-				      inburst, IPU_ROTATE_NONE);
-
-		ipu_cpmem_set_image(ch_in_p, &ctx->in_p);
-		ipu_cpmem_set_image(ch_in_n, &ctx->in_n);
-
-		ipu_cpmem_set_burstsize(ch_in_p, inburst);
-		ipu_cpmem_set_burstsize(ch_in_n, inburst);
-	}
-
-
-	if (ctx->deinterlace)
-		in_height = in->rect.height * 2;
-	else
-		in_height = in->rect.height;
+	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,
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] 17+ messages in thread

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

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Philipp Zabel
Sent: Tuesday, March 17, 2015 4:48 PM
> 
> 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>

Acked-by: Kamil Debski <k.debski@samsung.com>

> ---
>  drivers/gpu/ipu-v3/ipu-ic.c                 |  28 +-
>  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 +
>  6 files changed, 882 insertions(+), 28 deletions(-)  create mode
> 100644 drivers/media/platform/imx/imx-ipu-scaler.c
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index 39ee388..984f68f 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -701,7 +701,6 @@ 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_in_p, *ch_in_n;
>  	struct ipuv3_channel *ch_out = ic->output_channel;
>  	struct image_convert_ctx *ctx;
>  	struct ipu_image *in_p, *in, *in_n;
> @@ -748,32 +747,7 @@ static struct image_convert_ctx
> *ipu_image_convert_next(struct ipu_ic *ic)
>  	ipu_cpmem_set_burstsize(ch_in, inburst);
>  	ipu_cpmem_set_burstsize(ch_out, outburst);
> 
> -	if (ctx->deinterlace) {
> -		ch_in_p = ic->input_channel_p;
> -		ch_in_n = ic->input_channel_n;
> -
> -		ipu_cpmem_zero(ch_in_p);
> -		ipu_cpmem_zero(ch_in_n);
> -
> -		ipu_ic_task_idma_init(ic, ic->input_channel_p,
> -				      in_p->rect.width, in_p->rect.height,
> -				      inburst, IPU_ROTATE_NONE);
> -		ipu_ic_task_idma_init(ic, ic->input_channel_n,
> -				      in_n->rect.width, in_n->rect.height,
> -				      inburst, IPU_ROTATE_NONE);
> -
> -		ipu_cpmem_set_image(ch_in_p, &ctx->in_p);
> -		ipu_cpmem_set_image(ch_in_n, &ctx->in_n);
> -
> -		ipu_cpmem_set_burstsize(ch_in_p, inburst);
> -		ipu_cpmem_set_burstsize(ch_in_n, inburst);
> -	}
> -
> -
> -	if (ctx->deinterlace)
> -		in_height = in->rect.height * 2;
> -	else
> -		in_height = in->rect.height;
> +	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, 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html


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

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

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Philipp Zabel
Sent: Tuesday, March 17, 2015 4:48 PM
> 
> 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>

Acked-by: Kamil Debski <k.debski@samsung.com>

> ---
>  drivers/gpu/ipu-v3/ipu-ic.c                 |  28 +-
>  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 +
>  6 files changed, 882 insertions(+), 28 deletions(-)  create mode
> 100644 drivers/media/platform/imx/imx-ipu-scaler.c
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index 39ee388..984f68f 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -701,7 +701,6 @@ 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_in_p, *ch_in_n;
>  	struct ipuv3_channel *ch_out = ic->output_channel;
>  	struct image_convert_ctx *ctx;
>  	struct ipu_image *in_p, *in, *in_n;
> @@ -748,32 +747,7 @@ static struct image_convert_ctx
> *ipu_image_convert_next(struct ipu_ic *ic)
>  	ipu_cpmem_set_burstsize(ch_in, inburst);
>  	ipu_cpmem_set_burstsize(ch_out, outburst);
> 
> -	if (ctx->deinterlace) {
> -		ch_in_p = ic->input_channel_p;
> -		ch_in_n = ic->input_channel_n;
> -
> -		ipu_cpmem_zero(ch_in_p);
> -		ipu_cpmem_zero(ch_in_n);
> -
> -		ipu_ic_task_idma_init(ic, ic->input_channel_p,
> -				      in_p->rect.width, in_p->rect.height,
> -				      inburst, IPU_ROTATE_NONE);
> -		ipu_ic_task_idma_init(ic, ic->input_channel_n,
> -				      in_n->rect.width, in_n->rect.height,
> -				      inburst, IPU_ROTATE_NONE);
> -
> -		ipu_cpmem_set_image(ch_in_p, &ctx->in_p);
> -		ipu_cpmem_set_image(ch_in_n, &ctx->in_n);
> -
> -		ipu_cpmem_set_burstsize(ch_in_p, inburst);
> -		ipu_cpmem_set_burstsize(ch_in_n, inburst);
> -	}
> -
> -
> -	if (ctx->deinterlace)
> -		in_height = in->rect.height * 2;
> -	else
> -		in_height = in->rect.height;
> +	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, 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html

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

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

* RE: [PATCH 4/5] [media] imx-ipu: Add ipu media common code
  2015-03-17 15:48   ` Philipp Zabel
@ 2015-04-10 14:22     ` Kamil Debski
  -1 siblings, 0 replies; 17+ messages in thread
From: Kamil Debski @ 2015-04-10 14:22 UTC (permalink / raw)
  To: 'Philipp Zabel', linux-media
  Cc: dri-devel, 'David Airlie',
	'Mauro Carvalho Chehab', 'Steve Longerbeam',
	'Hans Verkuil', 'Ian Molton',
	'Jean-Michel Hautbois', kernel, 'Sascha Hauer',
	'Lucas Stach'

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Philipp Zabel
Sent: Tuesday, March 17, 2015 4:48 PM
> To: linux-media@vger.kernel.org
> Cc: dri-devel@lists.freedesktop.org; David Airlie; Mauro Carvalho
> Chehab; Steve Longerbeam; Hans Verkuil; Kamil Debski; Ian Molton; Jean-
> Michel Hautbois; kernel@pengutronix.de; Sascha Hauer; Lucas Stach;
> Philipp Zabel
> Subject: [PATCH 4/5] [media] imx-ipu: Add ipu media common code
> 
> 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>

Acked-by: Kamil Debski <k.debski@samsung.com>

> ---
>  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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html


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

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

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Philipp Zabel
Sent: Tuesday, March 17, 2015 4:48 PM
> To: linux-media@vger.kernel.org
> Cc: dri-devel@lists.freedesktop.org; David Airlie; Mauro Carvalho
> Chehab; Steve Longerbeam; Hans Verkuil; Kamil Debski; Ian Molton; Jean-
> Michel Hautbois; kernel@pengutronix.de; Sascha Hauer; Lucas Stach;
> Philipp Zabel
> Subject: [PATCH 4/5] [media] imx-ipu: Add ipu media common code
> 
> 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>

Acked-by: Kamil Debski <k.debski@samsung.com>

> ---
>  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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html

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

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

* RE: [PATCH 0/5] i.MX5/6 mem2mem scaler
  2015-03-17 15:48 ` Philipp Zabel
@ 2015-04-10 14:41   ` Kamil Debski
  -1 siblings, 0 replies; 17+ messages in thread
From: Kamil Debski @ 2015-04-10 14:41 UTC (permalink / raw)
  To: 'Philipp Zabel', linux-media
  Cc: dri-devel, 'David Airlie',
	'Mauro Carvalho Chehab', 'Steve Longerbeam',
	'Hans Verkuil', 'Ian Molton',
	'Jean-Michel Hautbois',
	kernel

Hi,

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Philipp Zabel
Sent: Tuesday, March 17, 2015 4:48 PM
> 
> Hi,
> 
> this series uses the IPU IC post-processing task, to implement a
> mem2mem device for scaling and colorspace conversion.

This patchset makes changes in two subsystems - media and gpu.
It would be good to merge these patchset through a single subsystem.

The media part of this patchset is good, are there any comments to
the gpu part of this patchset?

I talked with Mauro on the IRC and he acked that this patchset could be
merged via the gpu subsystem. 

Best wishes,
Kamil Debski

 
> 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html


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

* RE: [PATCH 0/5] i.MX5/6 mem2mem scaler
@ 2015-04-10 14:41   ` Kamil Debski
  0 siblings, 0 replies; 17+ messages in thread
From: Kamil Debski @ 2015-04-10 14:41 UTC (permalink / raw)
  To: 'Philipp Zabel', linux-media
  Cc: 'Mauro Carvalho Chehab',
	dri-devel, 'Hans Verkuil',
	kernel, 'Steve Longerbeam',
	'Jean-Michel Hautbois'

Hi,

From: linux-media-owner@vger.kernel.org [mailto:linux-media-
owner@vger.kernel.org] On Behalf Of Philipp Zabel
Sent: Tuesday, March 17, 2015 4:48 PM
> 
> Hi,
> 
> this series uses the IPU IC post-processing task, to implement a
> mem2mem device for scaling and colorspace conversion.

This patchset makes changes in two subsystems - media and gpu.
It would be good to merge these patchset through a single subsystem.

The media part of this patchset is good, are there any comments to
the gpu part of this patchset?

I talked with Mauro on the IRC and he acked that this patchset could be
merged via the gpu subsystem. 

Best wishes,
Kamil Debski

 
> 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html

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

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

* Re: [PATCH 0/5] i.MX5/6 mem2mem scaler
  2015-04-10 14:41   ` Kamil Debski
  (?)
@ 2015-07-13 12:57   ` Fabio Estevam
  2015-07-14 13:00     ` Philipp Zabel
  -1 siblings, 1 reply; 17+ messages in thread
From: Fabio Estevam @ 2015-07-13 12:57 UTC (permalink / raw)
  To: Kamil Debski, Philipp Zabel
  Cc: linux-media, Mauro Carvalho Chehab, DRI mailing list,
	Hans Verkuil, Sascha Hauer, Steve Longerbeam,
	Jean-Michel Hautbois

Hi Philipp,

On Fri, Apr 10, 2015 at 11:41 AM, Kamil Debski <k.debski@samsung.com> wrote:
> Hi,
>
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Philipp Zabel
> Sent: Tuesday, March 17, 2015 4:48 PM
>>
>> Hi,
>>
>> this series uses the IPU IC post-processing task, to implement a
>> mem2mem device for scaling and colorspace conversion.
>
> This patchset makes changes in two subsystems - media and gpu.
> It would be good to merge these patchset through a single subsystem.
>
> The media part of this patchset is good, are there any comments to
> the gpu part of this patchset?
>
> I talked with Mauro on the IRC and he acked that this patchset could be
> merged via the gpu subsystem.

Do you plan to resend this series?

It is still not applied.

Regards,

Fabio Estevam

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

* Re: [PATCH 0/5] i.MX5/6 mem2mem scaler
  2015-07-13 12:57   ` Fabio Estevam
@ 2015-07-14 13:00     ` Philipp Zabel
  0 siblings, 0 replies; 17+ messages in thread
From: Philipp Zabel @ 2015-07-14 13:00 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Kamil Debski, linux-media, Mauro Carvalho Chehab,
	DRI mailing list, Hans Verkuil, Sascha Hauer, Steve Longerbeam,
	Jean-Michel Hautbois

Hi Fabio,

Am Montag, den 13.07.2015, 09:57 -0300 schrieb Fabio Estevam:
> On Fri, Apr 10, 2015 at 11:41 AM, Kamil Debski <k.debski@samsung.com> wrote:
> > Hi,
> >
> > From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> > owner@vger.kernel.org] On Behalf Of Philipp Zabel
> > Sent: Tuesday, March 17, 2015 4:48 PM
> >>
> >> Hi,
> >>
> >> this series uses the IPU IC post-processing task, to implement a
> >> mem2mem device for scaling and colorspace conversion.
> >
> > This patchset makes changes in two subsystems - media and gpu.
> > It would be good to merge these patchset through a single subsystem.
> >
> > The media part of this patchset is good, are there any comments to
> > the gpu part of this patchset?
> >
> > I talked with Mauro on the IRC and he acked that this patchset could be
> > merged via the gpu subsystem.
> 
> Do you plan to resend this series?
> 
> It is still not applied.

Yes, thank you for the reminder.

regards
Philipp


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

end of thread, other threads:[~2015-07-14 13:00 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-17 15:48 [PATCH 0/5] i.MX5/6 mem2mem scaler Philipp Zabel
2015-03-17 15:48 ` Philipp Zabel
2015-03-17 15:48 ` [PATCH 1/5] gpu: ipu-v3: Add missing IDMAC channel names Philipp Zabel
2015-03-17 15:48 ` [PATCH 2/5] gpu: ipu-v3: Add mem2mem image conversion support to IC Philipp Zabel
2015-03-17 15:48 ` [PATCH 3/5] gpu: ipu-v3: Register scaler platform device Philipp Zabel
2015-03-17 15:48   ` Philipp Zabel
2015-03-17 15:48 ` [PATCH 4/5] [media] imx-ipu: Add ipu media common code Philipp Zabel
2015-03-17 15:48   ` Philipp Zabel
2015-04-10 14:22   ` Kamil Debski
2015-04-10 14:22     ` Kamil Debski
2015-03-17 15:48 ` [PATCH 5/5] [media] imx-ipu: Add i.MX IPUv3 scaler driver Philipp Zabel
2015-04-10 14:22   ` Kamil Debski
2015-04-10 14:22     ` Kamil Debski
2015-04-10 14:41 ` [PATCH 0/5] i.MX5/6 mem2mem scaler Kamil Debski
2015-04-10 14:41   ` Kamil Debski
2015-07-13 12:57   ` Fabio Estevam
2015-07-14 13:00     ` Philipp Zabel

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