This is an automatic generated email to let you know that the following patch 
were queued at the 
http://git.linuxtv.org/cgit.cgi/v4l-utils.git tree:

Subject: v4l-utils: sync with kernel
Author:  Hans Verkuil <[email protected]>
Date:    Fri Apr 3 10:48:15 2015 +0200

Update to the latest kernel sources, specifically vivid-tpg.

This adds support for 4:2:0 test patterns.

Signed-off-by: Hans Verkuil <[email protected]>

 utils/qv4l2/qv4l2.cpp                 |    5 +-
 utils/qv4l2/qv4l2.h                   |    1 +
 utils/v4l2-ctl/v4l2-ctl-streaming.cpp |    6 +-
 utils/v4l2-ctl/vivid-tpg.c            | 1082 ++++++++++++++++++++++++---------
 utils/v4l2-ctl/vivid-tpg.h            |  112 ++++-
 5 files changed, 924 insertions(+), 282 deletions(-)

---

http://git.linuxtv.org/cgit.cgi/v4l-utils.git/commit/?id=cef10270b1d5d89b6de2e4c6a4b0f746c9f97c01

diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index b3ffdcc..df29a8c 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -508,7 +508,7 @@ bool ApplicationWindow::startStreaming()
 
                                m_queue.buffer_init(buf, i);
                                buf.s_field(m_tpgField);
-                               tpg_s_field(&m_tpg, m_tpgField);
+                               tpg_s_field(&m_tpg, m_tpgField, m_tpgFieldAlt);
                                if (m_tpgField == V4L2_FIELD_TOP)
                                        m_tpgField = V4L2_FIELD_BOTTOM;
                                else if (m_tpgField == V4L2_FIELD_BOTTOM)
@@ -775,7 +775,7 @@ void ApplicationWindow::outFrame()
                }
                m_queue.buffer_init(buf, buf.g_index());
                buf.s_field(m_tpgField);
-               tpg_s_field(&m_tpg, m_tpgField);
+               tpg_s_field(&m_tpg, m_tpgField, m_tpgFieldAlt);
                if (m_tpgField == V4L2_FIELD_TOP)
                        m_tpgField = V4L2_FIELD_BOTTOM;
                else if (m_tpgField == V4L2_FIELD_BOTTOM)
@@ -1141,6 +1141,7 @@ void ApplicationWindow::outStart(bool start)
                else
                        m_tpgStd = 0;
                m_tpgField = fmt.g_first_field(m_tpgStd);
+               m_tpgFieldAlt = fmt.g_field() == V4L2_FIELD_ALTERNATE;
                m_tpgSizeImage = fmt.g_sizeimage(0);
                tpg_alloc(&m_tpg, fmt.g_width());
                m_useTpg = tpg_s_fourcc(&m_tpg, fmt.g_pixelformat());
diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
index 0ae358c..23e87fe 100644
--- a/utils/qv4l2/qv4l2.h
+++ b/utils/qv4l2/qv4l2.h
@@ -234,6 +234,7 @@ private:
        struct tpg_data m_tpg;
        v4l2_std_id m_tpgStd;
        unsigned m_tpgField;
+       bool m_tpgFieldAlt;
        unsigned m_tpgSizeImage;
        unsigned m_tpgColorspace;
        unsigned m_tpgYCbCrEnc;
diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp 
b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
index 2625c84..ec6163f 100644
--- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
@@ -45,6 +45,7 @@ static char *file_cap;
 static char *file_out;
 static struct tpg_data tpg;
 static unsigned output_field = V4L2_FIELD_NONE;
+static bool output_field_alt;
 
 static void *test_mmap(void *start, size_t length, int prot, int flags,
                int fd, int64_t offset)
@@ -623,6 +624,7 @@ static int do_setup_out_buffers(int fd, buffers &b, FILE 
*fin, bool qbuf)
                field = fmt.fmt.pix.field;
 
        output_field = field;
+       output_field_alt = field == V4L2_FIELD_ALTERNATE;
        if (V4L2_FIELD_HAS_T_OR_B(field)) {
                factor = 2;
                output_field = (stream_out_std & V4L2_STD_525_60) ?
@@ -695,7 +697,7 @@ static int do_setup_out_buffers(int fd, buffers &b, FILE 
*fin, bool qbuf)
                        return -1;
 
                buf.field = field;
-               tpg_s_field(&tpg, field);
+               tpg_s_field(&tpg, field, output_field_alt);
                if (field == V4L2_FIELD_TOP)
                        field = V4L2_FIELD_BOTTOM;
                else if (field == V4L2_FIELD_BOTTOM)
@@ -961,7 +963,7 @@ static int do_handle_out(int fd, buffers &b, FILE *fin, 
struct v4l2_buffer *cap,
                return -1;
        }
        buf.field = output_field;
-       tpg_s_field(&tpg, output_field);
+       tpg_s_field(&tpg, output_field, output_field_alt);
        if (output_field == V4L2_FIELD_TOP)
                output_field = V4L2_FIELD_BOTTOM;
        else if (output_field == V4L2_FIELD_BOTTOM)
diff --git a/utils/v4l2-ctl/vivid-tpg.c b/utils/v4l2-ctl/vivid-tpg.c
index 34493f4..cb766eb 100644
--- a/utils/v4l2-ctl/vivid-tpg.c
+++ b/utils/v4l2-ctl/vivid-tpg.c
@@ -35,7 +35,10 @@ const char * const tpg_pattern_strings[] = {
        "100% Green",
        "100% Blue",
        "16x16 Checkers",
+       "2x2 Checkers",
        "1x1 Checkers",
+       "2x2 Red/Green Checkers",
+       "1x1 Red/Green Checkers",
        "Alternating Hor Lines",
        "Alternating Vert Lines",
        "One Pixel Wide Cross",
@@ -120,15 +123,20 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
        tpg->max_line_width = max_w;
        for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) {
                for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
-                       unsigned pixelsz = plane ? 1 : 4;
+                       unsigned pixelsz = plane ? 2 : 4;
 
                        tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
                        if (!tpg->lines[pat][plane])
                                return -ENOMEM;
+                       if (plane == 0)
+                               continue;
+                       tpg->downsampled_lines[pat][plane] = vzalloc(max_w * 2 
* pixelsz);
+                       if (!tpg->downsampled_lines[pat][plane])
+                               return -ENOMEM;
                }
        }
        for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
-               unsigned pixelsz = plane ? 1 : 4;
+               unsigned pixelsz = plane ? 2 : 4;
 
                tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
                if (!tpg->contrast_line[plane])
@@ -152,6 +160,10 @@ void tpg_free(struct tpg_data *tpg)
                for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
                        vfree(tpg->lines[pat][plane]);
                        tpg->lines[pat][plane] = NULL;
+                       if (plane == 0)
+                               continue;
+                       vfree(tpg->downsampled_lines[pat][plane]);
+                       tpg->downsampled_lines[pat][plane] = NULL;
                }
        for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
                vfree(tpg->contrast_line[plane]);
@@ -167,14 +179,38 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
 {
        tpg->fourcc = fourcc;
        tpg->planes = 1;
+       tpg->buffers = 1;
        tpg->recalc_colors = true;
+       tpg->interleaved = false;
+       tpg->vdownsampling[0] = 1;
+       tpg->hdownsampling[0] = 1;
+       tpg->hmask[0] = ~0;
+       tpg->hmask[1] = ~0;
+       tpg->hmask[2] = ~0;
+
        switch (fourcc) {
+       case V4L2_PIX_FMT_SBGGR8:
+       case V4L2_PIX_FMT_SGBRG8:
+       case V4L2_PIX_FMT_SGRBG8:
+       case V4L2_PIX_FMT_SRGGB8:
+               tpg->interleaved = true;
+               tpg->vdownsampling[1] = 1;
+               tpg->hdownsampling[1] = 1;
+               tpg->planes = 2;
+               /* fall through */
+       case V4L2_PIX_FMT_RGB332:
        case V4L2_PIX_FMT_RGB565:
        case V4L2_PIX_FMT_RGB565X:
+       case V4L2_PIX_FMT_RGB444:
+       case V4L2_PIX_FMT_XRGB444:
+       case V4L2_PIX_FMT_ARGB444:
        case V4L2_PIX_FMT_RGB555:
        case V4L2_PIX_FMT_XRGB555:
        case V4L2_PIX_FMT_ARGB555:
        case V4L2_PIX_FMT_RGB555X:
+       case V4L2_PIX_FMT_XRGB555X:
+       case V4L2_PIX_FMT_ARGB555X:
+       case V4L2_PIX_FMT_BGR666:
        case V4L2_PIX_FMT_RGB24:
        case V4L2_PIX_FMT_BGR24:
        case V4L2_PIX_FMT_RGB32:
@@ -183,16 +219,72 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        case V4L2_PIX_FMT_XBGR32:
        case V4L2_PIX_FMT_ARGB32:
        case V4L2_PIX_FMT_ABGR32:
+       case V4L2_PIX_FMT_GREY:
                tpg->is_yuv = false;
                break;
+       case V4L2_PIX_FMT_YUV444:
+       case V4L2_PIX_FMT_YUV555:
+       case V4L2_PIX_FMT_YUV565:
+       case V4L2_PIX_FMT_YUV32:
+               tpg->is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YVU420M:
+               tpg->buffers = 3;
+               /* fall through */
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+               tpg->vdownsampling[1] = 2;
+               tpg->vdownsampling[2] = 2;
+               tpg->hdownsampling[1] = 2;
+               tpg->hdownsampling[2] = 2;
+               tpg->planes = 3;
+               tpg->is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_YUV422P:
+               tpg->vdownsampling[1] = 1;
+               tpg->vdownsampling[2] = 1;
+               tpg->hdownsampling[1] = 2;
+               tpg->hdownsampling[2] = 2;
+               tpg->planes = 3;
+               tpg->is_yuv = true;
+               break;
        case V4L2_PIX_FMT_NV16M:
        case V4L2_PIX_FMT_NV61M:
+               tpg->buffers = 2;
+               /* fall through */
+       case V4L2_PIX_FMT_NV16:
+       case V4L2_PIX_FMT_NV61:
+               tpg->vdownsampling[1] = 1;
+               tpg->hdownsampling[1] = 1;
+               tpg->hmask[1] = ~1;
                tpg->planes = 2;
-               /* fall-through */
+               tpg->is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV21M:
+               tpg->buffers = 2;
+               /* fall through */
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV21:
+               tpg->vdownsampling[1] = 2;
+               tpg->hdownsampling[1] = 1;
+               tpg->hmask[1] = ~1;
+               tpg->planes = 2;
+               tpg->is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_NV24:
+       case V4L2_PIX_FMT_NV42:
+               tpg->vdownsampling[1] = 1;
+               tpg->hdownsampling[1] = 1;
+               tpg->planes = 2;
+               tpg->is_yuv = true;
+               break;
        case V4L2_PIX_FMT_YUYV:
        case V4L2_PIX_FMT_UYVY:
        case V4L2_PIX_FMT_YVYU:
        case V4L2_PIX_FMT_VYUY:
+               tpg->hmask[0] = ~1;
                tpg->is_yuv = true;
                break;
        default:
@@ -200,35 +292,75 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        }
 
        switch (fourcc) {
+       case V4L2_PIX_FMT_RGB332:
+               tpg->twopixelsize[0] = 2;
+               break;
        case V4L2_PIX_FMT_RGB565:
        case V4L2_PIX_FMT_RGB565X:
+       case V4L2_PIX_FMT_RGB444:
+       case V4L2_PIX_FMT_XRGB444:
+       case V4L2_PIX_FMT_ARGB444:
        case V4L2_PIX_FMT_RGB555:
        case V4L2_PIX_FMT_XRGB555:
        case V4L2_PIX_FMT_ARGB555:
        case V4L2_PIX_FMT_RGB555X:
+       case V4L2_PIX_FMT_XRGB555X:
+       case V4L2_PIX_FMT_ARGB555X:
        case V4L2_PIX_FMT_YUYV:
        case V4L2_PIX_FMT_UYVY:
        case V4L2_PIX_FMT_YVYU:
        case V4L2_PIX_FMT_VYUY:
+       case V4L2_PIX_FMT_YUV444:
+       case V4L2_PIX_FMT_YUV555:
+       case V4L2_PIX_FMT_YUV565:
                tpg->twopixelsize[0] = 2 * 2;
                break;
        case V4L2_PIX_FMT_RGB24:
        case V4L2_PIX_FMT_BGR24:
                tpg->twopixelsize[0] = 2 * 3;
                break;
+       case V4L2_PIX_FMT_BGR666:
        case V4L2_PIX_FMT_RGB32:
        case V4L2_PIX_FMT_BGR32:
        case V4L2_PIX_FMT_XRGB32:
        case V4L2_PIX_FMT_XBGR32:
        case V4L2_PIX_FMT_ARGB32:
        case V4L2_PIX_FMT_ABGR32:
+       case V4L2_PIX_FMT_YUV32:
                tpg->twopixelsize[0] = 2 * 4;
                break;
+       case V4L2_PIX_FMT_GREY:
+               tpg->twopixelsize[0] = 2;
+               break;
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV21:
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV21M:
+       case V4L2_PIX_FMT_NV16:
+       case V4L2_PIX_FMT_NV61:
        case V4L2_PIX_FMT_NV16M:
        case V4L2_PIX_FMT_NV61M:
+       case V4L2_PIX_FMT_SBGGR8:
+       case V4L2_PIX_FMT_SGBRG8:
+       case V4L2_PIX_FMT_SGRBG8:
+       case V4L2_PIX_FMT_SRGGB8:
                tpg->twopixelsize[0] = 2;
                tpg->twopixelsize[1] = 2;
                break;
+       case V4L2_PIX_FMT_YUV422P:
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YVU420M:
+               tpg->twopixelsize[0] = 2;
+               tpg->twopixelsize[1] = 2;
+               tpg->twopixelsize[2] = 2;
+               break;
+       case V4L2_PIX_FMT_NV24:
+       case V4L2_PIX_FMT_NV42:
+               tpg->twopixelsize[0] = 2;
+               tpg->twopixelsize[1] = 4;
+               break;
        }
        return true;
 }
@@ -267,7 +399,8 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned width, 
unsigned height,
        tpg->compose.width = width;
        tpg->compose.height = tpg->buf_height;
        for (p = 0; p < tpg->planes; p++)
-               tpg->bytesperline[p] = width * tpg->twopixelsize[p] / 2;
+               tpg->bytesperline[p] = (width * tpg->twopixelsize[p]) /
+                                      (2 * tpg->hdownsampling[p]);
        tpg->recalc_square_border = true;
 }
 
@@ -347,9 +480,9 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int 
g, int b,
                { COEFF(0.5, 224),    COEFF(-0.445, 224), COEFF(-0.055, 224) },
        };
        static const int bt2020[3][3] = {
-               { COEFF(0.2726, 219),  COEFF(0.6780, 219),  COEFF(0.0593, 219)  
},
+               { COEFF(0.2627, 219),  COEFF(0.6780, 219),  COEFF(0.0593, 219)  
},
                { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224)     
},
-               { COEFF(0.5, 224),     COEFF(-0.4629, 224), COEFF(-0.0405, 224) 
},
+               { COEFF(0.5, 224),     COEFF(-0.4598, 224), COEFF(-0.0402, 224) 
},
        };
        bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
        unsigned y_offset = full ? 0 : 16;
@@ -524,10 +657,10 @@ static void precalculate_color(struct tpg_data *tpg, int 
k)
                g <<= 4;
                b <<= 4;
        }
-       if (tpg->qual == TPG_QUAL_GRAY) {
+       if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY) {
                /* Rec. 709 Luma function */
                /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
-               r = g = b = ((13879 * r + 46688 * g + 4713 * b) >> 16) + (16 << 
4);
+               r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16;
        }
 
        /*
@@ -601,9 +734,29 @@ static void precalculate_color(struct tpg_data *tpg, int k)
                        cb = clamp(cb, 16 << 4, 240 << 4);
                        cr = clamp(cr, 16 << 4, 240 << 4);
                }
-               tpg->colors[k][0] = clamp(y >> 4, 1, 254);
-               tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
-               tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
+               y = clamp(y >> 4, 1, 254);
+               cb = clamp(cb >> 4, 1, 254);
+               cr = clamp(cr >> 4, 1, 254);
+               switch (tpg->fourcc) {
+               case V4L2_PIX_FMT_YUV444:
+                       y >>= 4;
+                       cb >>= 4;
+                       cr >>= 4;
+                       break;
+               case V4L2_PIX_FMT_YUV555:
+                       y >>= 3;
+                       cb >>= 3;
+                       cr >>= 3;
+                       break;
+               case V4L2_PIX_FMT_YUV565:
+                       y >>= 3;
+                       cb >>= 2;
+                       cr >>= 3;
+                       break;
+               }
+               tpg->colors[k][0] = y;
+               tpg->colors[k][1] = cb;
+               tpg->colors[k][2] = cr;
        } else {
                if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
                        r = (r * 219) / 255 + (16 << 4);
@@ -611,20 +764,39 @@ static void precalculate_color(struct tpg_data *tpg, int 
k)
                        b = (b * 219) / 255 + (16 << 4);
                }
                switch (tpg->fourcc) {
+               case V4L2_PIX_FMT_RGB332:
+                       r >>= 9;
+                       g >>= 9;
+                       b >>= 10;
+                       break;
                case V4L2_PIX_FMT_RGB565:
                case V4L2_PIX_FMT_RGB565X:
                        r >>= 7;
                        g >>= 6;
                        b >>= 7;
                        break;
+               case V4L2_PIX_FMT_RGB444:
+               case V4L2_PIX_FMT_XRGB444:
+               case V4L2_PIX_FMT_ARGB444:
+                       r >>= 8;
+                       g >>= 8;
+                       b >>= 8;
+                       break;
                case V4L2_PIX_FMT_RGB555:
                case V4L2_PIX_FMT_XRGB555:
                case V4L2_PIX_FMT_ARGB555:
                case V4L2_PIX_FMT_RGB555X:
+               case V4L2_PIX_FMT_XRGB555X:
+               case V4L2_PIX_FMT_ARGB555X:
                        r >>= 7;
                        g >>= 7;
                        b >>= 7;
                        break;
+               case V4L2_PIX_FMT_BGR666:
+                       r >>= 6;
+                       g >>= 6;
+                       b >>= 6;
+                       break;
                default:
                        r >>= 4;
                        g >>= 4;
@@ -665,31 +837,120 @@ static void gen_twopix(struct tpg_data *tpg,
        b_v = tpg->colors[color][2]; /* B or precalculated V */
 
        switch (tpg->fourcc) {
+       case V4L2_PIX_FMT_GREY:
+               buf[0][offset] = r_y;
+               break;
+       case V4L2_PIX_FMT_YUV422P:
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YUV420M:
+               buf[0][offset] = r_y;
+               if (odd) {
+                       buf[1][0] = (buf[1][0] + g_u) / 2;
+                       buf[2][0] = (buf[2][0] + b_v) / 2;
+                       buf[1][1] = buf[1][0];
+                       buf[2][1] = buf[2][0];
+                       break;
+               }
+               buf[1][0] = g_u;
+               buf[2][0] = b_v;
+               break;
+       case V4L2_PIX_FMT_YVU420:
+       case V4L2_PIX_FMT_YVU420M:
+               buf[0][offset] = r_y;
+               if (odd) {
+                       buf[1][0] = (buf[1][0] + b_v) / 2;
+                       buf[2][0] = (buf[2][0] + g_u) / 2;
+                       buf[1][1] = buf[1][0];
+                       buf[2][1] = buf[2][0];
+                       break;
+               }
+               buf[1][0] = b_v;
+               buf[2][0] = g_u;
+               break;
+
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV16:
        case V4L2_PIX_FMT_NV16M:
                buf[0][offset] = r_y;
-               buf[1][offset] = odd ? b_v : g_u;
+               if (odd) {
+                       buf[1][0] = (buf[1][0] + g_u) / 2;
+                       buf[1][1] = (buf[1][1] + b_v) / 2;
+                       break;
+               }
+               buf[1][0] = g_u;
+               buf[1][1] = b_v;
                break;
+       case V4L2_PIX_FMT_NV21:
+       case V4L2_PIX_FMT_NV21M:
+       case V4L2_PIX_FMT_NV61:
        case V4L2_PIX_FMT_NV61M:
                buf[0][offset] = r_y;
-               buf[1][offset] = odd ? g_u : b_v;
+               if (odd) {
+                       buf[1][0] = (buf[1][0] + b_v) / 2;
+                       buf[1][1] = (buf[1][1] + g_u) / 2;
+                       break;
+               }
+               buf[1][0] = b_v;
+               buf[1][1] = g_u;
+               break;
+
+       case V4L2_PIX_FMT_NV24:
+               buf[0][offset] = r_y;
+               buf[1][2 * offset] = g_u;
+               buf[1][2 * offset + 1] = b_v;
+               break;
+
+       case V4L2_PIX_FMT_NV42:
+               buf[0][offset] = r_y;
+               buf[1][2 * offset] = b_v;
+               buf[1][2 * offset + 1] = g_u;
                break;
 
        case V4L2_PIX_FMT_YUYV:
                buf[0][offset] = r_y;
-               buf[0][offset + 1] = odd ? b_v : g_u;
+               if (odd) {
+                       buf[0][1] = (buf[0][1] + g_u) / 2;
+                       buf[0][3] = (buf[0][3] + b_v) / 2;
+                       break;
+               }
+               buf[0][1] = g_u;
+               buf[0][3] = b_v;
                break;
        case V4L2_PIX_FMT_UYVY:
-               buf[0][offset] = odd ? b_v : g_u;
                buf[0][offset + 1] = r_y;
+               if (odd) {
+                       buf[0][0] = (buf[0][0] + g_u) / 2;
+                       buf[0][2] = (buf[0][2] + b_v) / 2;
+                       break;
+               }
+               buf[0][0] = g_u;
+               buf[0][2] = b_v;
                break;
        case V4L2_PIX_FMT_YVYU:
                buf[0][offset] = r_y;
-               buf[0][offset + 1] = odd ? g_u : b_v;
+               if (odd) {
+                       buf[0][1] = (buf[0][1] + b_v) / 2;
+                       buf[0][3] = (buf[0][3] + g_u) / 2;
+                       break;
+               }
+               buf[0][1] = b_v;
+               buf[0][3] = g_u;
                break;
        case V4L2_PIX_FMT_VYUY:
-               buf[0][offset] = odd ? g_u : b_v;
                buf[0][offset + 1] = r_y;
+               if (odd) {
+                       buf[0][0] = (buf[0][0] + b_v) / 2;
+                       buf[0][2] = (buf[0][2] + g_u) / 2;
+                       break;
+               }
+               buf[0][0] = b_v;
+               buf[0][2] = g_u;
+               break;
+       case V4L2_PIX_FMT_RGB332:
+               buf[0][offset] = (r_y << 5) | (g_u << 2) | b_v;
                break;
+       case V4L2_PIX_FMT_YUV565:
        case V4L2_PIX_FMT_RGB565:
                buf[0][offset] = (g_u << 5) | b_v;
                buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
@@ -698,15 +959,29 @@ static void gen_twopix(struct tpg_data *tpg,
                buf[0][offset] = (r_y << 3) | (g_u >> 3);
                buf[0][offset + 1] = (g_u << 5) | b_v;
                break;
+       case V4L2_PIX_FMT_RGB444:
+       case V4L2_PIX_FMT_XRGB444:
+               alpha = 0;
+               /* fall through */
+       case V4L2_PIX_FMT_YUV444:
+       case V4L2_PIX_FMT_ARGB444:
+               buf[0][offset] = (g_u << 4) | b_v;
+               buf[0][offset + 1] = (alpha & 0xf0) | r_y;
+               break;
        case V4L2_PIX_FMT_RGB555:
        case V4L2_PIX_FMT_XRGB555:
                alpha = 0;
                /* fall through */
+       case V4L2_PIX_FMT_YUV555:
        case V4L2_PIX_FMT_ARGB555:
                buf[0][offset] = (g_u << 5) | b_v;
                buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
                break;
        case V4L2_PIX_FMT_RGB555X:
+       case V4L2_PIX_FMT_XRGB555X:
+               alpha = 0;
+               /* fall through */
+       case V4L2_PIX_FMT_ARGB555X:
                buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
                buf[0][offset + 1] = (g_u << 5) | b_v;
                break;
@@ -720,10 +995,17 @@ static void gen_twopix(struct tpg_data *tpg,
                buf[0][offset + 1] = g_u;
                buf[0][offset + 2] = r_y;
                break;
+       case V4L2_PIX_FMT_BGR666:
+               buf[0][offset] = (b_v << 2) | (g_u >> 4);
+               buf[0][offset + 1] = (g_u << 4) | (r_y >> 2);
+               buf[0][offset + 2] = r_y << 6;
+               buf[0][offset + 3] = 0;
+               break;
        case V4L2_PIX_FMT_RGB32:
        case V4L2_PIX_FMT_XRGB32:
                alpha = 0;
                /* fall through */
+       case V4L2_PIX_FMT_YUV32:
        case V4L2_PIX_FMT_ARGB32:
                buf[0][offset] = alpha;
                buf[0][offset + 1] = r_y;
@@ -740,15 +1022,47 @@ static void gen_twopix(struct tpg_data *tpg,
                buf[0][offset + 2] = r_y;
                buf[0][offset + 3] = alpha;
                break;
+       case V4L2_PIX_FMT_SBGGR8:
+               buf[0][offset] = odd ? g_u : b_v;
+               buf[1][offset] = odd ? r_y : g_u;
+               break;
+       case V4L2_PIX_FMT_SGBRG8:
+               buf[0][offset] = odd ? b_v : g_u;
+               buf[1][offset] = odd ? g_u : r_y;
+               break;
+       case V4L2_PIX_FMT_SGRBG8:
+               buf[0][offset] = odd ? r_y : g_u;
+               buf[1][offset] = odd ? g_u : b_v;
+               break;
+       case V4L2_PIX_FMT_SRGGB8:
+               buf[0][offset] = odd ? g_u : r_y;
+               buf[1][offset] = odd ? b_v : g_u;
+               break;
+       }
+}
+
+unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line)
+{
+       switch (tpg->fourcc) {
+       case V4L2_PIX_FMT_SBGGR8:
+       case V4L2_PIX_FMT_SGBRG8:
+       case V4L2_PIX_FMT_SGRBG8:
+       case V4L2_PIX_FMT_SRGGB8:
+               return buf_line & 1;
+       default:
+               return 0;
        }
 }
 
 /* Return how many pattern lines are used by the current pattern. */
-static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
+static unsigned tpg_get_pat_lines(const struct tpg_data *tpg)
 {
        switch (tpg->pattern) {
        case TPG_PAT_CHECKERS_16X16:
+       case TPG_PAT_CHECKERS_2X2:
        case TPG_PAT_CHECKERS_1X1:
+       case TPG_PAT_COLOR_CHECKERS_2X2:
+       case TPG_PAT_COLOR_CHECKERS_1X1:
        case TPG_PAT_ALTERNATING_HLINES:
        case TPG_PAT_CROSS_1_PIXEL:
        case TPG_PAT_CROSS_2_PIXELS:
@@ -763,14 +1077,18 @@ static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
 }
 
 /* Which pattern line should be used for the given frame line. */
-static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line)
+static unsigned tpg_get_pat_line(const struct tpg_data *tpg, unsigned line)
 {
        switch (tpg->pattern) {
        case TPG_PAT_CHECKERS_16X16:
                return (line >> 4) & 1;
        case TPG_PAT_CHECKERS_1X1:
+       case TPG_PAT_COLOR_CHECKERS_1X1:
        case TPG_PAT_ALTERNATING_HLINES:
                return line & 1;
+       case TPG_PAT_CHECKERS_2X2:
+       case TPG_PAT_COLOR_CHECKERS_2X2:
+               return (line & 2) >> 1;
        case TPG_PAT_100_COLORSQUARES:
        case TPG_PAT_100_HCOLORBAR:
                return (line * 8) / tpg->src_height;
@@ -789,7 +1107,8 @@ static unsigned tpg_get_pat_line(struct tpg_data *tpg, 
unsigned line)
  * Which color should be used for the given pattern line and X coordinate.
  * Note: x is in the range 0 to 2 * tpg->src_width.
  */
-static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, 
unsigned x)
+static enum tpg_color tpg_get_color(const struct tpg_data *tpg,
+                                   unsigned pat_line, unsigned x)
 {
        /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input 
print code
           should be modified */
@@ -836,6 +1155,15 @@ static enum tpg_color tpg_get_color(struct tpg_data *tpg, 
unsigned pat_line, uns
        case TPG_PAT_CHECKERS_1X1:
                return ((x & 1) ^ (pat_line & 1)) ?
                        TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+       case TPG_PAT_COLOR_CHECKERS_1X1:
+               return ((x & 1) ^ (pat_line & 1)) ?
+                       TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
+       case TPG_PAT_CHECKERS_2X2:
+               return (((x >> 1) & 1) ^ (pat_line & 1)) ?
+                       TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+       case TPG_PAT_COLOR_CHECKERS_2X2:
+               return (((x >> 1) & 1) ^ (pat_line & 1)) ?
+                       TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
        case TPG_PAT_ALTERNATING_HLINES:
                return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
        case TPG_PAT_ALTERNATING_VLINES:
@@ -948,6 +1276,7 @@ static void tpg_calculate_square_border(struct tpg_data 
*tpg)
 static void tpg_precalculate_line(struct tpg_data *tpg)
 {
        enum tpg_color contrast;
+       u8 pix[TPG_MAX_PLANES][8];
        unsigned pat;
        unsigned p;
        unsigned x;
@@ -974,7 +1303,6 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
                for (x = 0; x < tpg->scaled_width * 2; x += 2) {
                        unsigned real_x = src_x;
                        enum tpg_color color1, color2;
-                       u8 pix[TPG_MAX_PLANES][8];
 
                        real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : 
real_x;
                        color1 = tpg_get_color(tpg, pat, real_x);
@@ -1001,39 +1329,53 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
                        gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
                        for (p = 0; p < tpg->planes; p++) {
                                unsigned twopixsize = tpg->twopixelsize[p];
-                               u8 *pos = tpg->lines[pat][p] + x * twopixsize / 
2;
+                               unsigned hdiv = tpg->hdownsampling[p];
+                               u8 *pos = tpg->lines[pat][p] + tpg_hdiv(tpg, p, 
x);
 
-                               memcpy(pos, pix[p], twopixsize);
+                               memcpy(pos, pix[p], twopixsize / hdiv);
                        }
                }
        }
-       for (x = 0; x < tpg->scaled_width; x += 2) {
-               u8 pix[TPG_MAX_PLANES][8];
 
-               gen_twopix(tpg, pix, contrast, 0);
-               gen_twopix(tpg, pix, contrast, 1);
-               for (p = 0; p < tpg->planes; p++) {
-                       unsigned twopixsize = tpg->twopixelsize[p];
-                       u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
+       if (tpg->vdownsampling[tpg->planes - 1] > 1) {
+               unsigned pat_lines = tpg_get_pat_lines(tpg);
 
-                       memcpy(pos, pix[p], twopixsize);
+               for (pat = 0; pat < pat_lines; pat++) {
+                       unsigned next_pat = (pat + 1) % pat_lines;
+
+                       for (p = 1; p < tpg->planes; p++) {
+                               unsigned w = tpg_hdiv(tpg, p, tpg->scaled_width 
* 2);
+                               u8 *pos1 = tpg->lines[pat][p];
+                               u8 *pos2 = tpg->lines[next_pat][p];
+                               u8 *dest = tpg->downsampled_lines[pat][p];
+
+                               for (x = 0; x < w; x++, pos1++, pos2++, dest++)
+                                       *dest = ((u16)*pos1 + (u16)*pos2) / 2;
+                       }
                }
        }
-       for (x = 0; x < tpg->scaled_width; x += 2) {
-               u8 pix[TPG_MAX_PLANES][8];
 
-               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];
-                       u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
+       gen_twopix(tpg, pix, contrast, 0);
+       gen_twopix(tpg, pix, contrast, 1);
+       for (p = 0; p < tpg->planes; p++) {
+               unsigned twopixsize = tpg->twopixelsize[p];
+               u8 *pos = tpg->contrast_line[p];
 
+               for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize)
+                       memcpy(pos, pix[p], twopixsize);
+       }
+
+       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];
+               u8 *pos = tpg->black_line[p];
+
+               for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize)
                        memcpy(pos, pix[p], twopixsize);
-               }
        }
-       for (x = 0; x < tpg->scaled_width * 2; x += 2) {
-               u8 pix[TPG_MAX_PLANES][8];
 
+       for (x = 0; x < tpg->scaled_width * 2; x += 2) {
                gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
                gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
                for (p = 0; p < tpg->planes; p++) {
@@ -1043,6 +1385,7 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
                        memcpy(pos, pix[p], twopixsize);
                }
        }
+
        gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
        gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
        gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
@@ -1052,8 +1395,8 @@ static void tpg_precalculate_line(struct tpg_data *tpg)
 /* need this to do rgb24 rendering */
 typedef struct { u16 __; u8 _; } __packed x24;
 
-void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
-               int y, int x, char *text)
+void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+                 int y, int x, char *text)
 {
        int line;
        unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
@@ -1083,24 +1426,37 @@ 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 */
+               unsigned vdiv = tpg->vdownsampling[p];
+               unsigned hdiv = tpg->hdownsampling[p];
+
+               /* Print text */
 #define PRINTSTR(PIXTYPE) do { \
        PIXTYPE fg;     \
        PIXTYPE bg;     \
        memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE));   \
        memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE));   \
        \
-       for (line = first; line < 16; line += step) {   \
+       for (line = first; line < 16; line += vdiv * step) {    \
                int l = tpg->vflip ? 15 - line : line; \
-               PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
-                              ((y * step + l) / div) * tpg->bytesperline[p] + \
-                              x * sizeof(PIXTYPE));    \
+               PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \
+                              ((y * step + l) / (vdiv * div)) * 
tpg->bytesperline[p] + \
+                              (x / hdiv) * sizeof(PIXTYPE));   \
                unsigned s;     \
        \
                for (s = 0; s < len; s++) {     \
                        u8 chr = font8x16[text[s] * 16 + line]; \
        \
-                       if (tpg->hflip) { \
+                       if (hdiv == 2 && tpg->hflip) { \
+                               pos[3] = (chr & (0x01 << 6) ? fg : bg); \
+                               pos[2] = (chr & (0x01 << 4) ? fg : bg); \
+                               pos[1] = (chr & (0x01 << 2) ? fg : bg); \
+                               pos[0] = (chr & (0x01 << 0) ? fg : bg); \
+                       } else if (hdiv == 2) { \
+                               pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+                               pos[1] = (chr & (0x01 << 5) ? fg : bg); \
+                               pos[2] = (chr & (0x01 << 3) ? fg : bg); \
+                               pos[3] = (chr & (0x01 << 1) ? fg : bg); \
+                       } else if (tpg->hflip) { \
                                pos[7] = (chr & (0x01 << 7) ? fg : bg); \
                                pos[6] = (chr & (0x01 << 6) ? fg : bg); \
                                pos[5] = (chr & (0x01 << 5) ? fg : bg); \
@@ -1120,7 +1476,7 @@ void tpg_gen_text(struct tpg_data *tpg, u8 
*basep[TPG_MAX_PLANES][2],
                                pos[7] = (chr & (0x01 << 0) ? fg : bg); \
                        } \
        \
-                       pos += tpg->hflip ? -8 : 8;     \
+                       pos += (tpg->hflip ? -8 : 8) / hdiv;    \
                }       \
        }       \
 } while (0)
@@ -1187,7 +1543,7 @@ void tpg_update_mv_step(struct tpg_data *tpg)
 }
 
 /* Map the line number relative to the crop rectangle to a frame line number */
-static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
+static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y,
                                    unsigned field)
 {
        switch (field) {
@@ -1204,7 +1560,7 @@ static unsigned tpg_calc_frameline(struct tpg_data *tpg, 
unsigned src_y,
  * Map the line number relative to the compose rectangle to a destination
  * buffer line number.
  */
-static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
+static unsigned tpg_calc_buffer_line(const struct tpg_data *tpg, unsigned y,
                                    unsigned field)
 {
        y += tpg->compose.top;
@@ -1265,6 +1621,10 @@ static void tpg_recalc(struct tpg_data *tpg)
                                                V4L2_QUANTIZATION_LIM_RANGE;
                                        break;
                                }
+                       } else if (tpg->colorspace == V4L2_COLORSPACE_BT2020) {
+                               /* R'G'B' BT.2020 is limited range */
+                               tpg->real_quantization =
+                                       V4L2_QUANTIZATION_LIM_RANGE;
                        }
                }
                tpg_precalculate_colors(tpg);
@@ -1283,191 +1643,388 @@ void tpg_calc_text_basep(struct tpg_data *tpg,
                u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
 {
        unsigned stride = tpg->bytesperline[p];
+       unsigned h = tpg->buf_height;
 
        tpg_recalc(tpg);
 
        basep[p][0] = vbuf;
        basep[p][1] = vbuf;
+       h /= tpg->vdownsampling[p];
        if (tpg->field == V4L2_FIELD_SEQ_TB)
-               basep[p][1] += tpg->buf_height * stride / 2;
+               basep[p][1] += h * stride / 2;
        else if (tpg->field == V4L2_FIELD_SEQ_BT)
-               basep[p][0] += tpg->buf_height * stride / 2;
+               basep[p][0] += h * stride / 2;
+       if (p == 0 && tpg->interleaved)
+               tpg_calc_text_basep(tpg, basep, 1, vbuf);
 }
 
-void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 
*vbuf)
+static int tpg_pattern_avg(const struct tpg_data *tpg,
+                          unsigned pat1, unsigned pat2)
 {
-       bool is_tv = std;
-       bool is_60hz = is_tv && (std & V4L2_STD_525_60);
-       unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
-       unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % 
tpg->src_width;
-       unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
-       unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % 
tpg->src_height;
+       unsigned pat_lines = tpg_get_pat_lines(tpg);
+
+       if (pat1 == (pat2 + 1) % pat_lines)
+               return pat2;
+       if (pat2 == (pat1 + 1) % pat_lines)
+               return pat1;
+       return -1;
+}
+
+/*
+ * This struct contains common parameters used by both the drawing of the
+ * test pattern and the drawing of the extras (borders, square, etc.)
+ */
+struct tpg_draw_params {
+       /* common data */
+       bool is_tv;
+       bool is_60hz;
+       unsigned twopixsize;
+       unsigned img_width;
+       unsigned stride;
+       unsigned hmax;
+       unsigned frame_line;
+       unsigned frame_line_next;
+
+       /* test pattern */
+       unsigned mv_hor_old;
+       unsigned mv_hor_new;
+       unsigned mv_vert_old;
+       unsigned mv_vert_new;
+
+       /* extras */
        unsigned wss_width;
-       unsigned f;
-       int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
-       int h;
-       unsigned twopixsize = tpg->twopixelsize[p];
-       unsigned img_width = tpg->compose.width * twopixsize / 2;
-       unsigned line_offset;
-       unsigned left_pillar_width = 0;
-       unsigned right_pillar_start = img_width;
-       unsigned stride = tpg->bytesperline[p];
-       unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
-       u8 *orig_vbuf = vbuf;
+       unsigned wss_random_offset;
+       unsigned sav_eav_f;
+       unsigned left_pillar_width;
+       unsigned right_pillar_start;
+};
 
-       /* Coarse scaling with Bresenham */
-       unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
-       unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
-       unsigned src_y = 0;
-       unsigned error = 0;
+static void tpg_fill_params_pattern(const struct tpg_data *tpg, unsigned p,
+                                   struct tpg_draw_params *params)
+{
+       params->mv_hor_old =
+               tpg_hscale_div(tpg, p, tpg->mv_hor_count % tpg->src_width);
+       params->mv_hor_new =
+               tpg_hscale_div(tpg, p, (tpg->mv_hor_count + tpg->mv_hor_step) %
+                              tpg->src_width);
+       params->mv_vert_old = tpg->mv_vert_count % tpg->src_height;
+       params->mv_vert_new =
+               (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
+}
 
-       tpg_recalc(tpg);
+static void tpg_fill_params_extras(const struct tpg_data *tpg,
+                                  unsigned p,
+                                  struct tpg_draw_params *params)
+{
+       unsigned left_pillar_width = 0;
+       unsigned right_pillar_start = params->img_width;
+
+       params->wss_width = tpg->crop.left < tpg->src_width / 2 ?
+               tpg->src_width / 2 - tpg->crop.left : 0;
+       if (params->wss_width > tpg->crop.width)
+               params->wss_width = tpg->crop.width;
+       params->wss_width = tpg_hscale_div(tpg, p, params->wss_width);
+       params->wss_random_offset =
+               params->twopixsize * prandom_u32_max(tpg->src_width / 2);
 
-       mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
-       mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
-       wss_width = tpg->crop.left < tpg->src_width / 2 ?
-                       tpg->src_width / 2 - tpg->crop.left : 0;
-       if (wss_width > tpg->crop.width)
-               wss_width = tpg->crop.width;
-       wss_width = wss_width * tpg->scaled_width / tpg->src_width;
-
-       vbuf += tpg->compose.left * twopixsize / 2;
-       line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
-       line_offset = (line_offset & ~1) * twopixsize / 2;
        if (tpg->crop.left < tpg->border.left) {
                left_pillar_width = tpg->border.left - tpg->crop.left;
                if (left_pillar_width > tpg->crop.width)
                        left_pillar_width = tpg->crop.width;
-               left_pillar_width = (left_pillar_width * tpg->scaled_width) / 
tpg->src_width;
-               left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2;
+               left_pillar_width = tpg_hscale_div(tpg, p, left_pillar_width);
        }
-       if (tpg->crop.left + tpg->crop.width > tpg->border.left + 
tpg->border.width) {
-               right_pillar_start = tpg->border.left + tpg->border.width - 
tpg->crop.left;
-               right_pillar_start = (right_pillar_start * tpg->scaled_width) / 
tpg->src_width;
-               right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2;
-               if (right_pillar_start > img_width)
-                       right_pillar_start = img_width;
+       params->left_pillar_width = left_pillar_width;
+
+       if (tpg->crop.left + tpg->crop.width >
+           tpg->border.left + tpg->border.width) {
+               right_pillar_start =
+                       tpg->border.left + tpg->border.width - tpg->crop.left;
+               right_pillar_start =
+                       tpg_hscale_div(tpg, p, right_pillar_start);
+               if (right_pillar_start > params->img_width)
+                       right_pillar_start = params->img_width;
        }
+       params->right_pillar_start = right_pillar_start;
 
-       f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+       params->sav_eav_f = tpg->field ==
+                       (params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+}
 
-       for (h = 0; h < tpg->compose.height; h++) {
-               bool even;
-               bool fill_blank = false;
-               unsigned frame_line;
-               unsigned buf_line;
-               unsigned pat_line_old;
-               unsigned pat_line_new;
-               u8 *linestart_older;
-               u8 *linestart_newer;
-               u8 *linestart_top;
-               u8 *linestart_bottom;
-
-               frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
-               even = !(frame_line & 1);
-               buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
-               src_y += int_part;
-               error += fract_part;
-               if (error >= tpg->compose.height) {
-                       error -= tpg->compose.height;
-                       src_y++;
-               }
+static void tpg_fill_plane_extras(const struct tpg_data *tpg,
+                                 const struct tpg_draw_params *params,
+                                 unsigned p, unsigned h, u8 *vbuf)
+{
+       unsigned twopixsize = params->twopixsize;
+       unsigned img_width = params->img_width;
+       unsigned frame_line = params->frame_line;
+       const struct v4l2_rect *sq = &tpg->square;
+       const struct v4l2_rect *b = &tpg->border;
+       const struct v4l2_rect *c = &tpg->crop;
+
+       if (params->is_tv && !params->is_60hz &&
+           frame_line == 0 && params->wss_width) {
+               /*
+                * Replace the first half of the top line of a 50 Hz frame
+                * with random data to simulate a WSS signal.
+                */
+               u8 *wss = tpg->random_line[p] + params->wss_random_offset;
 
-               if (h >= hmax) {
-                       if (hmax == tpg->compose.height)
-                               continue;
-                       if (!tpg->perc_fill_blank)
-                               continue;
-                       fill_blank = true;
-               }
+               memcpy(vbuf, wss, params->wss_width);
+       }
+
+       if (tpg->show_border && frame_line >= b->top &&
+           frame_line < b->top + b->height) {
+               unsigned bottom = b->top + b->height - 1;
+               unsigned left = params->left_pillar_width;
+               unsigned right = params->right_pillar_start;
 
-               if (tpg->vflip)
-                       frame_line = tpg->src_height - frame_line - 1;
-
-               if (fill_blank) {
-                       linestart_older = tpg->contrast_line[p];
-                       linestart_newer = tpg->contrast_line[p];
-               } else if (tpg->qual != TPG_QUAL_NOISE &&
-                          (frame_line < tpg->border.top ||
-                           frame_line >= tpg->border.top + 
tpg->border.height)) {
-                       linestart_older = tpg->black_line[p];
-                       linestart_newer = tpg->black_line[p];
-               } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == 
TPG_QUAL_NOISE) {
-                       linestart_older = tpg->random_line[p] +
-                                         twopixsize * 
prandom_u32_max(tpg->src_width / 2);
-                       linestart_newer = tpg->random_line[p] +
-                                         twopixsize * 
prandom_u32_max(tpg->src_width / 2);
+               if (frame_line == b->top || frame_line == b->top + 1 ||
+                   frame_line == bottom || frame_line == bottom - 1) {
+                       memcpy(vbuf + left, tpg->contrast_line[p],
+                                       right - left);
                } else {
-                       pat_line_old = tpg_get_pat_line(tpg,
-                                               (frame_line + mv_vert_old) % 
tpg->src_height);
-                       pat_line_new = tpg_get_pat_line(tpg,
-                                               (frame_line + mv_vert_new) % 
tpg->src_height);
-                       linestart_older = tpg->lines[pat_line_old][p] +
-                                         mv_hor_old * twopixsize / 2;
-                       linestart_newer = tpg->lines[pat_line_new][p] +
-                                         mv_hor_new * twopixsize / 2;
-                       linestart_older += line_offset;
-                       linestart_newer += line_offset;
+                       if (b->left >= c->left &&
+                           b->left < c->left + c->width)
+                               memcpy(vbuf + left,
+                                       tpg->contrast_line[p], twopixsize);
+                       if (b->left + b->width > c->left &&
+                           b->left + b->width <= c->left + c->width)
+                               memcpy(vbuf + right - twopixsize,
+                                       tpg->contrast_line[p], twopixsize);
                }
-               if (is_60hz) {
-                       linestart_top = linestart_newer;
-                       linestart_bottom = linestart_older;
-               } else {
-                       linestart_top = linestart_older;
-                       linestart_bottom = linestart_newer;
+       }
+       if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
+           frame_line < b->top + b->height) {
+               memcpy(vbuf, tpg->black_line[p], params->left_pillar_width);
+               memcpy(vbuf + params->right_pillar_start, tpg->black_line[p],
+                      img_width - params->right_pillar_start);
+       }
+       if (tpg->show_square && frame_line >= sq->top &&
+           frame_line < sq->top + sq->height &&
+           sq->left < c->left + c->width &&
+           sq->left + sq->width >= c->left) {
+               unsigned left = sq->left;
+               unsigned width = sq->width;
+
+               if (c->left > left) {
+                       width -= c->left - left;
+                       left = c->left;
                }
+               if (c->left + c->width < left + width)
+                       width -= left + width - c->left - c->width;
+               left -= c->left;
+               left = tpg_hscale_div(tpg, p, left);
+               width = tpg_hscale_div(tpg, p, width);
+               memcpy(vbuf + left, tpg->contrast_line[p], width);
+       }
+       if (tpg->insert_sav) {
+               unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width / 3);
+               u8 *p = vbuf + offset;
+               unsigned vact = 0, hact = 0;
+
+               p[0] = 0xff;
+               p[1] = 0;
+               p[2] = 0;
+               p[3] = 0x80 | (params->sav_eav_f << 6) |
+                       (vact << 5) | (hact << 4) |
+                       ((hact ^ vact) << 3) |
+                       ((hact ^ params->sav_eav_f) << 2) |
+                       ((params->sav_eav_f ^ vact) << 1) |
+                       (hact ^ vact ^ params->sav_eav_f);
+       }
+       if (tpg->insert_eav) {
+               unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width * 2 / 3);
+               u8 *p = vbuf + offset;
+               unsigned vact = 0, hact = 1;
+
+               p[0] = 0xff;
+               p[1] = 0;
+               p[2] = 0;
+               p[3] = 0x80 | (params->sav_eav_f << 6) |
+                       (vact << 5) | (hact << 4) |
+                       ((hact ^ vact) << 3) |
+                       ((hact ^ params->sav_eav_f) << 2) |
+                       ((params->sav_eav_f ^ vact) << 1) |
+                       (hact ^ vact ^ params->sav_eav_f);
+       }
+}
 
-               switch (tpg->field) {
-               case V4L2_FIELD_INTERLACED:
-               case V4L2_FIELD_INTERLACED_TB:
-               case V4L2_FIELD_SEQ_TB:
-               case V4L2_FIELD_SEQ_BT:
-                       if (even)
-                               memcpy(vbuf + buf_line * stride, linestart_top, 
img_width);
-                       else
-                               memcpy(vbuf + buf_line * stride, 
linestart_bottom, img_width);
-                       break;
-               case V4L2_FIELD_INTERLACED_BT:
-                       if (even)
-                               memcpy(vbuf + buf_line * stride, 
linestart_bottom, img_width);
-                       else
-                               memcpy(vbuf + buf_line * stride, linestart_top, 
img_width);
-                       break;
-               case V4L2_FIELD_TOP:
-                       memcpy(vbuf + buf_line * stride, linestart_top, 
img_width);
-                       break;
-               case V4L2_FIELD_BOTTOM:
-                       memcpy(vbuf + buf_line * stride, linestart_bottom, 
img_width);
-                       break;
-               case V4L2_FIELD_NONE:
-               default:
-                       memcpy(vbuf + buf_line * stride, linestart_older, 
img_width);
-                       break;
-               }
+static void tpg_fill_plane_pattern(const struct tpg_data *tpg,
+                                  const struct tpg_draw_params *params,
+                                  unsigned p, unsigned h, u8 *vbuf)
+{
+       unsigned twopixsize = params->twopixsize;
+       unsigned img_width = params->img_width;
+       unsigned mv_hor_old = params->mv_hor_old;
+       unsigned mv_hor_new = params->mv_hor_new;
+       unsigned mv_vert_old = params->mv_vert_old;
+       unsigned mv_vert_new = params->mv_vert_new;
+       unsigned frame_line = params->frame_line;
+       unsigned frame_line_next = params->frame_line_next;
+       unsigned line_offset = tpg_hscale_div(tpg, p, tpg->crop.left);
+       bool even;
+       bool fill_blank = false;
+       unsigned pat_line_old;
+       unsigned pat_line_new;
+       u8 *linestart_older;
+       u8 *linestart_newer;
+       u8 *linestart_top;
+       u8 *linestart_bottom;
+
+       even = !(frame_line & 1);
+
+       if (h >= params->hmax) {
+               if (params->hmax == tpg->compose.height)
+                       return;
+               if (!tpg->perc_fill_blank)
+                       return;
+               fill_blank = true;
+       }
 
-               if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
-                       /*
-                        * Replace the first half of the top line of a 50 Hz 
frame
-                        * with random data to simulate a WSS signal.
-                        */
-                       u8 *wss = tpg->random_line[p] +
+       if (tpg->vflip) {
+               frame_line = tpg->src_height - frame_line - 1;
+               frame_line_next = tpg->src_height - frame_line_next - 1;
+       }
+
+       if (fill_blank) {
+               linestart_older = tpg->contrast_line[p];
+               linestart_newer = tpg->contrast_line[p];
+       } else if (tpg->qual != TPG_QUAL_NOISE &&
+                  (frame_line < tpg->border.top ||
+                   frame_line >= tpg->border.top + tpg->border.height)) {
+               linestart_older = tpg->black_line[p];
+               linestart_newer = tpg->black_line[p];
+       } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == 
TPG_QUAL_NOISE) {
+               linestart_older = tpg->random_line[p] +
+                                 twopixsize * prandom_u32_max(tpg->src_width / 
2);
+               linestart_newer = tpg->random_line[p] +
                                  twopixsize * prandom_u32_max(tpg->src_width / 
2);
+       } else {
+               unsigned frame_line_old =
+                       (frame_line + mv_vert_old) % tpg->src_height;
+               unsigned frame_line_new =
+                       (frame_line + mv_vert_new) % tpg->src_height;
+               unsigned pat_line_next_old;
+               unsigned pat_line_next_new;
 
-                       memcpy(vbuf + buf_line * stride, wss, wss_width * 
twopixsize / 2);
+               pat_line_old = tpg_get_pat_line(tpg, frame_line_old);
+               pat_line_new = tpg_get_pat_line(tpg, frame_line_new);
+               linestart_older = tpg->lines[pat_line_old][p] + mv_hor_old;
+               linestart_newer = tpg->lines[pat_line_new][p] + mv_hor_new;
+
+               if (tpg->vdownsampling[p] > 1 && frame_line != frame_line_next) 
{
+                       int avg_pat;
+
+                       /*
+                        * Now decide whether we need to use 
downsampled_lines[].
+                        * That's necessary if the two lines use different 
patterns.
+                        */
+                       pat_line_next_old = tpg_get_pat_line(tpg,
+                                       (frame_line_next + mv_vert_old) % 
tpg->src_height);
+                       pat_line_next_new = tpg_get_pat_line(tpg,
+                                       (frame_line_next + mv_vert_new) % 
tpg->src_height);
+
+                       switch (tpg->field) {
+                       case V4L2_FIELD_INTERLACED:
+                       case V4L2_FIELD_INTERLACED_BT:
+                       case V4L2_FIELD_INTERLACED_TB:
+                               avg_pat = tpg_pattern_avg(tpg, pat_line_old, 
pat_line_new);
+                               if (avg_pat < 0)
+                                       break;
+                               linestart_older = 
tpg->downsampled_lines[avg_pat][p] + mv_hor_old;
+                               linestart_newer = linestart_older;
+                               break;
+                       case V4L2_FIELD_NONE:
+                       case V4L2_FIELD_TOP:
+                       case V4L2_FIELD_BOTTOM:
+                       case V4L2_FIELD_SEQ_BT:
+                       case V4L2_FIELD_SEQ_TB:
+                               avg_pat = tpg_pattern_avg(tpg, pat_line_old, 
pat_line_next_old);
+                               if (avg_pat >= 0)
+                                       linestart_older = 
tpg->downsampled_lines[avg_pat][p] +
+                                               mv_hor_old;
+                               avg_pat = tpg_pattern_avg(tpg, pat_line_new, 
pat_line_next_new);
+                               if (avg_pat >= 0)
+                                       linestart_newer = 
tpg->downsampled_lines[avg_pat][p] +
+                                               mv_hor_new;
+                               break;
+                       }
                }
+               linestart_older += line_offset;
+               linestart_newer += line_offset;
+       }
+       if (tpg->field_alternate) {
+               linestart_top = linestart_bottom = linestart_older;
+       } else if (params->is_60hz) {
+               linestart_top = linestart_newer;
+               linestart_bottom = linestart_older;
+       } else {
+               linestart_top = linestart_older;
+               linestart_bottom = linestart_newer;
+       }
+
+       switch (tpg->field) {
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_SEQ_TB:
+       case V4L2_FIELD_SEQ_BT:
+               if (even)
+                       memcpy(vbuf, linestart_top, img_width);
+               else
+                       memcpy(vbuf, linestart_bottom, img_width);
+               break;
+       case V4L2_FIELD_INTERLACED_BT:
+               if (even)
+                       memcpy(vbuf, linestart_bottom, img_width);
+               else
+                       memcpy(vbuf, linestart_top, img_width);
+               break;
+       case V4L2_FIELD_TOP:
+               memcpy(vbuf, linestart_top, img_width);
+               break;
+       case V4L2_FIELD_BOTTOM:
+               memcpy(vbuf, linestart_bottom, img_width);
+               break;
+       case V4L2_FIELD_NONE:
+       default:
+               memcpy(vbuf, linestart_older, img_width);
+               break;
        }
+}
+
+void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
+                          unsigned p, u8 *vbuf)
+{
+       struct tpg_draw_params params;
+       unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+
+       /* Coarse scaling with Bresenham */
+       unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
+       unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
+       unsigned src_y = 0;
+       unsigned error = 0;
+       unsigned h;
+
+       tpg_recalc(tpg);
+
+       params.is_tv = std;
+       params.is_60hz = std & V4L2_STD_525_60;
+       params.twopixsize = tpg->twopixelsize[p];
+       params.img_width = tpg_hdiv(tpg, p, tpg->compose.width);
+       params.stride = tpg->bytesperline[p];
+       params.hmax = (tpg->compose.height * tpg->perc_fill) / 100;
+
+       tpg_fill_params_pattern(tpg, p, &params);
+       tpg_fill_params_extras(tpg, p, &params);
+
+       vbuf += tpg_hdiv(tpg, p, tpg->compose.left);
 
-       vbuf = orig_vbuf;
-       vbuf += tpg->compose.left * twopixsize / 2;
-       src_y = 0;
-       error = 0;
        for (h = 0; h < tpg->compose.height; h++) {
-               unsigned frame_line = tpg_calc_frameline(tpg, src_y, 
tpg->field);
-               unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
-               const struct v4l2_rect *sq = &tpg->square;
-               const struct v4l2_rect *b = &tpg->border;
-               const struct v4l2_rect *c = &tpg->crop;
+               unsigned buf_line;
 
+               params.frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+               params.frame_line_next = params.frame_line;
+               buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
                src_y += int_part;
                error += fract_part;
                if (error >= tpg->compose.height) {
@@ -1475,80 +2032,61 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id 
std, unsigned p, u8 *vbuf)
                        src_y++;
                }
 
-               if (tpg->show_border && frame_line >= b->top &&
-                   frame_line < b->top + b->height) {
-                       unsigned bottom = b->top + b->height - 1;
-                       unsigned left = left_pillar_width;
-                       unsigned right = right_pillar_start;
+               /*
+                * For line-interleaved formats determine the 'plane'
+                * based on the buffer line.
+                */
+               if (tpg_g_interleaved(tpg))
+                       p = tpg_g_interleaved_plane(tpg, buf_line);
 
-                       if (frame_line == b->top || frame_line == b->top + 1 ||
-                           frame_line == bottom || frame_line == bottom - 1) {
-                               memcpy(vbuf + buf_line * stride + left, 
tpg->contrast_line[p],
-                                               right - left);
+               if (tpg->vdownsampling[p] > 1) {
+                       /*
+                        * When doing vertical downsampling the field setting
+                        * matters: for SEQ_BT/TB we downsample each field
+                        * separately (i.e. lines 0+2 are combined, as are
+                        * lines 1+3), for the other field settings we combine
+                        * odd and even lines. Doing that for SEQ_BT/TB would
+                        * be really weird.
+                        */
+                       if (tpg->field == V4L2_FIELD_SEQ_BT ||
+                           tpg->field == V4L2_FIELD_SEQ_TB) {
+                               unsigned next_src_y = src_y;
+
+                               if ((h & 3) >= 2)
+                                       continue;
+                               next_src_y += int_part;
+                               if (error + fract_part >= tpg->compose.height)
+                                       next_src_y++;
+                               params.frame_line_next =
+                                       tpg_calc_frameline(tpg, next_src_y, 
tpg->field);
                        } else {
-                               if (b->left >= c->left &&
-                                   b->left < c->left + c->width)
-                                       memcpy(vbuf + buf_line * stride + left,
-                                               tpg->contrast_line[p], 
twopixsize);
-                               if (b->left + b->width > c->left &&
-                                   b->left + b->width <= c->left + c->width)
-                                       memcpy(vbuf + buf_line * stride + right 
- twopixsize,
-                                               tpg->contrast_line[p], 
twopixsize);
+                               if (h & 1)
+                                       continue;
+                               params.frame_line_next =
+                                       tpg_calc_frameline(tpg, src_y, 
tpg->field);
                        }
+
+                       buf_line /= tpg->vdownsampling[p];
                }
-               if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
-                   frame_line < b->top + b->height) {
-                       memcpy(vbuf + buf_line * stride, tpg->black_line[p], 
left_pillar_width);
-                       memcpy(vbuf + buf_line * stride + right_pillar_start, 
tpg->black_line[p],
-                              img_width - right_pillar_start);
-               }
-               if (tpg->show_square && frame_line >= sq->top &&
-                   frame_line < sq->top + sq->height &&
-                   sq->left < c->left + c->width &&
-                   sq->left + sq->width >= c->left) {
-                       unsigned left = sq->left;
-                       unsigned width = sq->width;
-
-                       if (c->left > left) {
-                               width -= c->left - left;
-                               left = c->left;
-                       }
-                       if (c->left + c->width < left + width)
-                               width -= left + width - c->left - c->width;
-                       left -= c->left;
-                       left = (left * tpg->scaled_width) / tpg->src_width;
-                       left = (left & ~1) * twopixsize / 2;
-                       width = (width * tpg->scaled_width) / tpg->src_width;
-                       width = (width & ~1) * twopixsize / 2;
-                       memcpy(vbuf + buf_line * stride + left, 
tpg->contrast_line[p], width);
-               }
-               if (tpg->insert_sav) {
-                       unsigned offset = (tpg->compose.width / 6) * twopixsize;
-                       u8 *p = vbuf + buf_line * stride + offset;
-                       unsigned vact = 0, hact = 0;
-
-                       p[0] = 0xff;
-                       p[1] = 0;
-                       p[2] = 0;
-                       p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
-                               ((hact ^ vact) << 3) |
-                               ((hact ^ f) << 2) |
-                               ((f ^ vact) << 1) |
-                               (hact ^ vact ^ f);
-               }
-               if (tpg->insert_eav) {
-                       unsigned offset = (tpg->compose.width / 6) * 2 * 
twopixsize;
-                       u8 *p = vbuf + buf_line * stride + offset;
-                       unsigned vact = 0, hact = 1;
-
-                       p[0] = 0xff;
-                       p[1] = 0;
-                       p[2] = 0;
-                       p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
-                               ((hact ^ vact) << 3) |
-                               ((hact ^ f) << 2) |
-                               ((f ^ vact) << 1) |
-                               (hact ^ vact ^ f);
-               }
+               tpg_fill_plane_pattern(tpg, &params, p, h,
+                               vbuf + buf_line * params.stride);
+               tpg_fill_plane_extras(tpg, &params, p, h,
+                               vbuf + buf_line * params.stride);
+       }
+}
+
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 
*vbuf)
+{
+       unsigned offset = 0;
+       unsigned i;
+
+       if (tpg->buffers > 1) {
+               tpg_fill_plane_buffer(tpg, std, p, vbuf);
+               return;
+       }
+
+       for (i = 0; i < tpg_g_planes(tpg); i++) {
+               tpg_fill_plane_buffer(tpg, std, i, vbuf + offset);
+               offset += tpg_calc_plane_size(tpg, i);
        }
 }
diff --git a/utils/v4l2-ctl/vivid-tpg.h b/utils/v4l2-ctl/vivid-tpg.h
index c215344..1e03c07 100644
--- a/utils/v4l2-ctl/vivid-tpg.h
+++ b/utils/v4l2-ctl/vivid-tpg.h
@@ -73,7 +73,10 @@ enum tpg_pattern {
        TPG_PAT_GREEN,
        TPG_PAT_BLUE,
        TPG_PAT_CHECKERS_16X16,
+       TPG_PAT_CHECKERS_2X2,
        TPG_PAT_CHECKERS_1X1,
+       TPG_PAT_COLOR_CHECKERS_2X2,
+       TPG_PAT_COLOR_CHECKERS_1X1,
        TPG_PAT_ALTERNATING_HLINES,
        TPG_PAT_ALTERNATING_VLINES,
        TPG_PAT_CROSS_1_PIXEL,
@@ -119,7 +122,7 @@ enum tpg_move_mode {
 
 extern const char * const tpg_aspect_strings[];
 
-#define TPG_MAX_PLANES 2
+#define TPG_MAX_PLANES 3
 #define TPG_MAX_PAT_LINES 8
 
 struct tpg_data {
@@ -130,6 +133,7 @@ struct tpg_data {
        /* Scaled output frame size */
        unsigned                        scaled_width;
        u32                             field;
+       bool                            field_alternate;
        /* crop coordinates are frame-based */
        struct v4l2_rect                crop;
        /* compose coordinates are format-based */
@@ -166,7 +170,16 @@ struct tpg_data {
        enum tpg_pixel_aspect           pix_aspect;
        unsigned                        rgb_range;
        unsigned                        real_rgb_range;
+       unsigned                        buffers;
        unsigned                        planes;
+       bool                            interleaved;
+       u8                              vdownsampling[TPG_MAX_PLANES];
+       u8                              hdownsampling[TPG_MAX_PLANES];
+       /*
+        * horizontal positions must be ANDed with this value to enforce
+        * correct boundaries for packed YUYV values.
+        */
+       unsigned                        hmask[TPG_MAX_PLANES];
        /* Used to store the colors in native format, either RGB or YUV */
        u8                              colors[TPG_COLOR_MAX][3];
        u8                              textfg[TPG_MAX_PLANES][8], 
textbg[TPG_MAX_PLANES][8];
@@ -200,6 +213,7 @@ struct tpg_data {
        /* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */
        unsigned                        max_line_width;
        u8                              
*lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
+       u8                              
*downsampled_lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
        u8                              *random_line[TPG_MAX_PLANES];
        u8                              *contrast_line[TPG_MAX_PLANES];
        u8                              *black_line[TPG_MAX_PLANES];
@@ -212,11 +226,15 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned 
width, unsigned height,
                       u32 field);
 
 void tpg_set_font(const u8 *f);
-void tpg_gen_text(struct tpg_data *tpg,
+void tpg_gen_text(const struct tpg_data *tpg,
                u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text);
 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);
+unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned 
buf_line);
+void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
+                          unsigned p, u8 *vbuf);
+void tpg_fillbuffer(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);
@@ -355,9 +373,19 @@ static inline u32 tpg_g_quantization(const struct tpg_data 
*tpg)
        return tpg->quantization;
 }
 
+static inline unsigned tpg_g_buffers(const struct tpg_data *tpg)
+{
+       return tpg->buffers;
+}
+
 static inline unsigned tpg_g_planes(const struct tpg_data *tpg)
 {
-       return tpg->planes;
+       return tpg->interleaved ? 1 : tpg->planes;
+}
+
+static inline bool tpg_g_interleaved(const struct tpg_data *tpg)
+{
+       return tpg->interleaved;
 }
 
 static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned 
plane)
@@ -365,6 +393,24 @@ static inline unsigned tpg_g_twopixelsize(const struct 
tpg_data *tpg, unsigned p
        return tpg->twopixelsize[plane];
 }
 
+static inline unsigned tpg_hdiv(const struct tpg_data *tpg,
+                                 unsigned plane, unsigned x)
+{
+       return ((x / tpg->hdownsampling[plane]) & tpg->hmask[plane]) *
+               tpg->twopixelsize[plane] / 2;
+}
+
+static inline unsigned tpg_hscale(const struct tpg_data *tpg, unsigned x)
+{
+       return (x * tpg->scaled_width) / tpg->src_width;
+}
+
+static inline unsigned tpg_hscale_div(const struct tpg_data *tpg,
+                                     unsigned plane, unsigned x)
+{
+       return tpg_hdiv(tpg, plane, tpg_hscale(tpg, x));
+}
+
 static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned 
plane)
 {
        return tpg->bytesperline[plane];
@@ -372,7 +418,60 @@ static inline unsigned tpg_g_bytesperline(const struct 
tpg_data *tpg, unsigned p
 
 static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, 
unsigned bpl)
 {
-       tpg->bytesperline[plane] = bpl;
+       unsigned p;
+
+       if (tpg->buffers > 1) {
+               tpg->bytesperline[plane] = bpl;
+               return;
+       }
+
+       for (p = 0; p < tpg_g_planes(tpg); p++) {
+               unsigned plane_w = bpl * tpg->twopixelsize[p] / 
tpg->twopixelsize[0];
+
+               tpg->bytesperline[p] = plane_w / tpg->hdownsampling[p];
+       }
+}
+
+
+static inline unsigned tpg_g_line_width(const struct tpg_data *tpg, unsigned 
plane)
+{
+       unsigned w = 0;
+       unsigned p;
+
+       if (tpg->buffers > 1)
+               return tpg_g_bytesperline(tpg, plane);
+       for (p = 0; p < tpg_g_planes(tpg); p++) {
+               unsigned plane_w = tpg_g_bytesperline(tpg, p);
+
+               w += plane_w / tpg->vdownsampling[p];
+       }
+       return w;
+}
+
+static inline unsigned tpg_calc_line_width(const struct tpg_data *tpg,
+                                          unsigned plane, unsigned bpl)
+{
+       unsigned w = 0;
+       unsigned p;
+
+       if (tpg->buffers > 1)
+               return bpl;
+       for (p = 0; p < tpg_g_planes(tpg); p++) {
+               unsigned plane_w = bpl * tpg->twopixelsize[p] / 
tpg->twopixelsize[0];
+
+               plane_w /= tpg->hdownsampling[p];
+               w += plane_w / tpg->vdownsampling[p];
+       }
+       return w;
+}
+
+static inline unsigned tpg_calc_plane_size(const struct tpg_data *tpg, 
unsigned plane)
+{
+       if (plane >= tpg_g_planes(tpg))
+               return 0;
+
+       return tpg_g_bytesperline(tpg, plane) * tpg->buf_height /
+              tpg->vdownsampling[plane];
 }
 
 static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h)
@@ -380,9 +479,10 @@ static inline void tpg_s_buf_height(struct tpg_data *tpg, 
unsigned h)
        tpg->buf_height = h;
 }
 
-static inline void tpg_s_field(struct tpg_data *tpg, unsigned field)
+static inline void tpg_s_field(struct tpg_data *tpg, unsigned field, bool 
alternate)
 {
        tpg->field = field;
+       tpg->field_alternate = alternate;
 }
 
 static inline void tpg_s_perc_fill(struct tpg_data *tpg,

_______________________________________________
linuxtv-commits mailing list
[email protected]
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits

Reply via email to