V4L/DVB (6713): ivtv: ivtv_yuv_prep_frame breakup and yuv hardware buffer changes

ivtv_yuv_prep_frame is split in smaller code blocks.

Modified yuv buffer handling on the PVR350 itself. We now cycle through all 8
hardware buffers.

With this patch in place, driver behaviour should remain unchanged from the
existing release.

Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 48db22c..e3020f45 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -697,6 +697,7 @@
 	atomic_set(&itv->yuv_info.next_dma_frame, -1);
 	itv->yuv_info.lace_mode = ivtv_yuv_mode;
 	itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+	itv->yuv_info.max_frames_buffered = 3;
 	return 0;
 }
 
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index b6dd236..12ff938 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -403,6 +403,8 @@
 #define IVTV_YUV_SYNC_ODD		0x04
 #define IVTV_YUV_SYNC_MASK		0x04
 
+#define IVTV_YUV_BUFFERS 8
+
 struct yuv_playback_info
 {
 	u32 reg_2834;
@@ -475,11 +477,11 @@
 	u32 yuv_forced_update;
 	int update_frame;
 
-	int sync_field[4];  /* Field to sync on */
-	int field_delay[4]; /* Flag to extend duration of previous frame */
+	int sync_field[IVTV_YUV_BUFFERS];  /* Field to sync on */
+	int field_delay[IVTV_YUV_BUFFERS]; /* Flag to extend duration of previous frame */
 	u8 fields_lapsed;   /* Counter used when delaying a frame */
 
-	struct yuv_frame_info new_frame_info[4];
+	struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS];
 	struct yuv_frame_info old_frame_info;
 	struct yuv_frame_info old_frame_info_args;
 
@@ -487,6 +489,9 @@
 	dma_addr_t blanking_dmaptr;
 
 	int stream_size;
+
+	u8 draw_frame; /* PVR350 buffer to draw into */
+	u8 max_frames_buffered; /* Maximum number of frames to buffer */
 };
 
 #define IVTV_VBI_FRAMES 32
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index bcf1c85..fc8eac0 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -744,24 +744,25 @@
 	 * one vsync per frame.
 	 */
 	unsigned int frame = read_reg(0x28c0) & 1;
+	struct yuv_playback_info *yi = &itv->yuv_info;
 	int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
 
 	if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
 
-	if (((frame ^ itv->yuv_info.sync_field[last_dma_frame]) == 0 &&
-		((itv->last_vsync_field & 1) ^ itv->yuv_info.sync_field[last_dma_frame])) ||
-			(frame != (itv->last_vsync_field & 1) && !itv->yuv_info.frame_interlaced)) {
+	if (((frame ^ yi->sync_field[last_dma_frame]) == 0 &&
+		((itv->last_vsync_field & 1) ^ yi->sync_field[last_dma_frame])) ||
+			(frame != (itv->last_vsync_field & 1) && !yi->frame_interlaced)) {
 		int next_dma_frame = last_dma_frame;
 
-		if (!(itv->yuv_info.frame_interlaced && itv->yuv_info.field_delay[next_dma_frame] && itv->yuv_info.fields_lapsed < 1)) {
-			if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
+		if (!(yi->frame_interlaced && yi->field_delay[next_dma_frame] && yi->fields_lapsed < 1)) {
+			if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
 				write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
 				write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
 				write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
 				write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
-				next_dma_frame = (next_dma_frame + 1) & 0x3;
-				atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
-				itv->yuv_info.fields_lapsed = -1;
+				next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
+				atomic_set(&yi->next_dma_frame, next_dma_frame);
+				yi->fields_lapsed = -1;
 			}
 		}
 	}
@@ -794,20 +795,20 @@
 		}
 
 		/* Check if we need to update the yuv registers */
-		if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
-			if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
+		if ((yi->yuv_forced_update || yi->new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
+			if (!yi->new_frame_info[last_dma_frame].update)
 				last_dma_frame = (last_dma_frame - 1) & 3;
 
-			if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
-				itv->yuv_info.update_frame = last_dma_frame;
-				itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
-				itv->yuv_info.yuv_forced_update = 0;
+			if (yi->new_frame_info[last_dma_frame].src_w) {
+				yi->update_frame = last_dma_frame;
+				yi->new_frame_info[last_dma_frame].update = 0;
+				yi->yuv_forced_update = 0;
 				set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
 				set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
 			}
 		}
 
-		itv->yuv_info.fields_lapsed ++;
+		yi->fields_lapsed++;
 	}
 }
 
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
index 9091c48..15e9bd2 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -22,11 +22,16 @@
 #include "ivtv-udma.h"
 #include "ivtv-yuv.h"
 
-const u32 yuv_offset[4] = {
-	IVTV_YUV_BUFFER_OFFSET,
-	IVTV_YUV_BUFFER_OFFSET_1,
-	IVTV_YUV_BUFFER_OFFSET_2,
-	IVTV_YUV_BUFFER_OFFSET_3
+/* YUV buffer offsets */
+const u32 yuv_offset[IVTV_YUV_BUFFERS] = {
+	0x001a8600,
+	0x00240400,
+	0x002d8200,
+	0x00370000,
+	0x00029000,
+	0x000C0E00,
+	0x006B0400,
+	0x00748200
 };
 
 static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
@@ -37,10 +42,9 @@
 
 	int i;
 	int y_pages, uv_pages;
-
+	u8 frame = itv->yuv_info.draw_frame;
 	unsigned long y_buffer_offset, uv_buffer_offset;
 	int y_decode_height, uv_decode_height, y_size;
-	int frame = atomic_read(&itv->yuv_info.next_fill_frame);
 
 	y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame];
 	uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
@@ -954,76 +958,105 @@
 	atomic_set(&yi->next_dma_frame, 0);
 }
 
+/* Get next available yuv buffer on PVR350 */
+void ivtv_yuv_next_free(struct ivtv *itv)
+{
+	int draw, display;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+
+	if (atomic_read(&yi->next_dma_frame) == -1)
+		ivtv_yuv_init(itv);
+
+	draw = atomic_read(&yi->next_fill_frame);
+	display = atomic_read(&yi->next_dma_frame);
+
+	if (display > draw)
+		display -= IVTV_YUV_BUFFERS;
+
+	if (draw - display >= yi->max_frames_buffered)
+		draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS;
+	else
+		yi->new_frame_info[draw].update = 0;
+
+	yi->draw_frame = draw;
+}
+
+/* Set up frame according to ivtv_dma_frame parameters */
+void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	u8 frame = yi->draw_frame;
+
+	/* Preserve old update flag in case we're overwriting a queued frame */
+	int register_update = yi->new_frame_info[frame].update;
+
+	/* Take a snapshot of the yuv coordinate information */
+	yi->new_frame_info[frame].src_x = args->src.left;
+	yi->new_frame_info[frame].src_y = args->src.top;
+	yi->new_frame_info[frame].src_w = args->src.width;
+	yi->new_frame_info[frame].src_h = args->src.height;
+	yi->new_frame_info[frame].dst_x = args->dst.left;
+	yi->new_frame_info[frame].dst_y = args->dst.top;
+	yi->new_frame_info[frame].dst_w = args->dst.width;
+	yi->new_frame_info[frame].dst_h = args->dst.height;
+	yi->new_frame_info[frame].tru_x = args->dst.left;
+	yi->new_frame_info[frame].tru_w = args->src_width;
+	yi->new_frame_info[frame].tru_h = args->src_height;
+
+	/* Snapshot field order */
+	yi->sync_field[frame] = yi->lace_sync_field;
+
+	/* Are we going to offset the Y plane */
+	if (args->src.height + args->src.top < 512-16)
+		yi->new_frame_info[frame].offset_y = 1;
+	else
+		yi->new_frame_info[frame].offset_y = 0;
+
+	/* Snapshot the osd pan info */
+	yi->new_frame_info[frame].pan_x = yi->osd_x_pan;
+	yi->new_frame_info[frame].pan_y = yi->osd_y_pan;
+	yi->new_frame_info[frame].vis_w = yi->osd_vis_w;
+	yi->new_frame_info[frame].vis_h = yi->osd_vis_h;
+
+	yi->new_frame_info[frame].update = 0;
+	yi->new_frame_info[frame].interlaced_y = 0;
+	yi->new_frame_info[frame].interlaced_uv = 0;
+	yi->new_frame_info[frame].lace_mode = yi->lace_mode;
+
+	if (memcmp(&yi->old_frame_info_args, &yi->new_frame_info[frame],
+					sizeof(yi->new_frame_info[frame]))) {
+		yi->old_frame_info_args = yi->new_frame_info[frame];
+		yi->new_frame_info[frame].update = 1;
+/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
+	}
+
+	yi->new_frame_info[frame].update |= register_update;
+
+	/* Should this frame be delayed ? */
+	if (yi->sync_field[frame] !=
+				yi->sync_field[(frame - 1) % IVTV_YUV_BUFFERS])
+		yi->field_delay[frame] = 1;
+	else
+		yi->field_delay[frame] = 0;
+}
+
+/* Frame is complete & ready for display */
+void ivtv_yuv_frame_complete(struct ivtv *itv)
+{
+	atomic_set(&itv->yuv_info.next_fill_frame,
+			(itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS);
+}
+
 int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
 {
 	DEFINE_WAIT(wait);
 	int rc = 0;
 	int got_sig = 0;
-	int frame, next_fill_frame, last_fill_frame;
-	int register_update = 0;
 
 	IVTV_DEBUG_INFO("yuv_prep_frame\n");
 
-	if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv);
-
-	frame = atomic_read(&itv->yuv_info.next_fill_frame);
-	next_fill_frame = (frame + 1) & 0x3;
-	last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3;
-
-	if (next_fill_frame != last_fill_frame && last_fill_frame != frame) {
-		/* Buffers are full - Overwrite the last frame */
-		next_fill_frame = frame;
-		frame = (frame - 1) & 3;
-		register_update = itv->yuv_info.new_frame_info[frame].update;
-	}
-
-	/* Take a snapshot of the yuv coordinate information */
-	itv->yuv_info.new_frame_info[frame].src_x = args->src.left;
-	itv->yuv_info.new_frame_info[frame].src_y = args->src.top;
-	itv->yuv_info.new_frame_info[frame].src_w = args->src.width;
-	itv->yuv_info.new_frame_info[frame].src_h = args->src.height;
-	itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left;
-	itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top;
-	itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width;
-	itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height;
-	itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left;
-	itv->yuv_info.new_frame_info[frame].tru_w = args->src_width;
-	itv->yuv_info.new_frame_info[frame].tru_h = args->src_height;
-
-	/* Snapshot field order */
-	itv->yuv_info.sync_field[frame] = itv->yuv_info.lace_sync_field;
-
-	/* Are we going to offset the Y plane */
-	if (args->src.height + args->src.top < 512-16)
-		itv->yuv_info.new_frame_info[frame].offset_y = 1;
-	else
-		itv->yuv_info.new_frame_info[frame].offset_y = 0;
-
-	/* Snapshot the osd pan info */
-	itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan;
-	itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan;
-	itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w;
-	itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h;
-
-	itv->yuv_info.new_frame_info[frame].update = 0;
-	itv->yuv_info.new_frame_info[frame].interlaced_y = 0;
-	itv->yuv_info.new_frame_info[frame].interlaced_uv = 0;
-	itv->yuv_info.new_frame_info[frame].lace_mode = itv->yuv_info.lace_mode;
-
-	if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame],
-	    sizeof (itv->yuv_info.new_frame_info[frame]))) {
-		memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args));
-		itv->yuv_info.new_frame_info[frame].update = 1;
-/*		IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
-	}
-
-	itv->yuv_info.new_frame_info[frame].update |= register_update;
-
-	/* Should this frame be delayed ? */
-	if (itv->yuv_info.sync_field[frame] != itv->yuv_info.sync_field[(frame - 1) & 3])
-		itv->yuv_info.field_delay[frame] = 1;
-	else
-		itv->yuv_info.field_delay[frame] = 0;
+	ivtv_yuv_next_free(itv);
+	ivtv_yuv_setup_frame(itv, args);
 
 	/* DMA the frame */
 	mutex_lock(&itv->udma.lock);
@@ -1057,7 +1090,7 @@
 		return -EINTR;
 	}
 
-	atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame);
+	ivtv_yuv_frame_complete(itv);
 
 	mutex_unlock(&itv->udma.lock);
 	return rc;
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h
index 3b966f0..3b29092 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.h
+++ b/drivers/media/video/ivtv/ivtv-yuv.h
@@ -21,11 +21,6 @@
 #ifndef IVTV_YUV_H
 #define IVTV_YUV_H
 
-/* Buffers on hardware offsets */
-#define IVTV_YUV_BUFFER_OFFSET    0x001a8600	/* First YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_1  0x00240400	/* Second YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_2  0x002d8200	/* Third YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_3  0x00370000	/* Fourth YUV Buffer */
 #define IVTV_YUV_BUFFER_UV_OFFSET 0x65400	/* Offset to UV Buffer */
 
 /* Offset to filter table in firmware */
@@ -36,7 +31,7 @@
 #define IVTV_YUV_UPDATE_VERTICAL    0x02
 #define IVTV_YUV_UPDATE_INVALID     0x04
 
-extern const u32 yuv_offset[4];
+extern const u32 yuv_offset[IVTV_YUV_BUFFERS];
 
 int ivtv_yuv_filter_check(struct ivtv *itv);
 int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);