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,