[PATCH] media: vivid: adding YU12, YV12, NV12, NV21 formats

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Following our previous discussions, attached is the patch adding
vertical + horizontal subsampled formats to vivid and applying to
those in the subject as defined in [1,2]. These formats are tightly
packed N planar, because they provide chroma(s) as a separate array,
but they are not mplanar yet, as suggested.

The modus operandi is to let tpg_fillbuffer() create a YUYV packed
format per pattern line as usual and apply downsampling if needed
immediately afterwards, in a new function called
tpg_apply_downsampling(). This one will unpack as needed, and average
the chroma samples (note that luma samples are never downsampled).
(Some provisions for horizontal downsampling are made, so I can follow
up with e.g. YUV410 etc formats, please understand in this context).

Writing the text information on top of the produced pattern also needs
a bit of retouch.

I'm not familiar at all with the review process here :) can anyone
point me to some info about it? Like, what happens now, how can we
iterate, how are comments accumulated, who approves or disapproves...?

FTR, the patches live in [2], directly against kernel sources. Is this
a good, bad idea?

[1] http://linuxtv.org/downloads/v4l-dvb-apis/re30.html
[2] http://linuxtv.org/downloads/v4l-dvb-apis/re24.html
[3] https://github.com/miguelao/linux/tree/adding_yu12_yv12_nv12_nv21_

M
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index 39a67cf..93c6ca3 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -669,8 +669,7 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
 	if (vid_cap_buf) {
 		/* Fill buffer */
 		vivid_fillbuff(dev, vid_cap_buf);
-		dprintk(dev, 1, "filled buffer %d\n",
-			vid_cap_buf->vb.v4l2_buf.index);
+		dprintk(dev, 1, "filled buffer %d\n", vid_cap_buf->vb.v4l2_buf.index);
 
 		/* Handle overlay */
 		if (dev->overlay_cap_owner && dev->fb_cap.base &&
@@ -679,8 +678,7 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
 
 		vb2_buffer_done(&vid_cap_buf->vb, dev->dqbuf_error ?
 				VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
-		dprintk(dev, 2, "vid_cap buffer %d done\n",
-				vid_cap_buf->vb.v4l2_buf.index);
+		dprintk(dev, 2, "vid_cap buffer %d done\n", vid_cap_buf->vb.v4l2_buf.index);
 	}
 
 	if (vbi_cap_buf) {
diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c
index fc9c653..56af289 100644
--- a/drivers/media/platform/vivid/vivid-tpg.c
+++ b/drivers/media/platform/vivid/vivid-tpg.c
@@ -193,6 +193,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
 	case V4L2_PIX_FMT_UYVY:
 	case V4L2_PIX_FMT_YVYU:
 	case V4L2_PIX_FMT_VYUY:
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
 		tpg->is_yuv = true;
 		break;
 	default:
@@ -224,12 +228,32 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
 	case V4L2_PIX_FMT_ABGR32:
 		tpg->twopixelsize[0] = 2 * 4;
 		break;
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		tpg->twopixelsize[0] = 3;
+		break;
 	case V4L2_PIX_FMT_NV16M:
 	case V4L2_PIX_FMT_NV61M:
 		tpg->twopixelsize[0] = 2;
 		tpg->twopixelsize[1] = 2;
 		break;
 	}
+
+	switch (fourcc) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		tpg->vertical_downsampling = 2;
+		tpg->horizontal_downsampling = 2;
+		break;
+	default:
+		tpg->vertical_downsampling = 0;
+		tpg->horizontal_downsampling = 0;
+	}
+
 	return true;
 }
 
@@ -271,6 +295,12 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
 	tpg->recalc_square_border = true;
 }
 
+/* Vertically downsampled pixel formats use YUYV as intermediate. */
+static unsigned tpg_get_packed_twopixsize(struct tpg_data *tpg, unsigned p)
+{
+	return tpg->vertical_downsampling ? 4: tpg->twopixelsize[p];
+}
+
 static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
 {
 	switch (tpg->pattern) {
@@ -671,7 +701,15 @@ static void gen_twopix(struct tpg_data *tpg,
 		buf[0][offset] = r_y;
 		buf[1][offset] = odd ? g_u : b_v;
 		break;
-
+	/*
+	 * For these cases we compose a YUYV macropixel. They will be verticallly
+	 * downsampled later on.
+	 */
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		offset = odd * tpg_get_packed_twopixsize(tpg, 0) / 2;
 	case V4L2_PIX_FMT_YUYV:
 		buf[0][offset] = r_y;
 		buf[0][offset + 1] = odd ? b_v : g_u;
@@ -998,9 +1036,8 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
 			gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
 			gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
 			for (p = 0; p < tpg->planes; p++) {
-				unsigned twopixsize = tpg->twopixelsize[p];
+				const unsigned twopixsize = tpg_get_packed_twopixsize(tpg, p);
 				u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2;
-
 				memcpy(pos, pix[p], twopixsize);
 			}
 		}
@@ -1011,7 +1048,7 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
 		gen_twopix(tpg, pix, contrast, 0);
 		gen_twopix(tpg, pix, contrast, 1);
 		for (p = 0; p < tpg->planes; p++) {
-			unsigned twopixsize = tpg->twopixelsize[p];
+			const unsigned twopixsize = tpg_get_packed_twopixsize(tpg, p);
 			u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
 
 			memcpy(pos, pix[p], twopixsize);
@@ -1023,9 +1060,8 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
 		gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
 		gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
 		for (p = 0; p < tpg->planes; p++) {
-			unsigned twopixsize = tpg->twopixelsize[p];
+			const unsigned twopixsize = tpg_get_packed_twopixsize(tpg, p);
 			u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
-
 			memcpy(pos, pix[p], twopixsize);
 		}
 	}
@@ -1035,7 +1071,7 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
 		gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
 		gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
 		for (p = 0; p < tpg->planes; p++) {
-			unsigned twopixsize = tpg->twopixelsize[p];
+			const unsigned twopixsize = tpg_get_packed_twopixsize(tpg, p);
 			u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
 
 			memcpy(pos, pix[p], twopixsize);
@@ -1081,8 +1117,8 @@ void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
 		div = 2;
 
 	for (p = 0; p < tpg->planes; p++) {
-		/* Print stream time */
-#define PRINTSTR(PIXTYPE) do {	\
+		/* Print text */
+#define PRINTSTR(PIXTYPE, dst_ptr, stride) do {	\
 	PIXTYPE fg;	\
 	PIXTYPE bg;	\
 	memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE));	\
@@ -1090,32 +1126,31 @@ void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
 	\
 	for (line = first; line < 16; line += step) {	\
 		int l = tpg->vflip ? 15 - line : line; \
-		PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
-			       ((y * step + l) / div) * tpg->bytesperline[p] + \
+		PIXTYPE *pos = (PIXTYPE *)((dst_ptr)[line & 1] +	\
+			       ((y * step + l) / div) * stride + \
 			       x * sizeof(PIXTYPE));	\
-		unsigned s;	\
 	\
+		unsigned s;	\
 		for (s = 0; s < len; s++) {	\
 			u8 chr = font8x16[text[s] * 16 + line];	\
-	\
 			if (tpg->hflip) { \
-				pos[7] = (chr & (0x01 << 7) ? fg : bg);	\
-				pos[6] = (chr & (0x01 << 6) ? fg : bg);	\
-				pos[5] = (chr & (0x01 << 5) ? fg : bg);	\
-				pos[4] = (chr & (0x01 << 4) ? fg : bg);	\
-				pos[3] = (chr & (0x01 << 3) ? fg : bg);	\
-				pos[2] = (chr & (0x01 << 2) ? fg : bg);	\
-				pos[1] = (chr & (0x01 << 1) ? fg : bg);	\
-				pos[0] = (chr & (0x01 << 0) ? fg : bg);	\
+				pos[7] = (chr & (0x01 << 7) ? fg : bg); \
+				pos[6] = (chr & (0x01 << 6) ? fg : bg); \
+				pos[5] = (chr & (0x01 << 5) ? fg : bg); \
+				pos[4] = (chr & (0x01 << 4) ? fg : bg); \
+				pos[3] = (chr & (0x01 << 3) ? fg : bg); \
+				pos[2] = (chr & (0x01 << 2) ? fg : bg); \
+				pos[1] = (chr & (0x01 << 1) ? fg : bg); \
+				pos[0] = (chr & (0x01 << 0) ? fg : bg); \
 			} else { \
-				pos[0] = (chr & (0x01 << 7) ? fg : bg);	\
-				pos[1] = (chr & (0x01 << 6) ? fg : bg);	\
-				pos[2] = (chr & (0x01 << 5) ? fg : bg);	\
-				pos[3] = (chr & (0x01 << 4) ? fg : bg);	\
-				pos[4] = (chr & (0x01 << 3) ? fg : bg);	\
-				pos[5] = (chr & (0x01 << 2) ? fg : bg);	\
-				pos[6] = (chr & (0x01 << 1) ? fg : bg);	\
-				pos[7] = (chr & (0x01 << 0) ? fg : bg);	\
+				pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+				pos[1] = (chr & (0x01 << 6) ? fg : bg); \
+				pos[2] = (chr & (0x01 << 5) ? fg : bg); \
+				pos[3] = (chr & (0x01 << 4) ? fg : bg); \
+				pos[4] = (chr & (0x01 << 3) ? fg : bg); \
+				pos[5] = (chr & (0x01 << 2) ? fg : bg); \
+				pos[6] = (chr & (0x01 << 1) ? fg : bg); \
+				pos[7] = (chr & (0x01 << 0) ? fg : bg); \
 			} \
 	\
 			pos += tpg->hflip ? -8 : 8;	\
@@ -1123,15 +1158,25 @@ void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
 	}	\
 } while (0)
 
-		switch (tpg->twopixelsize[p]) {
-		case 2:
-			PRINTSTR(u8); break;
-		case 4:
-			PRINTSTR(u16); break;
-		case 6:
-			PRINTSTR(x24); break;
-		case 8:
-			PRINTSTR(u32); break;
+		if (!tpg->vertical_downsampling) {
+			switch (tpg->twopixelsize[p]) {
+			case 2:
+				PRINTSTR(u8, basep[p], tpg->bytesperline[p]); break;
+			case 4:
+				PRINTSTR(u16, basep[p], tpg->bytesperline[p]); break;
+			case 6:
+				PRINTSTR(x24, basep[p], tpg->bytesperline[p]); break;
+			case 8:
+				PRINTSTR(u32, basep[p], tpg->bytesperline[p]); break;
+			}
+		} else {
+			/* tpg->twopixelsize[p] is 3 for the defined formats so far. */
+			if (tpg->twopixelsize[p] != 3) {
+				printk(KERN_WARNING "Unsupported twopixelsize");
+				return;
+			}
+			PRINTSTR(u8, basep[p], tpg->compose.width);
+			// TODO(mcasas): figure out what to do with Chroma planes.
 		}
 	}
 }
@@ -1549,4 +1594,114 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
 				(hact ^ vact ^ f);
 		}
 	}
+
+  tpg_apply_downsampling(tpg, std, p, vbuf);
+}
+
+/*
+ * Apply downsampling(s). Assumes that pattern lines have been generated in
+ * packed YUYV and written in tpg->lines: we unpack them in the appropriate
+ * plane locations, which depend on the concrete format and its planarity
+ * and chroma packaging.
+ */
+void tpg_apply_downsampling(struct tpg_data *tpg, v4l2_std_id std, unsigned p,
+		u8 *vbuf)
+{
+	u8* y_ptr = vbuf;
+	u8* chromas_start = y_ptr + (tpg->compose.height * tpg->compose.width);
+	u8* u_ptr;
+	u8* v_ptr;
+	u8 u_ptr_step = 1;
+	u8 v_ptr_step = 1;
+	int y;
+
+	if (tpg->vertical_downsampling == 0)
+		return;
+	if (tpg->vertical_downsampling > 2) {
+		printk(KERN_WARNING "Vertical downsampling by > 2 not implemented\n");
+		return;
+	}
+	if (tpg->horizontal_downsampling > 2) {
+		printk(KERN_WARNING "Horizontal downsampling by > 2 not implemented\n");
+		return;
+	}
+	if (tpg->planes > 1) {
+		printk(KERN_WARNING "Mplane vertically downsampling not implemented\n");
+		return;
+	}
+
+	switch (tpg->fourcc) {
+	case V4L2_PIX_FMT_YUV420:
+		u_ptr = chromas_start;
+		v_ptr = chromas_start +
+			(tpg->compose.height * tpg->compose.width) /
+			(2 * tpg->horizontal_downsampling);
+		break;
+	case V4L2_PIX_FMT_YVU420:
+		v_ptr = chromas_start;
+		u_ptr = chromas_start +
+			(tpg->compose.height * tpg->compose.width) /
+			(2 * tpg->horizontal_downsampling);
+		break;
+	case V4L2_PIX_FMT_NV12:  /* N{21,21} are special: interleaved chromas. */
+		u_ptr = chromas_start;
+		v_ptr = u_ptr + 1;
+		u_ptr_step = v_ptr_step = 2;
+		break;
+	case V4L2_PIX_FMT_NV21:
+		v_ptr = chromas_start;
+		u_ptr = v_ptr + 1;
+		u_ptr_step = v_ptr_step = 2;
+		break;
+	default:
+		printk(KERN_ERR "Unknown vertically downsampled format\n");
+		return;
+	}
+
+	/*
+	 * Per line: fetch the pattern line to use in packed YUYV, then unpack in
+	 * the dst buffers. Vertical downsampling can only be 2 or 4 here: average
+	 * as-many-rows of Chroma components. Since we use YUYV as starting point,
+	 * horizontal downsampling of 2 is a natural match; 4 would need hoops.
+	 */
+	for (y = 0; y < tpg->compose.height; y += 2) {
+		u8* src_ptr;
+		unsigned buf_line;
+		unsigned downsampling_index = 0;
+		for(; downsampling_index < tpg->vertical_downsampling;
+			++downsampling_index) {
+			int x;
+
+			/* Fetch the pattern line to use in packed YUYV. */
+			buf_line = tpg_calc_buffer_line(tpg, y + downsampling_index,
+				tpg->field);
+			src_ptr = tpg->lines[ tpg_get_pat_line(tpg, buf_line) ][0];
+			if ((downsampling_index % tpg->vertical_downsampling) == 0) {
+				/* Unpack yuyv macropixel. _Set_ the Chroma planes. */
+				for (x = 0; x < 2 * tpg->compose.width; x += 4) {
+					*y_ptr++ = *src_ptr++;
+					*u_ptr = *src_ptr++ / tpg->vertical_downsampling;
+					u_ptr += u_ptr_step;
+					*y_ptr++ = *src_ptr++;
+					*v_ptr = *src_ptr++ / tpg->vertical_downsampling;
+					v_ptr += v_ptr_step;
+				}
+			} else {
+				/* Rewind U, V pointers. */
+				u_ptr -= (u_ptr_step * tpg->compose.width /
+						tpg->horizontal_downsampling);
+				v_ptr -= (v_ptr_step * tpg->compose.width /
+						tpg->horizontal_downsampling);
+				/* Unpack yuyv macropixel. _Accumulate_ the Chroma planes. */
+				for (x = 0; x < 2 * tpg->compose.width; x += 4) {
+					*y_ptr++ = *src_ptr++;
+					*u_ptr += *src_ptr++ / tpg->vertical_downsampling;
+					u_ptr += u_ptr_step;
+					*y_ptr++ = *src_ptr++;
+					*v_ptr += *src_ptr++ / tpg->vertical_downsampling;
+					v_ptr += v_ptr_step;
+				}
+			}
+		}
+	}
 }
diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h
index 9dc463a4..3dec2ca 100644
--- a/drivers/media/platform/vivid/vivid-tpg.h
+++ b/drivers/media/platform/vivid/vivid-tpg.h
@@ -142,6 +142,14 @@ struct tpg_data {
 	/* size in bytes for two pixels in each plane */
 	unsigned			twopixelsize[TPG_MAX_PLANES];
 	unsigned			bytesperline[TPG_MAX_PLANES];
+	/*
+	 * Vertical and horizontal downsample factors. Only applies to YUV formats,
+	 * concretely Chroma components (not the Luma), and implies that the format
+	 * is planar (2 or 3 planes). Only even numbers are allowed. Typical values
+	 * include 0 for no downsampling and 2 (like in 4:2:0 formats, e.g. YUV420).
+	 */
+	unsigned      vertical_downsampling;
+	unsigned      horizontal_downsampling;
 
 	/* Configuration */
 	enum tpg_pattern		pattern;
@@ -166,7 +174,10 @@ struct tpg_data {
 	bool				recalc_lines;
 	bool				recalc_square_border;
 
-	/* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */
+	/*
+	 *  Used to store TPG_MAX_PAT_LINES lines, each with up to TPG_MAX_PLANES
+	 *  planes
+	 */
 	unsigned			max_line_width;
 	u8				*lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
 	u8				*random_line[TPG_MAX_PLANES];
@@ -186,6 +197,8 @@ void tpg_gen_text(struct tpg_data *tpg,
 void tpg_calc_text_basep(struct tpg_data *tpg,
 		u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf);
 void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf);
+void tpg_apply_downsampling(struct tpg_data *tpg, v4l2_std_id std, unsigned p,
+		u8 *vbuf);
 bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc);
 void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
 		const struct v4l2_rect *compose);
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index 6bef1e6..99e0d22 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -73,6 +73,34 @@ struct vivid_fmt vivid_formats[] = {
 		.planes   = 1,
 	},
 	{
+		.name     = "YUV 4:2:0, planar, 1 Chroma plane",
+		.fourcc   = V4L2_PIX_FMT_NV12,
+		.depth    = 12,
+		.is_yuv   = true,
+		.planes   = 1,
+	},
+	{
+		.name     = "YVU 4:2:0, planar, 1 Chroma plane",
+		.fourcc   = V4L2_PIX_FMT_NV21,
+		.depth    = 12,
+		.is_yuv   = true,
+		.planes   = 1,
+	},
+	{
+		.name     = "YUV 4:2:0, planar, 2 Chroma planes",
+		.fourcc   = V4L2_PIX_FMT_YUV420,
+		.depth    = 12,
+		.is_yuv   = true,
+		.planes   = 1,
+	},
+	{
+		.name     = "YVU 4:2:0, planar, 2 Chroma planes",
+		.fourcc   = V4L2_PIX_FMT_YVU420,
+		.depth    = 12,
+		.is_yuv   = true,
+		.planes   = 1,
+	},
+	{
 		.name     = "RGB565 (LE)",
 		.fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
 		.depth    = 16,

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux