[media] vivid-tpg: add hor/vert downsampling support to tpg_gen_text
[deliverable/linux.git] / drivers / media / platform / vivid / vivid-tpg.c
index 34493f435d5ab42906f682ca9356eabffcadbbb4..f4f0b746d778be0f27af1925ea53a872b0630ae4 100644 (file)
@@ -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",
@@ -125,6 +128,11 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
                        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++) {
@@ -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,7 +179,11 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
 {
        tpg->fourcc = fourcc;
        tpg->planes = 1;
+       tpg->buffers = 1;
        tpg->recalc_colors = true;
+       tpg->vdownsampling[0] = 1;
+       tpg->hdownsampling[0] = 1;
+
        switch (fourcc) {
        case V4L2_PIX_FMT_RGB565:
        case V4L2_PIX_FMT_RGB565X:
@@ -187,6 +203,9 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
                break;
        case V4L2_PIX_FMT_NV16M:
        case V4L2_PIX_FMT_NV61M:
+               tpg->vdownsampling[1] = 1;
+               tpg->hdownsampling[1] = 1;
+               tpg->buffers = 2;
                tpg->planes = 2;
                /* fall-through */
        case V4L2_PIX_FMT_YUYV:
@@ -267,7 +286,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;
 }
 
@@ -527,7 +547,7 @@ static void precalculate_color(struct tpg_data *tpg, int k)
        if (tpg->qual == TPG_QUAL_GRAY) {
                /* 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;
        }
 
        /*
@@ -667,28 +687,64 @@ static void gen_twopix(struct tpg_data *tpg,
        switch (tpg->fourcc) {
        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_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_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_RGB565:
                buf[0][offset] = (g_u << 5) | b_v;
@@ -744,11 +800,14 @@ static void gen_twopix(struct tpg_data *tpg,
 }
 
 /* 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 +822,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 +852,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 +900,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:
@@ -1001,12 +1074,39 @@ 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] +
+                                               (x / hdiv) * twopixsize / 2;
+
+                               memcpy(pos, pix[p], twopixsize / hdiv);
+                       }
+               }
+       }
+
+       if (tpg->vdownsampling[tpg->planes - 1] > 1) {
+               unsigned pat_lines = tpg_get_pat_lines(tpg);
+
+               for (pat = 0; pat < pat_lines; pat++) {
+                       unsigned next_pat = (pat + 1) % pat_lines;
+
+                       for (p = 1; p < tpg->planes; p++) {
+                               unsigned twopixsize = tpg->twopixelsize[p];
+                               unsigned hdiv = tpg->hdownsampling[p];
+
+                               for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+                                       unsigned offset = (x / hdiv) * twopixsize / 2;
+                                       u8 *pos1 = tpg->lines[pat][p] + offset;
+                                       u8 *pos2 = tpg->lines[next_pat][p] + offset;
+                                       u8 *dest = tpg->downsampled_lines[pat][p] + offset;
+                                       unsigned i;
 
-                               memcpy(pos, pix[p], twopixsize);
+                                       for (i = 0; i < twopixsize / hdiv; i++, dest++, pos1++, pos2++)
+                                               *dest = ((u16)*pos1 + (u16)*pos2) / 2;
+                               }
                        }
                }
        }
+
        for (x = 0; x < tpg->scaled_width; x += 2) {
                u8 pix[TPG_MAX_PLANES][8];
 
@@ -1083,24 +1183,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 +1233,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)
@@ -1265,6 +1378,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);
@@ -1294,7 +1411,7 @@ void tpg_calc_text_basep(struct tpg_data *tpg,
                basep[p][0] += tpg->buf_height * stride / 2;
 }
 
-void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
+void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
 {
        bool is_tv = std;
        bool is_60hz = is_tv && (std & V4L2_STD_525_60);
@@ -1409,7 +1526,9 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
                        linestart_older += line_offset;
                        linestart_newer += line_offset;
                }
-               if (is_60hz) {
+               if (tpg->field_alternate) {
+                       linestart_top = linestart_bottom = linestart_older;
+               } else if (is_60hz) {
                        linestart_top = linestart_newer;
                        linestart_bottom = linestart_older;
                } else {
@@ -1552,3 +1671,19 @@ void tpg_fillbuffer(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)
+{
+       unsigned offset = 0;
+       unsigned i;
+
+       if (tpg->buffers > 1) {
+               tpg_fill_plane_buffer(tpg, std, p, vbuf);
+               return;
+       }
+
+       for (i = 0; i < tpg->planes; i++) {
+               tpg_fill_plane_buffer(tpg, std, i, vbuf + offset);
+               offset += tpg_calc_plane_size(tpg, i);
+       }
+}
This page took 0.030204 seconds and 5 git commands to generate.