All of lore.kernel.org
 help / color / mirror / Atom feed
From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
To: linux-media@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org,
	Kieran Bingham <kieran@ksquared.org.uk>
Subject: [PATCH v3 10/10] v4l: fdp1: Store buffer information in vb2 buffer
Date: Thu,  8 Sep 2016 01:25:10 +0300	[thread overview]
Message-ID: <1473287110-780-11-git-send-email-laurent.pinchart+renesas@ideasonboard.com> (raw)
In-Reply-To: <1473287110-780-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>

The struct fdp1_buffer instances are allocated separately from the vb2
buffers, with one instance per field. Simplify the allocation by
splitting the fdp1_buffer structure in per-buffer and per-field data,
and let vb2 allocate the the fdp1_buffer structure.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/rcar_fdp1.c | 437 ++++++++++++++++++-------------------
 1 file changed, 210 insertions(+), 227 deletions(-)

diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c
index c25531a919db..d4101a4fd114 100644
--- a/drivers/media/platform/rcar_fdp1.c
+++ b/drivers/media/platform/rcar_fdp1.c
@@ -473,12 +473,12 @@ static const u8 fdp1_mdet[] = {
 
 /* Per-queue, driver-specific private data */
 struct fdp1_q_data {
-	const struct fdp1_fmt	*fmt;
-	struct v4l2_pix_format_mplane format;
+	const struct fdp1_fmt		*fmt;
+	struct v4l2_pix_format_mplane	format;
 
-	unsigned int		vsize;
-	unsigned int		stride_y;
-	unsigned int		stride_c;
+	unsigned int			vsize;
+	unsigned int			stride_y;
+	unsigned int			stride_c;
 };
 
 static const struct fdp1_fmt *fdp1_find_format(u32 pixelformat)
@@ -519,91 +519,102 @@ enum fdp1_deint_mode {
  * from the VB buffers using this context structure.
  * Will always be a field or a full frame, never two fields.
  */
-struct fdp1_buffer {
-	struct vb2_v4l2_buffer	*vb;
-	dma_addr_t		addrs[3];
+struct fdp1_field_buffer {
+	struct vb2_v4l2_buffer		*vb;
+	dma_addr_t			addrs[3];
 
 	/* Should be NONE:TOP:BOTTOM only */
-	enum v4l2_field		field;
+	enum v4l2_field			field;
 
 	/* Flag to indicate this is the last field in the vb */
-	bool			last_field;
+	bool				last_field;
 
 	/* Buffer queue lists */
-	struct list_head	list;
+	struct list_head		list;
+};
+
+struct fdp1_buffer {
+	struct v4l2_m2m_buffer		m2m_buf;
+	struct fdp1_field_buffer	fields[2];
+	unsigned int			num_fields;
 };
 
+static inline struct fdp1_buffer *to_fdp1_buffer(struct vb2_v4l2_buffer *vb)
+{
+	return container_of(vb, struct fdp1_buffer, m2m_buf.vb);
+}
+
 struct fdp1_job {
 	/* These could be pointers to save 'memory' and copying */
-	struct fdp1_buffer	*previous;
-	struct fdp1_buffer	*active;
-	struct fdp1_buffer	*next;
-	struct fdp1_buffer	dst;
+	struct fdp1_field_buffer	*previous;
+	struct fdp1_field_buffer	*active;
+	struct fdp1_field_buffer	*next;
+	struct fdp1_field_buffer	*dst;
 
 	/* A job can only be on one list at a time */
-	struct list_head	list;
+	struct list_head		list;
 };
 
 struct fdp1_dev {
-	struct v4l2_device	v4l2_dev;
-	struct video_device	vfd;
+	struct v4l2_device		v4l2_dev;
+	struct video_device		vfd;
 
-	struct mutex		dev_mutex;
-	spinlock_t		irqlock;
-	spinlock_t		device_process_lock;
+	struct mutex			dev_mutex;
+	spinlock_t			irqlock;
+	spinlock_t			device_process_lock;
 
-	void __iomem		*regs;
-	unsigned int		irq;
-	struct device		*dev;
+	void __iomem			*regs;
+	unsigned int			irq;
+	struct device			*dev;
 
 	/* Job Queues */
-	struct fdp1_job		jobs[FDP1_NUMBER_JOBS];
-	struct list_head	free_job_list;
-	struct list_head	queued_job_list;
-	struct list_head	hw_job_list;
+	struct fdp1_job			jobs[FDP1_NUMBER_JOBS];
+	struct list_head		free_job_list;
+	struct list_head		queued_job_list;
+	struct list_head		hw_job_list;
 
-	unsigned int		clk_rate;
+	unsigned int			clk_rate;
 
-	struct rcar_fcp_device	*fcp;
-	struct v4l2_m2m_dev	*m2m_dev;
+	struct rcar_fcp_device		*fcp;
+	struct v4l2_m2m_dev		*m2m_dev;
 };
 
 struct fdp1_ctx {
-	struct v4l2_fh		fh;
-	struct fdp1_dev		*fdp1;
+	struct v4l2_fh			fh;
+	struct fdp1_dev			*fdp1;
 
-	struct v4l2_ctrl_handler hdl;
-	unsigned int		sequence;
+	struct v4l2_ctrl_handler	hdl;
+	unsigned int			sequence;
 
 	/* Processed buffers in this transaction */
-	u8			num_processed;
+	u8				num_processed;
 
 	/* Transaction length (i.e. how many buffers per transaction) */
-	u32			translen;
+	u32				translen;
 
 	/* Abort requested by m2m */
-	int			aborting;
+	int				aborting;
 
 	/* Deinterlace processing mode */
-	enum fdp1_deint_mode	deint_mode;
+	enum fdp1_deint_mode		deint_mode;
 
 	/*
 	 * Adaptive 2d 3d mode uses a shared mask
 	 * This is allocated at streamon, if the ADAPT2D3D mode
 	 * is requested
 	 */
-	unsigned int		smsk_size;
-	dma_addr_t		smsk_addr[2];
-	void			*smsk_cpu;
+	unsigned int			smsk_size;
+	dma_addr_t			smsk_addr[2];
+	void				*smsk_cpu;
 
 	/* Capture pipeline, can specify an alpha value
 	 * for supported formats. 0-255 only
 	 */
-	unsigned char		alpha;
+	unsigned char			alpha;
 
 	/* Source and destination queue data */
-	struct fdp1_q_data	out_q; /* HW Source */
-	struct fdp1_q_data	cap_q; /* HW Destination */
+	struct fdp1_q_data		out_q; /* HW Source */
+	struct fdp1_q_data		cap_q; /* HW Destination */
 
 	/*
 	 * Field Queues
@@ -613,16 +624,14 @@ struct fdp1_ctx {
 	 * V4L2 Buffers are tracked inside the fdp1_buffer
 	 * and released when the last 'field' completes
 	 */
-	struct fdp1_buffer	buffers[FDP1_NUMBER_BUFFERS];
-	struct list_head	free_buffers;
-	struct list_head	fdp1_buffer_queue;
-	unsigned int		buffers_queued;
+	struct list_head		fields_queue;
+	unsigned int			buffers_queued;
 
 	/*
 	 * For de-interlacing we need to track our previous buffer
 	 * while preparing our job lists.
 	 */
-	struct fdp1_buffer	*previous;
+	struct fdp1_field_buffer	*previous;
 };
 
 static inline struct fdp1_ctx *fh_to_ctx(struct v4l2_fh *fh)
@@ -711,90 +720,61 @@ static struct fdp1_job *get_hw_queued_job(struct fdp1_dev *fdp1)
 /*
  * Buffer lists handling
  */
-static struct fdp1_buffer *list_remove_buffer(struct fdp1_dev *fdp1,
-					       struct list_head *list)
+static void fdp1_field_complete(struct fdp1_ctx *ctx,
+				struct fdp1_field_buffer *fbuf)
 {
-	struct fdp1_buffer *buf;
-	unsigned long flags;
-
-	spin_lock_irqsave(&fdp1->irqlock, flags);
-	buf = list_first_entry_or_null(list, struct fdp1_buffer, list);
-	if (buf)
-		list_del(&buf->list);
-	spin_unlock_irqrestore(&fdp1->irqlock, flags);
+	/* job->previous may be on the first field */
+	if (!fbuf)
+		return;
 
-	return buf;
+	if (fbuf->last_field)
+		v4l2_m2m_buf_done(fbuf->vb, VB2_BUF_STATE_DONE);
 }
 
-/*
- * list_add_buffer: Add a buffer to the specified list
- *
- * Returns: void - always succeeds
- */
-static void list_add_buffer(struct fdp1_dev *fdp1,
-			    struct list_head *list,
-			    struct fdp1_buffer *buf)
+static void fdp1_queue_field(struct fdp1_ctx *ctx,
+			     struct fdp1_field_buffer *fbuf)
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&fdp1->irqlock, flags);
-	list_add_tail(&buf->list, list);
-	spin_unlock_irqrestore(&fdp1->irqlock, flags);
-}
+	spin_lock_irqsave(&ctx->fdp1->irqlock, flags);
+	list_add_tail(&fbuf->list, &ctx->fields_queue);
+	spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags);
 
-/*
- * fdp1_buffer_alloc: Retrieve a buffer to track a single field/frame
- *
- * Must always return a buffer, and should block if necessary.
- */
-static struct fdp1_buffer *fdp1_buffer_alloc(struct fdp1_ctx *ctx)
-{
-	return list_remove_buffer(ctx->fdp1, &ctx->free_buffers);
+	ctx->buffers_queued++;
 }
 
-static void fdp1_buffer_free(struct fdp1_ctx *ctx,
-			struct fdp1_buffer *buf)
+static struct fdp1_field_buffer *fdp1_dequeue_field(struct fdp1_ctx *ctx)
 {
-	/* job->previous may be on the first field */
-	if (!buf)
-		return;
-
-	if (buf->last_field)
-		v4l2_m2m_buf_done(buf->vb, VB2_BUF_STATE_DONE);
-
-	memset(buf, 0, sizeof(struct fdp1_buffer));
+	struct fdp1_field_buffer *fbuf;
+	unsigned long flags;
 
-	list_add_buffer(ctx->fdp1, &ctx->free_buffers, buf);
-}
+	ctx->buffers_queued--;
 
-static void queue_buffer(struct fdp1_ctx *ctx, struct fdp1_buffer *buf)
-{
-	list_add_buffer(ctx->fdp1, &ctx->fdp1_buffer_queue, buf);
-	ctx->buffers_queued++;
-}
+	spin_lock_irqsave(&ctx->fdp1->irqlock, flags);
+	fbuf = list_first_entry_or_null(&ctx->fields_queue,
+					struct fdp1_field_buffer, list);
+	if (fbuf)
+		list_del(&fbuf->list);
+	spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags);
 
-static struct fdp1_buffer *dequeue_buffer(struct fdp1_ctx *ctx)
-{
-	ctx->buffers_queued--;
-	return list_remove_buffer(ctx->fdp1, &ctx->fdp1_buffer_queue);
+	return fbuf;
 }
 
 /*
- * Return the next buffer in the queue - or NULL,
+ * Return the next field in the queue - or NULL,
  * without removing the item from the list
  */
-static struct fdp1_buffer *peek_queued_buffer(struct fdp1_ctx *ctx)
+static struct fdp1_field_buffer *fdp1_peek_queued_field(struct fdp1_ctx *ctx)
 {
-	struct fdp1_dev *fdp1 = ctx->fdp1;
+	struct fdp1_field_buffer *fbuf;
 	unsigned long flags;
-	struct fdp1_buffer *buf;
 
-	spin_lock_irqsave(&fdp1->irqlock, flags);
-	buf = list_first_entry_or_null(&ctx->fdp1_buffer_queue,
-			struct fdp1_buffer, list);
-	spin_unlock_irqrestore(&fdp1->irqlock, flags);
+	spin_lock_irqsave(&ctx->fdp1->irqlock, flags);
+	fbuf = list_first_entry_or_null(&ctx->fields_queue,
+					struct fdp1_field_buffer, list);
+	spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags);
 
-	return buf;
+	return fbuf;
 }
 
 static u32 fdp1_read(struct fdp1_dev *fdp1, unsigned int reg)
@@ -1015,9 +995,9 @@ static void fdp1_configure_wpf(struct fdp1_ctx *ctx,
 	fdp1_write(fdp1, swap, FD1_WPF_SWAP);
 	fdp1_write(fdp1, pstride, FD1_WPF_PSTRIDE);
 
-	fdp1_write(fdp1, job->dst.addrs[0], FD1_WPF_ADDR_Y);
-	fdp1_write(fdp1, job->dst.addrs[1], FD1_WPF_ADDR_C0);
-	fdp1_write(fdp1, job->dst.addrs[2], FD1_WPF_ADDR_C1);
+	fdp1_write(fdp1, job->dst->addrs[0], FD1_WPF_ADDR_Y);
+	fdp1_write(fdp1, job->dst->addrs[1], FD1_WPF_ADDR_C0);
+	fdp1_write(fdp1, job->dst->addrs[2], FD1_WPF_ADDR_C1);
 }
 
 static void fdp1_configure_deint_mode(struct fdp1_ctx *ctx,
@@ -1196,80 +1176,15 @@ static void fdp1_m2m_job_abort(void *priv)
 }
 
 /*
- * prepare_buffer: Prepare an fdp1_buffer, from a vb2_v4l2_buffer
- *
- * This helps us serialise buffers containing two fields into
- * sequential top and bottom fields.
- * Destination buffers also go through this function to
- * set the vb and addrs in the same manner.
- */
-static void prepare_buffer(struct fdp1_ctx *ctx,
-			   struct fdp1_buffer *buf,
-			   struct vb2_v4l2_buffer *vb,
-			   bool next_field, bool last_field)
-{
-	struct fdp1_q_data *q_data = get_q_data(ctx, vb->vb2_buf.type);
-	unsigned int i;
-
-	buf->vb = vb;
-	buf->last_field = last_field;
-
-	for (i = 0; i < vb->vb2_buf.num_planes; ++i)
-		buf->addrs[i] = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, i);
-
-	switch (vb->field) {
-	case V4L2_FIELD_INTERLACED:
-		/*
-		 * Interlaced means bottom-top for 60Hz TV standards (NTSC) and
-		 * top-bottom for 50Hz. As TV standards are not applicable to
-		 * the mem-to-mem API, use the height as a heuristic.
-		 */
-		buf->field = (q_data->format.height < 576) == next_field
-			   ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
-		break;
-	case V4L2_FIELD_INTERLACED_TB:
-	case V4L2_FIELD_SEQ_TB:
-		buf->field = next_field ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
-		break;
-	case V4L2_FIELD_INTERLACED_BT:
-	case V4L2_FIELD_SEQ_BT:
-		buf->field = next_field ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
-		break;
-	default:
-		buf->field = vb->field;
-		break;
-	}
-
-	/* Buffer is completed */
-	if (next_field == false)
-		return;
-
-	/* Adjust buffer addresses for second field */
-	switch (vb->field) {
-	case V4L2_FIELD_INTERLACED:
-	case V4L2_FIELD_INTERLACED_TB:
-	case V4L2_FIELD_INTERLACED_BT:
-		for (i = 0; i < vb->vb2_buf.num_planes; i++)
-			buf->addrs[i] +=
-				(i == 0 ? q_data->stride_y : q_data->stride_c);
-		break;
-	case V4L2_FIELD_SEQ_TB:
-	case V4L2_FIELD_SEQ_BT:
-		for (i = 0; i < vb->vb2_buf.num_planes; i++)
-			buf->addrs[i] += q_data->vsize *
-				(i == 0 ? q_data->stride_y : q_data->stride_c);
-		break;
-	}
-}
-
-/*
- * prepare_job: Prepare and queue a new job for a single action of work
+ * fdp1_prepare_job: Prepare and queue a new job for a single action of work
  *
  * Prepare the next field, (or frame in progressive) and an output
  * buffer for the hardware to perform a single operation.
  */
-static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
+static struct fdp1_job *fdp1_prepare_job(struct fdp1_ctx *ctx)
 {
+	struct vb2_v4l2_buffer *vbuf;
+	struct fdp1_buffer *fbuf;
 	struct fdp1_dev *fdp1 = ctx->fdp1;
 	struct fdp1_job *job;
 	unsigned int buffers_required = 1;
@@ -1280,7 +1195,7 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 		buffers_required = 2;
 
 	if (ctx->buffers_queued < buffers_required)
-		return 0;
+		return NULL;
 
 	job = fdp1_job_alloc(fdp1);
 	if (!job) {
@@ -1288,7 +1203,7 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 		return NULL;
 	}
 
-	job->active = dequeue_buffer(ctx);
+	job->active = fdp1_dequeue_field(ctx);
 	if (!job->active) {
 		/* Buffer check should prevent this ever happening */
 		dprintk(fdp1, "No input buffers currently available\n");
@@ -1302,11 +1217,12 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 	/* Source buffers have been prepared on our buffer_queue
 	 * Prepare our Output buffer
 	 */
-	job->dst.vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
-	prepare_buffer(ctx, &job->dst, job->dst.vb, false, true);
+	vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+	fbuf = to_fdp1_buffer(vbuf);
+	job->dst = &fbuf->fields[0];
 
 	job->active->vb->sequence = ctx->sequence;
-	job->dst.vb->sequence = ctx->sequence;
+	job->dst->vb->sequence = ctx->sequence;
 	ctx->sequence++;
 
 	if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode)) {
@@ -1318,14 +1234,14 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 
 	if (FDP1_DEINT_MODE_USES_NEXT(ctx->deint_mode)) {
 		/* Must be called after 'active' is dequeued */
-		job->next = peek_queued_buffer(ctx);
+		job->next = fdp1_peek_queued_field(ctx);
 	}
 
 	/* Transfer timestamps and flags from src->dst */
 
-	job->dst.vb->vb2_buf.timestamp = job->active->vb->vb2_buf.timestamp;
+	job->dst->vb->vb2_buf.timestamp = job->active->vb->vb2_buf.timestamp;
 
-	job->dst.vb->flags = job->active->vb->flags &
+	job->dst->vb->flags = job->active->vb->flags &
 				V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
 
 	/* Ideally, the frame-end function will just 'check' to see
@@ -1351,9 +1267,8 @@ static void fdp1_m2m_device_run(void *priv)
 {
 	struct fdp1_ctx *ctx = priv;
 	struct fdp1_dev *fdp1 = ctx->fdp1;
-	struct fdp1_q_data *src_q_data = &ctx->out_q;
 	struct vb2_v4l2_buffer *src_vb;
-	int fields = V4L2_FIELD_HAS_BOTH(src_q_data->format.field) ? 2 : 1;
+	struct fdp1_buffer *buf;
 	unsigned int i;
 
 	dprintk(fdp1, "+\n");
@@ -1362,19 +1277,18 @@ static void fdp1_m2m_device_run(void *priv)
 
 	/* Get our incoming buffer of either one or two fields, or one frame */
 	src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	buf = to_fdp1_buffer(src_vb);
 
-	for (i = 0; i < fields; i++) {
-		struct fdp1_buffer *buf = fdp1_buffer_alloc(ctx);
-		bool last_field = (i+1 == fields);
+	for (i = 0; i < buf->num_fields; i++) {
+		struct fdp1_field_buffer *fbuf = &buf->fields[i];
 
-		prepare_buffer(ctx, buf, src_vb, i, last_field);
-		queue_buffer(ctx, buf);
+		fdp1_queue_field(ctx, fbuf);
 		dprintk(fdp1, "Queued Buffer [%d] last_field:%d\n",
-				i, last_field);
+				i, fbuf->last_field);
 	}
 
 	/* Queue as many jobs as our data provides for */
-	while (prepare_job(ctx))
+	while (fdp1_prepare_job(ctx))
 		;
 
 	if (ctx->translen == 0) {
@@ -1412,16 +1326,17 @@ static void device_frame_end(struct fdp1_dev *fdp1,
 	ctx->num_processed++;
 
 	/*
-	 * fdp1_buffer_free will call buf_done only when the last vb2_buffer
+	 * fdp1_field_complete will call buf_done only when the last vb2_buffer
 	 * reference is complete
 	 */
 	if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode))
-		fdp1_buffer_free(ctx, job->previous);
+		fdp1_field_complete(ctx, job->previous);
 	else
-		fdp1_buffer_free(ctx, job->active);
+		fdp1_field_complete(ctx, job->active);
 
 	spin_lock_irqsave(&fdp1->irqlock, flags);
-	v4l2_m2m_buf_done(job->dst.vb, state);
+	v4l2_m2m_buf_done(job->dst->vb, state);
+	job->dst = NULL;
 	spin_unlock_irqrestore(&fdp1->irqlock, flags);
 
 	/* Move this job back to the free job list */
@@ -1875,15 +1790,84 @@ static int fdp1_queue_setup(struct vb2_queue *vq,
 	return 0;
 }
 
+/*
+ * prepare_buffer: Prepare an fdp1_buffer, from a vb2_v4l2_buffer
+ *
+ * This helps us serialise buffers containing two fields into
+ * sequential top and bottom fields.
+ * Destination buffers also go through this function to
+ * set the vb and addrs in the same manner.
+ */
+static void fdp1_buf_prepare_field(struct fdp1_q_data *q_data,
+				   struct vb2_v4l2_buffer *vbuf,
+				   unsigned int field_num)
+{
+	struct fdp1_buffer *buf = to_fdp1_buffer(vbuf);
+	struct fdp1_field_buffer *fbuf = &buf->fields[field_num];
+	unsigned int num_fields;
+	unsigned int i;
+
+	num_fields = V4L2_FIELD_HAS_BOTH(vbuf->field) ? 2 : 1;
+
+	fbuf->vb = vbuf;
+	fbuf->last_field = (field_num + 1) == num_fields;
+
+	for (i = 0; i < vbuf->vb2_buf.num_planes; ++i)
+		fbuf->addrs[i] = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, i);
+
+	switch (vbuf->field) {
+	case V4L2_FIELD_INTERLACED:
+		/*
+		 * Interlaced means bottom-top for 60Hz TV standards (NTSC) and
+		 * top-bottom for 50Hz. As TV standards are not applicable to
+		 * the mem-to-mem API, use the height as a heuristic.
+		 */
+		fbuf->field = (q_data->format.height < 576) == field_num
+			    ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+		break;
+	case V4L2_FIELD_INTERLACED_TB:
+	case V4L2_FIELD_SEQ_TB:
+		fbuf->field = field_num ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
+		break;
+	case V4L2_FIELD_INTERLACED_BT:
+	case V4L2_FIELD_SEQ_BT:
+		fbuf->field = field_num ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+		break;
+	default:
+		fbuf->field = vbuf->field;
+		break;
+	}
+
+	/* Buffer is completed */
+	if (!field_num)
+		return;
+
+	/* Adjust buffer addresses for second field */
+	switch (vbuf->field) {
+	case V4L2_FIELD_INTERLACED:
+	case V4L2_FIELD_INTERLACED_TB:
+	case V4L2_FIELD_INTERLACED_BT:
+		for (i = 0; i < vbuf->vb2_buf.num_planes; i++)
+			fbuf->addrs[i] +=
+				(i == 0 ? q_data->stride_y : q_data->stride_c);
+		break;
+	case V4L2_FIELD_SEQ_TB:
+	case V4L2_FIELD_SEQ_BT:
+		for (i = 0; i < vbuf->vb2_buf.num_planes; i++)
+			fbuf->addrs[i] += q_data->vsize *
+				(i == 0 ? q_data->stride_y : q_data->stride_c);
+		break;
+	}
+}
+
 static int fdp1_buf_prepare(struct vb2_buffer *vb)
 {
-	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 	struct fdp1_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-	struct fdp1_q_data *q_data;
+	struct fdp1_q_data *q_data = get_q_data(ctx, vb->vb2_queue->type);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct fdp1_buffer *buf = to_fdp1_buffer(vbuf);
 	unsigned int i;
 
-	q_data = get_q_data(ctx, vb->vb2_queue->type);
-
 	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
 		bool field_valid = true;
 
@@ -1936,6 +1920,10 @@ static int fdp1_buf_prepare(struct vb2_buffer *vb)
 		vb2_set_plane_payload(vb, i, size);
 	}
 
+	buf->num_fields = V4L2_FIELD_HAS_BOTH(vbuf->field) ? 2 : 1;
+	for (i = 0; i < buf->num_fields; ++i)
+		fdp1_buf_prepare_field(q_data, vbuf, i);
+
 	return 0;
 }
 
@@ -2007,13 +1995,13 @@ static void fdp1_stop_streaming(struct vb2_queue *q)
 	/* Empty Output queues */
 	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
 		/* Empty our internal queues */
-		struct fdp1_buffer *b;
+		struct fdp1_field_buffer *fbuf;
 
 		/* Free any queued buffers */
-		b = dequeue_buffer(ctx);
-		while (b != NULL) {
-			fdp1_buffer_free(ctx, b);
-			b = dequeue_buffer(ctx);
+		fbuf = fdp1_dequeue_field(ctx);
+		while (fbuf != NULL) {
+			fdp1_field_complete(ctx, fbuf);
+			fbuf = fdp1_dequeue_field(ctx);
 		}
 
 		/* Free smsk_data */
@@ -2024,7 +2012,7 @@ static void fdp1_stop_streaming(struct vb2_queue *q)
 			ctx->smsk_cpu = NULL;
 		}
 
-		WARN(!list_empty(&ctx->fdp1_buffer_queue),
+		WARN(!list_empty(&ctx->fields_queue),
 				"Buffer queue not empty");
 	} else {
 		/* Empty Capture queues (Jobs) */
@@ -2033,17 +2021,18 @@ static void fdp1_stop_streaming(struct vb2_queue *q)
 		job = get_queued_job(ctx->fdp1);
 		while (job) {
 			if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode))
-				fdp1_buffer_free(ctx, job->previous);
+				fdp1_field_complete(ctx, job->previous);
 			else
-				fdp1_buffer_free(ctx, job->active);
+				fdp1_field_complete(ctx, job->active);
 
-			v4l2_m2m_buf_done(job->dst.vb, VB2_BUF_STATE_ERROR);
+			v4l2_m2m_buf_done(job->dst->vb, VB2_BUF_STATE_ERROR);
+			job->dst = NULL;
 
 			job = get_queued_job(ctx->fdp1);
 		}
 
 		/* Free any held buffer in the ctx */
-		fdp1_buffer_free(ctx, ctx->previous);
+		fdp1_field_complete(ctx, ctx->previous);
 
 		WARN(!list_empty(&ctx->fdp1->queued_job_list),
 				"Queued Job List not empty");
@@ -2072,7 +2061,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
 	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->buf_struct_size = sizeof(struct fdp1_buffer);
 	src_vq->ops = &fdp1_qops;
 	src_vq->mem_ops = &vb2_dma_contig_memops;
 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -2086,7 +2075,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 	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->buf_struct_size = sizeof(struct fdp1_buffer);
 	dst_vq->ops = &fdp1_qops;
 	dst_vq->mem_ops = &vb2_dma_contig_memops;
 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -2105,7 +2094,6 @@ static int fdp1_open(struct file *file)
 	struct v4l2_pix_format_mplane format;
 	struct fdp1_ctx *ctx = NULL;
 	struct v4l2_ctrl *ctrl;
-	unsigned int i;
 	int ret = 0;
 
 	if (mutex_lock_interruptible(&fdp1->dev_mutex))
@@ -2122,12 +2110,7 @@ static int fdp1_open(struct file *file)
 	ctx->fdp1 = fdp1;
 
 	/* Initialise Queues */
-	INIT_LIST_HEAD(&ctx->free_buffers);
-	INIT_LIST_HEAD(&ctx->fdp1_buffer_queue);
-
-	/* Initialise the buffers on the free list */
-	for (i = 0; i < ARRAY_SIZE(ctx->buffers); i++)
-		list_add(&ctx->buffers[i].list, &ctx->free_buffers);
+	INIT_LIST_HEAD(&ctx->fields_queue);
 
 	ctx->translen = 1;
 	ctx->sequence = 0;
-- 
Regards,

Laurent Pinchart


WARNING: multiple messages have this Message-ID (diff)
From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
To: linux-media@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org, Kieran Bingham <kieran@bingham.xyz>
Subject: [PATCH v3 10/10] v4l: fdp1: Store buffer information in vb2 buffer
Date: Thu,  8 Sep 2016 01:25:10 +0300	[thread overview]
Message-ID: <1473287110-780-11-git-send-email-laurent.pinchart+renesas@ideasonboard.com> (raw)
In-Reply-To: <1473287110-780-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com>

The struct fdp1_buffer instances are allocated separately from the vb2
buffers, with one instance per field. Simplify the allocation by
splitting the fdp1_buffer structure in per-buffer and per-field data,
and let vb2 allocate the the fdp1_buffer structure.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/rcar_fdp1.c | 437 ++++++++++++++++++-------------------
 1 file changed, 210 insertions(+), 227 deletions(-)

diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c
index c25531a919db..d4101a4fd114 100644
--- a/drivers/media/platform/rcar_fdp1.c
+++ b/drivers/media/platform/rcar_fdp1.c
@@ -473,12 +473,12 @@ static const u8 fdp1_mdet[] = {
 
 /* Per-queue, driver-specific private data */
 struct fdp1_q_data {
-	const struct fdp1_fmt	*fmt;
-	struct v4l2_pix_format_mplane format;
+	const struct fdp1_fmt		*fmt;
+	struct v4l2_pix_format_mplane	format;
 
-	unsigned int		vsize;
-	unsigned int		stride_y;
-	unsigned int		stride_c;
+	unsigned int			vsize;
+	unsigned int			stride_y;
+	unsigned int			stride_c;
 };
 
 static const struct fdp1_fmt *fdp1_find_format(u32 pixelformat)
@@ -519,91 +519,102 @@ enum fdp1_deint_mode {
  * from the VB buffers using this context structure.
  * Will always be a field or a full frame, never two fields.
  */
-struct fdp1_buffer {
-	struct vb2_v4l2_buffer	*vb;
-	dma_addr_t		addrs[3];
+struct fdp1_field_buffer {
+	struct vb2_v4l2_buffer		*vb;
+	dma_addr_t			addrs[3];
 
 	/* Should be NONE:TOP:BOTTOM only */
-	enum v4l2_field		field;
+	enum v4l2_field			field;
 
 	/* Flag to indicate this is the last field in the vb */
-	bool			last_field;
+	bool				last_field;
 
 	/* Buffer queue lists */
-	struct list_head	list;
+	struct list_head		list;
+};
+
+struct fdp1_buffer {
+	struct v4l2_m2m_buffer		m2m_buf;
+	struct fdp1_field_buffer	fields[2];
+	unsigned int			num_fields;
 };
 
+static inline struct fdp1_buffer *to_fdp1_buffer(struct vb2_v4l2_buffer *vb)
+{
+	return container_of(vb, struct fdp1_buffer, m2m_buf.vb);
+}
+
 struct fdp1_job {
 	/* These could be pointers to save 'memory' and copying */
-	struct fdp1_buffer	*previous;
-	struct fdp1_buffer	*active;
-	struct fdp1_buffer	*next;
-	struct fdp1_buffer	dst;
+	struct fdp1_field_buffer	*previous;
+	struct fdp1_field_buffer	*active;
+	struct fdp1_field_buffer	*next;
+	struct fdp1_field_buffer	*dst;
 
 	/* A job can only be on one list at a time */
-	struct list_head	list;
+	struct list_head		list;
 };
 
 struct fdp1_dev {
-	struct v4l2_device	v4l2_dev;
-	struct video_device	vfd;
+	struct v4l2_device		v4l2_dev;
+	struct video_device		vfd;
 
-	struct mutex		dev_mutex;
-	spinlock_t		irqlock;
-	spinlock_t		device_process_lock;
+	struct mutex			dev_mutex;
+	spinlock_t			irqlock;
+	spinlock_t			device_process_lock;
 
-	void __iomem		*regs;
-	unsigned int		irq;
-	struct device		*dev;
+	void __iomem			*regs;
+	unsigned int			irq;
+	struct device			*dev;
 
 	/* Job Queues */
-	struct fdp1_job		jobs[FDP1_NUMBER_JOBS];
-	struct list_head	free_job_list;
-	struct list_head	queued_job_list;
-	struct list_head	hw_job_list;
+	struct fdp1_job			jobs[FDP1_NUMBER_JOBS];
+	struct list_head		free_job_list;
+	struct list_head		queued_job_list;
+	struct list_head		hw_job_list;
 
-	unsigned int		clk_rate;
+	unsigned int			clk_rate;
 
-	struct rcar_fcp_device	*fcp;
-	struct v4l2_m2m_dev	*m2m_dev;
+	struct rcar_fcp_device		*fcp;
+	struct v4l2_m2m_dev		*m2m_dev;
 };
 
 struct fdp1_ctx {
-	struct v4l2_fh		fh;
-	struct fdp1_dev		*fdp1;
+	struct v4l2_fh			fh;
+	struct fdp1_dev			*fdp1;
 
-	struct v4l2_ctrl_handler hdl;
-	unsigned int		sequence;
+	struct v4l2_ctrl_handler	hdl;
+	unsigned int			sequence;
 
 	/* Processed buffers in this transaction */
-	u8			num_processed;
+	u8				num_processed;
 
 	/* Transaction length (i.e. how many buffers per transaction) */
-	u32			translen;
+	u32				translen;
 
 	/* Abort requested by m2m */
-	int			aborting;
+	int				aborting;
 
 	/* Deinterlace processing mode */
-	enum fdp1_deint_mode	deint_mode;
+	enum fdp1_deint_mode		deint_mode;
 
 	/*
 	 * Adaptive 2d 3d mode uses a shared mask
 	 * This is allocated at streamon, if the ADAPT2D3D mode
 	 * is requested
 	 */
-	unsigned int		smsk_size;
-	dma_addr_t		smsk_addr[2];
-	void			*smsk_cpu;
+	unsigned int			smsk_size;
+	dma_addr_t			smsk_addr[2];
+	void				*smsk_cpu;
 
 	/* Capture pipeline, can specify an alpha value
 	 * for supported formats. 0-255 only
 	 */
-	unsigned char		alpha;
+	unsigned char			alpha;
 
 	/* Source and destination queue data */
-	struct fdp1_q_data	out_q; /* HW Source */
-	struct fdp1_q_data	cap_q; /* HW Destination */
+	struct fdp1_q_data		out_q; /* HW Source */
+	struct fdp1_q_data		cap_q; /* HW Destination */
 
 	/*
 	 * Field Queues
@@ -613,16 +624,14 @@ struct fdp1_ctx {
 	 * V4L2 Buffers are tracked inside the fdp1_buffer
 	 * and released when the last 'field' completes
 	 */
-	struct fdp1_buffer	buffers[FDP1_NUMBER_BUFFERS];
-	struct list_head	free_buffers;
-	struct list_head	fdp1_buffer_queue;
-	unsigned int		buffers_queued;
+	struct list_head		fields_queue;
+	unsigned int			buffers_queued;
 
 	/*
 	 * For de-interlacing we need to track our previous buffer
 	 * while preparing our job lists.
 	 */
-	struct fdp1_buffer	*previous;
+	struct fdp1_field_buffer	*previous;
 };
 
 static inline struct fdp1_ctx *fh_to_ctx(struct v4l2_fh *fh)
@@ -711,90 +720,61 @@ static struct fdp1_job *get_hw_queued_job(struct fdp1_dev *fdp1)
 /*
  * Buffer lists handling
  */
-static struct fdp1_buffer *list_remove_buffer(struct fdp1_dev *fdp1,
-					       struct list_head *list)
+static void fdp1_field_complete(struct fdp1_ctx *ctx,
+				struct fdp1_field_buffer *fbuf)
 {
-	struct fdp1_buffer *buf;
-	unsigned long flags;
-
-	spin_lock_irqsave(&fdp1->irqlock, flags);
-	buf = list_first_entry_or_null(list, struct fdp1_buffer, list);
-	if (buf)
-		list_del(&buf->list);
-	spin_unlock_irqrestore(&fdp1->irqlock, flags);
+	/* job->previous may be on the first field */
+	if (!fbuf)
+		return;
 
-	return buf;
+	if (fbuf->last_field)
+		v4l2_m2m_buf_done(fbuf->vb, VB2_BUF_STATE_DONE);
 }
 
-/*
- * list_add_buffer: Add a buffer to the specified list
- *
- * Returns: void - always succeeds
- */
-static void list_add_buffer(struct fdp1_dev *fdp1,
-			    struct list_head *list,
-			    struct fdp1_buffer *buf)
+static void fdp1_queue_field(struct fdp1_ctx *ctx,
+			     struct fdp1_field_buffer *fbuf)
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&fdp1->irqlock, flags);
-	list_add_tail(&buf->list, list);
-	spin_unlock_irqrestore(&fdp1->irqlock, flags);
-}
+	spin_lock_irqsave(&ctx->fdp1->irqlock, flags);
+	list_add_tail(&fbuf->list, &ctx->fields_queue);
+	spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags);
 
-/*
- * fdp1_buffer_alloc: Retrieve a buffer to track a single field/frame
- *
- * Must always return a buffer, and should block if necessary.
- */
-static struct fdp1_buffer *fdp1_buffer_alloc(struct fdp1_ctx *ctx)
-{
-	return list_remove_buffer(ctx->fdp1, &ctx->free_buffers);
+	ctx->buffers_queued++;
 }
 
-static void fdp1_buffer_free(struct fdp1_ctx *ctx,
-			struct fdp1_buffer *buf)
+static struct fdp1_field_buffer *fdp1_dequeue_field(struct fdp1_ctx *ctx)
 {
-	/* job->previous may be on the first field */
-	if (!buf)
-		return;
-
-	if (buf->last_field)
-		v4l2_m2m_buf_done(buf->vb, VB2_BUF_STATE_DONE);
-
-	memset(buf, 0, sizeof(struct fdp1_buffer));
+	struct fdp1_field_buffer *fbuf;
+	unsigned long flags;
 
-	list_add_buffer(ctx->fdp1, &ctx->free_buffers, buf);
-}
+	ctx->buffers_queued--;
 
-static void queue_buffer(struct fdp1_ctx *ctx, struct fdp1_buffer *buf)
-{
-	list_add_buffer(ctx->fdp1, &ctx->fdp1_buffer_queue, buf);
-	ctx->buffers_queued++;
-}
+	spin_lock_irqsave(&ctx->fdp1->irqlock, flags);
+	fbuf = list_first_entry_or_null(&ctx->fields_queue,
+					struct fdp1_field_buffer, list);
+	if (fbuf)
+		list_del(&fbuf->list);
+	spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags);
 
-static struct fdp1_buffer *dequeue_buffer(struct fdp1_ctx *ctx)
-{
-	ctx->buffers_queued--;
-	return list_remove_buffer(ctx->fdp1, &ctx->fdp1_buffer_queue);
+	return fbuf;
 }
 
 /*
- * Return the next buffer in the queue - or NULL,
+ * Return the next field in the queue - or NULL,
  * without removing the item from the list
  */
-static struct fdp1_buffer *peek_queued_buffer(struct fdp1_ctx *ctx)
+static struct fdp1_field_buffer *fdp1_peek_queued_field(struct fdp1_ctx *ctx)
 {
-	struct fdp1_dev *fdp1 = ctx->fdp1;
+	struct fdp1_field_buffer *fbuf;
 	unsigned long flags;
-	struct fdp1_buffer *buf;
 
-	spin_lock_irqsave(&fdp1->irqlock, flags);
-	buf = list_first_entry_or_null(&ctx->fdp1_buffer_queue,
-			struct fdp1_buffer, list);
-	spin_unlock_irqrestore(&fdp1->irqlock, flags);
+	spin_lock_irqsave(&ctx->fdp1->irqlock, flags);
+	fbuf = list_first_entry_or_null(&ctx->fields_queue,
+					struct fdp1_field_buffer, list);
+	spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags);
 
-	return buf;
+	return fbuf;
 }
 
 static u32 fdp1_read(struct fdp1_dev *fdp1, unsigned int reg)
@@ -1015,9 +995,9 @@ static void fdp1_configure_wpf(struct fdp1_ctx *ctx,
 	fdp1_write(fdp1, swap, FD1_WPF_SWAP);
 	fdp1_write(fdp1, pstride, FD1_WPF_PSTRIDE);
 
-	fdp1_write(fdp1, job->dst.addrs[0], FD1_WPF_ADDR_Y);
-	fdp1_write(fdp1, job->dst.addrs[1], FD1_WPF_ADDR_C0);
-	fdp1_write(fdp1, job->dst.addrs[2], FD1_WPF_ADDR_C1);
+	fdp1_write(fdp1, job->dst->addrs[0], FD1_WPF_ADDR_Y);
+	fdp1_write(fdp1, job->dst->addrs[1], FD1_WPF_ADDR_C0);
+	fdp1_write(fdp1, job->dst->addrs[2], FD1_WPF_ADDR_C1);
 }
 
 static void fdp1_configure_deint_mode(struct fdp1_ctx *ctx,
@@ -1196,80 +1176,15 @@ static void fdp1_m2m_job_abort(void *priv)
 }
 
 /*
- * prepare_buffer: Prepare an fdp1_buffer, from a vb2_v4l2_buffer
- *
- * This helps us serialise buffers containing two fields into
- * sequential top and bottom fields.
- * Destination buffers also go through this function to
- * set the vb and addrs in the same manner.
- */
-static void prepare_buffer(struct fdp1_ctx *ctx,
-			   struct fdp1_buffer *buf,
-			   struct vb2_v4l2_buffer *vb,
-			   bool next_field, bool last_field)
-{
-	struct fdp1_q_data *q_data = get_q_data(ctx, vb->vb2_buf.type);
-	unsigned int i;
-
-	buf->vb = vb;
-	buf->last_field = last_field;
-
-	for (i = 0; i < vb->vb2_buf.num_planes; ++i)
-		buf->addrs[i] = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, i);
-
-	switch (vb->field) {
-	case V4L2_FIELD_INTERLACED:
-		/*
-		 * Interlaced means bottom-top for 60Hz TV standards (NTSC) and
-		 * top-bottom for 50Hz. As TV standards are not applicable to
-		 * the mem-to-mem API, use the height as a heuristic.
-		 */
-		buf->field = (q_data->format.height < 576) == next_field
-			   ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
-		break;
-	case V4L2_FIELD_INTERLACED_TB:
-	case V4L2_FIELD_SEQ_TB:
-		buf->field = next_field ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
-		break;
-	case V4L2_FIELD_INTERLACED_BT:
-	case V4L2_FIELD_SEQ_BT:
-		buf->field = next_field ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
-		break;
-	default:
-		buf->field = vb->field;
-		break;
-	}
-
-	/* Buffer is completed */
-	if (next_field == false)
-		return;
-
-	/* Adjust buffer addresses for second field */
-	switch (vb->field) {
-	case V4L2_FIELD_INTERLACED:
-	case V4L2_FIELD_INTERLACED_TB:
-	case V4L2_FIELD_INTERLACED_BT:
-		for (i = 0; i < vb->vb2_buf.num_planes; i++)
-			buf->addrs[i] +=
-				(i == 0 ? q_data->stride_y : q_data->stride_c);
-		break;
-	case V4L2_FIELD_SEQ_TB:
-	case V4L2_FIELD_SEQ_BT:
-		for (i = 0; i < vb->vb2_buf.num_planes; i++)
-			buf->addrs[i] += q_data->vsize *
-				(i == 0 ? q_data->stride_y : q_data->stride_c);
-		break;
-	}
-}
-
-/*
- * prepare_job: Prepare and queue a new job for a single action of work
+ * fdp1_prepare_job: Prepare and queue a new job for a single action of work
  *
  * Prepare the next field, (or frame in progressive) and an output
  * buffer for the hardware to perform a single operation.
  */
-static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
+static struct fdp1_job *fdp1_prepare_job(struct fdp1_ctx *ctx)
 {
+	struct vb2_v4l2_buffer *vbuf;
+	struct fdp1_buffer *fbuf;
 	struct fdp1_dev *fdp1 = ctx->fdp1;
 	struct fdp1_job *job;
 	unsigned int buffers_required = 1;
@@ -1280,7 +1195,7 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 		buffers_required = 2;
 
 	if (ctx->buffers_queued < buffers_required)
-		return 0;
+		return NULL;
 
 	job = fdp1_job_alloc(fdp1);
 	if (!job) {
@@ -1288,7 +1203,7 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 		return NULL;
 	}
 
-	job->active = dequeue_buffer(ctx);
+	job->active = fdp1_dequeue_field(ctx);
 	if (!job->active) {
 		/* Buffer check should prevent this ever happening */
 		dprintk(fdp1, "No input buffers currently available\n");
@@ -1302,11 +1217,12 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 	/* Source buffers have been prepared on our buffer_queue
 	 * Prepare our Output buffer
 	 */
-	job->dst.vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
-	prepare_buffer(ctx, &job->dst, job->dst.vb, false, true);
+	vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+	fbuf = to_fdp1_buffer(vbuf);
+	job->dst = &fbuf->fields[0];
 
 	job->active->vb->sequence = ctx->sequence;
-	job->dst.vb->sequence = ctx->sequence;
+	job->dst->vb->sequence = ctx->sequence;
 	ctx->sequence++;
 
 	if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode)) {
@@ -1318,14 +1234,14 @@ static struct fdp1_job *prepare_job(struct fdp1_ctx *ctx)
 
 	if (FDP1_DEINT_MODE_USES_NEXT(ctx->deint_mode)) {
 		/* Must be called after 'active' is dequeued */
-		job->next = peek_queued_buffer(ctx);
+		job->next = fdp1_peek_queued_field(ctx);
 	}
 
 	/* Transfer timestamps and flags from src->dst */
 
-	job->dst.vb->vb2_buf.timestamp = job->active->vb->vb2_buf.timestamp;
+	job->dst->vb->vb2_buf.timestamp = job->active->vb->vb2_buf.timestamp;
 
-	job->dst.vb->flags = job->active->vb->flags &
+	job->dst->vb->flags = job->active->vb->flags &
 				V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
 
 	/* Ideally, the frame-end function will just 'check' to see
@@ -1351,9 +1267,8 @@ static void fdp1_m2m_device_run(void *priv)
 {
 	struct fdp1_ctx *ctx = priv;
 	struct fdp1_dev *fdp1 = ctx->fdp1;
-	struct fdp1_q_data *src_q_data = &ctx->out_q;
 	struct vb2_v4l2_buffer *src_vb;
-	int fields = V4L2_FIELD_HAS_BOTH(src_q_data->format.field) ? 2 : 1;
+	struct fdp1_buffer *buf;
 	unsigned int i;
 
 	dprintk(fdp1, "+\n");
@@ -1362,19 +1277,18 @@ static void fdp1_m2m_device_run(void *priv)
 
 	/* Get our incoming buffer of either one or two fields, or one frame */
 	src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	buf = to_fdp1_buffer(src_vb);
 
-	for (i = 0; i < fields; i++) {
-		struct fdp1_buffer *buf = fdp1_buffer_alloc(ctx);
-		bool last_field = (i+1 == fields);
+	for (i = 0; i < buf->num_fields; i++) {
+		struct fdp1_field_buffer *fbuf = &buf->fields[i];
 
-		prepare_buffer(ctx, buf, src_vb, i, last_field);
-		queue_buffer(ctx, buf);
+		fdp1_queue_field(ctx, fbuf);
 		dprintk(fdp1, "Queued Buffer [%d] last_field:%d\n",
-				i, last_field);
+				i, fbuf->last_field);
 	}
 
 	/* Queue as many jobs as our data provides for */
-	while (prepare_job(ctx))
+	while (fdp1_prepare_job(ctx))
 		;
 
 	if (ctx->translen == 0) {
@@ -1412,16 +1326,17 @@ static void device_frame_end(struct fdp1_dev *fdp1,
 	ctx->num_processed++;
 
 	/*
-	 * fdp1_buffer_free will call buf_done only when the last vb2_buffer
+	 * fdp1_field_complete will call buf_done only when the last vb2_buffer
 	 * reference is complete
 	 */
 	if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode))
-		fdp1_buffer_free(ctx, job->previous);
+		fdp1_field_complete(ctx, job->previous);
 	else
-		fdp1_buffer_free(ctx, job->active);
+		fdp1_field_complete(ctx, job->active);
 
 	spin_lock_irqsave(&fdp1->irqlock, flags);
-	v4l2_m2m_buf_done(job->dst.vb, state);
+	v4l2_m2m_buf_done(job->dst->vb, state);
+	job->dst = NULL;
 	spin_unlock_irqrestore(&fdp1->irqlock, flags);
 
 	/* Move this job back to the free job list */
@@ -1875,15 +1790,84 @@ static int fdp1_queue_setup(struct vb2_queue *vq,
 	return 0;
 }
 
+/*
+ * prepare_buffer: Prepare an fdp1_buffer, from a vb2_v4l2_buffer
+ *
+ * This helps us serialise buffers containing two fields into
+ * sequential top and bottom fields.
+ * Destination buffers also go through this function to
+ * set the vb and addrs in the same manner.
+ */
+static void fdp1_buf_prepare_field(struct fdp1_q_data *q_data,
+				   struct vb2_v4l2_buffer *vbuf,
+				   unsigned int field_num)
+{
+	struct fdp1_buffer *buf = to_fdp1_buffer(vbuf);
+	struct fdp1_field_buffer *fbuf = &buf->fields[field_num];
+	unsigned int num_fields;
+	unsigned int i;
+
+	num_fields = V4L2_FIELD_HAS_BOTH(vbuf->field) ? 2 : 1;
+
+	fbuf->vb = vbuf;
+	fbuf->last_field = (field_num + 1) == num_fields;
+
+	for (i = 0; i < vbuf->vb2_buf.num_planes; ++i)
+		fbuf->addrs[i] = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, i);
+
+	switch (vbuf->field) {
+	case V4L2_FIELD_INTERLACED:
+		/*
+		 * Interlaced means bottom-top for 60Hz TV standards (NTSC) and
+		 * top-bottom for 50Hz. As TV standards are not applicable to
+		 * the mem-to-mem API, use the height as a heuristic.
+		 */
+		fbuf->field = (q_data->format.height < 576) == field_num
+			    ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+		break;
+	case V4L2_FIELD_INTERLACED_TB:
+	case V4L2_FIELD_SEQ_TB:
+		fbuf->field = field_num ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
+		break;
+	case V4L2_FIELD_INTERLACED_BT:
+	case V4L2_FIELD_SEQ_BT:
+		fbuf->field = field_num ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+		break;
+	default:
+		fbuf->field = vbuf->field;
+		break;
+	}
+
+	/* Buffer is completed */
+	if (!field_num)
+		return;
+
+	/* Adjust buffer addresses for second field */
+	switch (vbuf->field) {
+	case V4L2_FIELD_INTERLACED:
+	case V4L2_FIELD_INTERLACED_TB:
+	case V4L2_FIELD_INTERLACED_BT:
+		for (i = 0; i < vbuf->vb2_buf.num_planes; i++)
+			fbuf->addrs[i] +=
+				(i == 0 ? q_data->stride_y : q_data->stride_c);
+		break;
+	case V4L2_FIELD_SEQ_TB:
+	case V4L2_FIELD_SEQ_BT:
+		for (i = 0; i < vbuf->vb2_buf.num_planes; i++)
+			fbuf->addrs[i] += q_data->vsize *
+				(i == 0 ? q_data->stride_y : q_data->stride_c);
+		break;
+	}
+}
+
 static int fdp1_buf_prepare(struct vb2_buffer *vb)
 {
-	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 	struct fdp1_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-	struct fdp1_q_data *q_data;
+	struct fdp1_q_data *q_data = get_q_data(ctx, vb->vb2_queue->type);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct fdp1_buffer *buf = to_fdp1_buffer(vbuf);
 	unsigned int i;
 
-	q_data = get_q_data(ctx, vb->vb2_queue->type);
-
 	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
 		bool field_valid = true;
 
@@ -1936,6 +1920,10 @@ static int fdp1_buf_prepare(struct vb2_buffer *vb)
 		vb2_set_plane_payload(vb, i, size);
 	}
 
+	buf->num_fields = V4L2_FIELD_HAS_BOTH(vbuf->field) ? 2 : 1;
+	for (i = 0; i < buf->num_fields; ++i)
+		fdp1_buf_prepare_field(q_data, vbuf, i);
+
 	return 0;
 }
 
@@ -2007,13 +1995,13 @@ static void fdp1_stop_streaming(struct vb2_queue *q)
 	/* Empty Output queues */
 	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
 		/* Empty our internal queues */
-		struct fdp1_buffer *b;
+		struct fdp1_field_buffer *fbuf;
 
 		/* Free any queued buffers */
-		b = dequeue_buffer(ctx);
-		while (b != NULL) {
-			fdp1_buffer_free(ctx, b);
-			b = dequeue_buffer(ctx);
+		fbuf = fdp1_dequeue_field(ctx);
+		while (fbuf != NULL) {
+			fdp1_field_complete(ctx, fbuf);
+			fbuf = fdp1_dequeue_field(ctx);
 		}
 
 		/* Free smsk_data */
@@ -2024,7 +2012,7 @@ static void fdp1_stop_streaming(struct vb2_queue *q)
 			ctx->smsk_cpu = NULL;
 		}
 
-		WARN(!list_empty(&ctx->fdp1_buffer_queue),
+		WARN(!list_empty(&ctx->fields_queue),
 				"Buffer queue not empty");
 	} else {
 		/* Empty Capture queues (Jobs) */
@@ -2033,17 +2021,18 @@ static void fdp1_stop_streaming(struct vb2_queue *q)
 		job = get_queued_job(ctx->fdp1);
 		while (job) {
 			if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode))
-				fdp1_buffer_free(ctx, job->previous);
+				fdp1_field_complete(ctx, job->previous);
 			else
-				fdp1_buffer_free(ctx, job->active);
+				fdp1_field_complete(ctx, job->active);
 
-			v4l2_m2m_buf_done(job->dst.vb, VB2_BUF_STATE_ERROR);
+			v4l2_m2m_buf_done(job->dst->vb, VB2_BUF_STATE_ERROR);
+			job->dst = NULL;
 
 			job = get_queued_job(ctx->fdp1);
 		}
 
 		/* Free any held buffer in the ctx */
-		fdp1_buffer_free(ctx, ctx->previous);
+		fdp1_field_complete(ctx, ctx->previous);
 
 		WARN(!list_empty(&ctx->fdp1->queued_job_list),
 				"Queued Job List not empty");
@@ -2072,7 +2061,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
 	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->buf_struct_size = sizeof(struct fdp1_buffer);
 	src_vq->ops = &fdp1_qops;
 	src_vq->mem_ops = &vb2_dma_contig_memops;
 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -2086,7 +2075,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 	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->buf_struct_size = sizeof(struct fdp1_buffer);
 	dst_vq->ops = &fdp1_qops;
 	dst_vq->mem_ops = &vb2_dma_contig_memops;
 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -2105,7 +2094,6 @@ static int fdp1_open(struct file *file)
 	struct v4l2_pix_format_mplane format;
 	struct fdp1_ctx *ctx = NULL;
 	struct v4l2_ctrl *ctrl;
-	unsigned int i;
 	int ret = 0;
 
 	if (mutex_lock_interruptible(&fdp1->dev_mutex))
@@ -2122,12 +2110,7 @@ static int fdp1_open(struct file *file)
 	ctx->fdp1 = fdp1;
 
 	/* Initialise Queues */
-	INIT_LIST_HEAD(&ctx->free_buffers);
-	INIT_LIST_HEAD(&ctx->fdp1_buffer_queue);
-
-	/* Initialise the buffers on the free list */
-	for (i = 0; i < ARRAY_SIZE(ctx->buffers); i++)
-		list_add(&ctx->buffers[i].list, &ctx->free_buffers);
+	INIT_LIST_HEAD(&ctx->fields_queue);
 
 	ctx->translen = 1;
 	ctx->sequence = 0;
-- 
Regards,

Laurent Pinchart

  parent reply	other threads:[~2016-09-07 22:24 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-07 22:25 [PATCH v3 00/10] v4l: platform: Add Renesas R-Car FDP1 Driver Laurent Pinchart
2016-09-07 22:25 ` Laurent Pinchart
2016-09-07 22:25 ` [PATCH v3 01/10] v4l: ioctl: Clear the v4l2_pix_format_mplane reserved field Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-08  7:04   ` Sakari Ailus
2016-09-07 22:25 ` [PATCH v3 02/10] v4l: ctrls: Add deinterlacing mode control Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-08  9:04   ` Kieran Bingham
2016-09-08  9:04     ` Kieran Bingham
2016-10-21 14:34   ` Hans Verkuil
2016-10-24  9:07     ` Laurent Pinchart
2016-09-07 22:25 ` [PATCH v3 03/10] v4l: Extend FCP compatible list to support the FDP Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-07 22:25 ` [PATCH v3 04/10] v4l: Add Renesas R-Car FDP1 Driver Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-07 22:25 ` [PATCH v3 05/10] v4l: fdp1: vb2_queue dev conversion Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-08  9:05   ` Kieran Bingham
2016-09-08  9:05     ` Kieran Bingham
2016-09-07 22:25 ` [PATCH v3 06/10] v4l: fdp1: Incorporate miscellaneous review comments Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-11 16:35   ` Kieran Bingham
2016-09-11 16:35     ` Kieran Bingham
2016-09-07 22:25 ` [PATCH v3 07/10] v4l: fdp1: Remove unused struct fdp1_v4l2_buffer Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-09  7:29   ` Kieran Bingham
2016-09-09  7:29     ` Kieran Bingham
2016-09-07 22:25 ` [PATCH v3 08/10] v4l: fdp1: Rewrite format setting code Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-11 20:27   ` Kieran Bingham
2016-09-11 20:27     ` Kieran Bingham
2016-09-07 22:25 ` [PATCH v3 09/10] v4l: fdp1: Fix field validation when preparing buffer Laurent Pinchart
2016-09-07 22:25   ` Laurent Pinchart
2016-09-11 16:45   ` Kieran Bingham
2016-09-11 16:45     ` Kieran Bingham
2016-09-07 22:25 ` Laurent Pinchart [this message]
2016-09-07 22:25   ` [PATCH v3 10/10] v4l: fdp1: Store buffer information in vb2 buffer Laurent Pinchart
2016-09-11 20:20   ` Kieran Bingham
2016-09-11 20:20     ` Kieran Bingham
2016-09-08  9:10 ` [PATCH v3 00/10] v4l: platform: Add Renesas R-Car FDP1 Driver Kieran Bingham
2016-09-08  9:10   ` Kieran Bingham

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1473287110-780-11-git-send-email-laurent.pinchart+renesas@ideasonboard.com \
    --to=laurent.pinchart+renesas@ideasonboard.com \
    --cc=kieran@ksquared.org.uk \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.