These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / media / platform / vivid / vivid-tpg.c
index cb766eb..1425614 100644 (file)
@@ -193,6 +193,14 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        case V4L2_PIX_FMT_SGBRG8:
        case V4L2_PIX_FMT_SGRBG8:
        case V4L2_PIX_FMT_SRGGB8:
+       case V4L2_PIX_FMT_SBGGR10:
+       case V4L2_PIX_FMT_SGBRG10:
+       case V4L2_PIX_FMT_SGRBG10:
+       case V4L2_PIX_FMT_SRGGB10:
+       case V4L2_PIX_FMT_SBGGR12:
+       case V4L2_PIX_FMT_SGBRG12:
+       case V4L2_PIX_FMT_SGRBG12:
+       case V4L2_PIX_FMT_SRGGB12:
                tpg->interleaved = true;
                tpg->vdownsampling[1] = 1;
                tpg->hdownsampling[1] = 1;
@@ -220,6 +228,8 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        case V4L2_PIX_FMT_ARGB32:
        case V4L2_PIX_FMT_ABGR32:
        case V4L2_PIX_FMT_GREY:
+       case V4L2_PIX_FMT_Y16:
+       case V4L2_PIX_FMT_Y16_BE:
                tpg->is_yuv = false;
                break;
        case V4L2_PIX_FMT_YUV444:
@@ -292,6 +302,7 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        }
 
        switch (fourcc) {
+       case V4L2_PIX_FMT_GREY:
        case V4L2_PIX_FMT_RGB332:
                tpg->twopixelsize[0] = 2;
                break;
@@ -313,6 +324,8 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        case V4L2_PIX_FMT_YUV444:
        case V4L2_PIX_FMT_YUV555:
        case V4L2_PIX_FMT_YUV565:
+       case V4L2_PIX_FMT_Y16:
+       case V4L2_PIX_FMT_Y16_BE:
                tpg->twopixelsize[0] = 2 * 2;
                break;
        case V4L2_PIX_FMT_RGB24:
@@ -329,9 +342,6 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
        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:
@@ -347,6 +357,17 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
                tpg->twopixelsize[0] = 2;
                tpg->twopixelsize[1] = 2;
                break;
+       case V4L2_PIX_FMT_SRGGB10:
+       case V4L2_PIX_FMT_SGRBG10:
+       case V4L2_PIX_FMT_SGBRG10:
+       case V4L2_PIX_FMT_SBGGR10:
+       case V4L2_PIX_FMT_SRGGB12:
+       case V4L2_PIX_FMT_SGRBG12:
+       case V4L2_PIX_FMT_SGBRG12:
+       case V4L2_PIX_FMT_SBGGR12:
+               tpg->twopixelsize[0] = 4;
+               tpg->twopixelsize[1] = 4;
+               break;
        case V4L2_PIX_FMT_YUV422P:
        case V4L2_PIX_FMT_YUV420:
        case V4L2_PIX_FMT_YVU420:
@@ -479,44 +500,71 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b,
                { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224)    },
                { COEFF(0.5, 224),    COEFF(-0.445, 224), COEFF(-0.055, 224) },
        };
+       static const int smpte240m_full[3][3] = {
+               { COEFF(0.212, 255),  COEFF(0.701, 255),  COEFF(0.087, 255)  },
+               { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255)    },
+               { COEFF(0.5, 255),    COEFF(-0.445, 255), COEFF(-0.055, 255) },
+       };
        static const int bt2020[3][3] = {
                { 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.4598, 224), COEFF(-0.0402, 224) },
        };
+       static const int bt2020_full[3][3] = {
+               { COEFF(0.2627, 255),  COEFF(0.6780, 255),  COEFF(0.0593, 255)  },
+               { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255)     },
+               { COEFF(0.5, 255),     COEFF(-0.4698, 255), COEFF(-0.0402, 255) },
+       };
+       static const int bt2020c[4] = {
+               COEFF(1.0 / 1.9404, 224), COEFF(1.0 / 1.5816, 224),
+               COEFF(1.0 / 1.7184, 224), COEFF(1.0 / 0.9936, 224),
+       };
+       static const int bt2020c_full[4] = {
+               COEFF(1.0 / 1.9404, 255), COEFF(1.0 / 1.5816, 255),
+               COEFF(1.0 / 1.7184, 255), COEFF(1.0 / 0.9936, 255),
+       };
+
        bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
        unsigned y_offset = full ? 0 : 16;
        int lin_y, yc;
 
        switch (tpg->real_ycbcr_enc) {
        case V4L2_YCBCR_ENC_601:
-       case V4L2_YCBCR_ENC_XV601:
        case V4L2_YCBCR_ENC_SYCC:
                rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr);
                break;
+       case V4L2_YCBCR_ENC_XV601:
+               /* Ignore quantization range, there is only one possible
+                * Y'CbCr encoding. */
+               rgb2ycbcr(bt601, r, g, b, 16, y, cb, cr);
+               break;
+       case V4L2_YCBCR_ENC_XV709:
+               /* Ignore quantization range, there is only one possible
+                * Y'CbCr encoding. */
+               rgb2ycbcr(rec709, r, g, b, 16, y, cb, cr);
+               break;
        case V4L2_YCBCR_ENC_BT2020:
-               rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr);
+               rgb2ycbcr(full ? bt2020_full : bt2020, r, g, b, y_offset, y, cb, cr);
                break;
        case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
                lin_y = (COEFF(0.2627, 255) * rec709_to_linear(r) +
                         COEFF(0.6780, 255) * rec709_to_linear(g) +
                         COEFF(0.0593, 255) * rec709_to_linear(b)) >> 16;
                yc = linear_to_rec709(lin_y);
-               *y = (yc * 219) / 255 + (16 << 4);
+               *y = full ? yc : (yc * 219) / 255 + (16 << 4);
                if (b <= yc)
-                       *cb = (((b - yc) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4);
+                       *cb = (((b - yc) * (full ? bt2020c_full[0] : bt2020c[0])) >> 16) + (128 << 4);
                else
-                       *cb = (((b - yc) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4);
+                       *cb = (((b - yc) * (full ? bt2020c_full[1] : bt2020c[1])) >> 16) + (128 << 4);
                if (r <= yc)
-                       *cr = (((r - yc) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4);
+                       *cr = (((r - yc) * (full ? bt2020c_full[2] : bt2020c[2])) >> 16) + (128 << 4);
                else
-                       *cr = (((r - yc) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4);
+                       *cr = (((r - yc) * (full ? bt2020c_full[3] : bt2020c[3])) >> 16) + (128 << 4);
                break;
        case V4L2_YCBCR_ENC_SMPTE240M:
-               rgb2ycbcr(smpte240m, r, g, b, 16, y, cb, cr);
+               rgb2ycbcr(full ? smpte240m_full : smpte240m, r, g, b, y_offset, y, cb, cr);
                break;
        case V4L2_YCBCR_ENC_709:
-       case V4L2_YCBCR_ENC_XV709:
        default:
                rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr);
                break;
@@ -567,42 +615,71 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr,
                { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
                { COEFF(1, 219), COEFF(1.8270, 224),  COEFF(0, 224)       },
        };
+       static const int smpte240m_full[3][3] = {
+               { COEFF(1, 255), COEFF(0, 255),       COEFF(1.5756, 255)  },
+               { COEFF(1, 255), COEFF(-0.2253, 255), COEFF(-0.4767, 255) },
+               { COEFF(1, 255), COEFF(1.8270, 255),  COEFF(0, 255)       },
+       };
        static const int bt2020[3][3] = {
                { COEFF(1, 219), COEFF(0, 224),       COEFF(1.4746, 224)  },
                { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
                { COEFF(1, 219), COEFF(1.8814, 224),  COEFF(0, 224)       },
        };
+       static const int bt2020_full[3][3] = {
+               { COEFF(1, 255), COEFF(0, 255),       COEFF(1.4746, 255)  },
+               { COEFF(1, 255), COEFF(-0.1646, 255), COEFF(-0.5714, 255) },
+               { COEFF(1, 255), COEFF(1.8814, 255),  COEFF(0, 255)       },
+       };
+       static const int bt2020c[4] = {
+               COEFF(1.9404, 224), COEFF(1.5816, 224),
+               COEFF(1.7184, 224), COEFF(0.9936, 224),
+       };
+       static const int bt2020c_full[4] = {
+               COEFF(1.9404, 255), COEFF(1.5816, 255),
+               COEFF(1.7184, 255), COEFF(0.9936, 255),
+       };
+
        bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
        unsigned y_offset = full ? 0 : 16;
+       int y_fac = full ? COEFF(1.0, 255) : COEFF(1.0, 219);
        int lin_r, lin_g, lin_b, lin_y;
 
        switch (tpg->real_ycbcr_enc) {
        case V4L2_YCBCR_ENC_601:
-       case V4L2_YCBCR_ENC_XV601:
        case V4L2_YCBCR_ENC_SYCC:
                ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b);
                break;
+       case V4L2_YCBCR_ENC_XV601:
+               /* Ignore quantization range, there is only one possible
+                * Y'CbCr encoding. */
+               ycbcr2rgb(bt601, y, cb, cr, 16, r, g, b);
+               break;
+       case V4L2_YCBCR_ENC_XV709:
+               /* Ignore quantization range, there is only one possible
+                * Y'CbCr encoding. */
+               ycbcr2rgb(rec709, y, cb, cr, 16, r, g, b);
+               break;
        case V4L2_YCBCR_ENC_BT2020:
-               ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b);
+               ycbcr2rgb(full ? bt2020_full : bt2020, y, cb, cr, y_offset, r, g, b);
                break;
        case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
-               y -= 16 << 4;
+               y -= full ? 0 : 16 << 4;
                cb -= 128 << 4;
                cr -= 128 << 4;
 
                if (cb <= 0)
-                       *b = COEFF(1.0, 219) * y + COEFF(1.9404, 224) * cb;
+                       *b = y_fac * y + (full ? bt2020c_full[0] : bt2020c[0]) * cb;
                else
-                       *b = COEFF(1.0, 219) * y + COEFF(1.5816, 224) * cb;
+                       *b = y_fac * y + (full ? bt2020c_full[1] : bt2020c[1]) * cb;
                *b = *b >> 12;
                if (cr <= 0)
-                       *r = COEFF(1.0, 219) * y + COEFF(1.7184, 224) * cr;
+                       *r = y_fac * y + (full ? bt2020c_full[2] : bt2020c[2]) * cr;
                else
-                       *r = COEFF(1.0, 219) * y + COEFF(0.9936, 224) * cr;
+                       *r = y_fac * y + (full ? bt2020c_full[3] : bt2020c[3]) * cr;
                *r = *r >> 12;
                lin_r = rec709_to_linear(*r);
                lin_b = rec709_to_linear(*b);
-               lin_y = rec709_to_linear((y * 255) / 219);
+               lin_y = rec709_to_linear((y * 255) / (full ? 255 : 219));
 
                lin_g = COEFF(1.0 / 0.6780, 255) * lin_y -
                        COEFF(0.2627 / 0.6780, 255) * lin_r -
@@ -610,10 +687,9 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr,
                *g = linear_to_rec709(lin_g >> 12);
                break;
        case V4L2_YCBCR_ENC_SMPTE240M:
-               ycbcr2rgb(smpte240m, y, cb, cr, 16, r, g, b);
+               ycbcr2rgb(full ? smpte240m_full : smpte240m, y, cb, cr, y_offset, r, g, b);
                break;
        case V4L2_YCBCR_ENC_709:
-       case V4L2_YCBCR_ENC_XV709:
        default:
                ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b);
                break;
@@ -649,15 +725,17 @@ static void precalculate_color(struct tpg_data *tpg, int k)
        }
 
        if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
-               r = tpg_csc_colors[tpg->colorspace][col].r;
-               g = tpg_csc_colors[tpg->colorspace][col].g;
-               b = tpg_csc_colors[tpg->colorspace][col].b;
+               r = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].r;
+               g = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].g;
+               b = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].b;
        } else {
                r <<= 4;
                g <<= 4;
                b <<= 4;
        }
-       if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY) {
+       if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY ||
+           tpg->fourcc == V4L2_PIX_FMT_Y16 ||
+           tpg->fourcc == V4L2_PIX_FMT_Y16_BE) {
                /* Rec. 709 Luma function */
                /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
                r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16;
@@ -840,6 +918,21 @@ static void gen_twopix(struct tpg_data *tpg,
        case V4L2_PIX_FMT_GREY:
                buf[0][offset] = r_y;
                break;
+       case V4L2_PIX_FMT_Y16:
+               /*
+                * Ideally both bytes should be set to r_y, but then you won't
+                * be able to detect endian problems. So keep it 0 except for
+                * the corner case where r_y is 0xff so white really will be
+                * white (0xffff).
+                */
+               buf[0][offset] = r_y == 0xff ? r_y : 0;
+               buf[0][offset+1] = r_y;
+               break;
+       case V4L2_PIX_FMT_Y16_BE:
+               /* See comment for V4L2_PIX_FMT_Y16 above */
+               buf[0][offset] = r_y;
+               buf[0][offset+1] = r_y == 0xff ? r_y : 0;
+               break;
        case V4L2_PIX_FMT_YUV422P:
        case V4L2_PIX_FMT_YUV420:
        case V4L2_PIX_FMT_YUV420M:
@@ -1038,6 +1131,70 @@ static void gen_twopix(struct tpg_data *tpg,
                buf[0][offset] = odd ? g_u : r_y;
                buf[1][offset] = odd ? b_v : g_u;
                break;
+       case V4L2_PIX_FMT_SBGGR10:
+               buf[0][offset] = odd ? g_u << 2 : b_v << 2;
+               buf[0][offset + 1] = odd ? g_u >> 6 : b_v >> 6;
+               buf[1][offset] = odd ? r_y << 2 : g_u << 2;
+               buf[1][offset + 1] = odd ? r_y >> 6 : g_u >> 6;
+               buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+               buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+               break;
+       case V4L2_PIX_FMT_SGBRG10:
+               buf[0][offset] = odd ? b_v << 2 : g_u << 2;
+               buf[0][offset + 1] = odd ? b_v >> 6 : g_u >> 6;
+               buf[1][offset] = odd ? g_u << 2 : r_y << 2;
+               buf[1][offset + 1] = odd ? g_u >> 6 : r_y >> 6;
+               buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+               buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+               break;
+       case V4L2_PIX_FMT_SGRBG10:
+               buf[0][offset] = odd ? r_y << 2 : g_u << 2;
+               buf[0][offset + 1] = odd ? r_y >> 6 : g_u >> 6;
+               buf[1][offset] = odd ? g_u << 2 : b_v << 2;
+               buf[1][offset + 1] = odd ? g_u >> 6 : b_v >> 6;
+               buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+               buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+               break;
+       case V4L2_PIX_FMT_SRGGB10:
+               buf[0][offset] = odd ? g_u << 2 : r_y << 2;
+               buf[0][offset + 1] = odd ? g_u >> 6 : r_y >> 6;
+               buf[1][offset] = odd ? b_v << 2 : g_u << 2;
+               buf[1][offset + 1] = odd ? b_v >> 6 : g_u >> 6;
+               buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+               buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+               break;
+       case V4L2_PIX_FMT_SBGGR12:
+               buf[0][offset] = odd ? g_u << 4 : b_v << 4;
+               buf[0][offset + 1] = odd ? g_u >> 4 : b_v >> 4;
+               buf[1][offset] = odd ? r_y << 4 : g_u << 4;
+               buf[1][offset + 1] = odd ? r_y >> 4 : g_u >> 4;
+               buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+               buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+               break;
+       case V4L2_PIX_FMT_SGBRG12:
+               buf[0][offset] = odd ? b_v << 4 : g_u << 4;
+               buf[0][offset + 1] = odd ? b_v >> 4 : g_u >> 4;
+               buf[1][offset] = odd ? g_u << 4 : r_y << 4;
+               buf[1][offset + 1] = odd ? g_u >> 4 : r_y >> 4;
+               buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+               buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+               break;
+       case V4L2_PIX_FMT_SGRBG12:
+               buf[0][offset] = odd ? r_y << 4 : g_u << 4;
+               buf[0][offset + 1] = odd ? r_y >> 4 : g_u >> 4;
+               buf[1][offset] = odd ? g_u << 4 : b_v << 4;
+               buf[1][offset + 1] = odd ? g_u >> 4 : b_v >> 4;
+               buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+               buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+               break;
+       case V4L2_PIX_FMT_SRGGB12:
+               buf[0][offset] = odd ? g_u << 4 : r_y << 4;
+               buf[0][offset + 1] = odd ? g_u >> 4 : r_y >> 4;
+               buf[1][offset] = odd ? b_v << 4 : g_u << 4;
+               buf[1][offset + 1] = odd ? b_v >> 4 : g_u >> 4;
+               buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+               buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+               break;
        }
 }
 
@@ -1048,6 +1205,14 @@ unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line)
        case V4L2_PIX_FMT_SGBRG8:
        case V4L2_PIX_FMT_SGRBG8:
        case V4L2_PIX_FMT_SRGGB8:
+       case V4L2_PIX_FMT_SBGGR10:
+       case V4L2_PIX_FMT_SGBRG10:
+       case V4L2_PIX_FMT_SGRBG10:
+       case V4L2_PIX_FMT_SRGGB10:
+       case V4L2_PIX_FMT_SBGGR12:
+       case V4L2_PIX_FMT_SGBRG12:
+       case V4L2_PIX_FMT_SGRBG12:
+       case V4L2_PIX_FMT_SRGGB12:
                return buf_line & 1;
        default:
                return 0;
@@ -1395,42 +1560,10 @@ 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(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;
-       unsigned div = step;
-       unsigned first = 0;
-       unsigned len = strlen(text);
-       unsigned p;
-
-       if (font8x16 == NULL || basep == NULL)
-               return;
-
-       /* Checks if it is possible to show string */
-       if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
-               return;
-
-       if (len > (tpg->compose.width - x) / 8)
-               len = (tpg->compose.width - x) / 8;
-       if (tpg->vflip)
-               y = tpg->compose.height - y - 16;
-       if (tpg->hflip)
-               x = tpg->compose.width - x - 8;
-       y += tpg->compose.top;
-       x += tpg->compose.left;
-       if (tpg->field == V4L2_FIELD_BOTTOM)
-               first = 1;
-       else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
-               div = 2;
-
-       for (p = 0; p < tpg->planes; p++) {
-               unsigned vdiv = tpg->vdownsampling[p];
-               unsigned hdiv = tpg->hdownsampling[p];
-
-               /* Print text */
 #define PRINTSTR(PIXTYPE) do { \
+       unsigned vdiv = tpg->vdownsampling[p]; \
+       unsigned hdiv = tpg->hdownsampling[p]; \
+       int line;       \
        PIXTYPE fg;     \
        PIXTYPE bg;     \
        memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE));   \
@@ -1481,15 +1614,82 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
        }       \
 } while (0)
 
+static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+                       unsigned p, unsigned first, unsigned div, unsigned step,
+                       int y, int x, char *text, unsigned len)
+{
+       PRINTSTR(u8);
+}
+
+static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+                       unsigned p, unsigned first, unsigned div, unsigned step,
+                       int y, int x, char *text, unsigned len)
+{
+       PRINTSTR(u16);
+}
+
+static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+                       unsigned p, unsigned first, unsigned div, unsigned step,
+                       int y, int x, char *text, unsigned len)
+{
+       PRINTSTR(x24);
+}
+
+static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+                       unsigned p, unsigned first, unsigned div, unsigned step,
+                       int y, int x, char *text, unsigned len)
+{
+       PRINTSTR(u32);
+}
+
+void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+                 int y, int x, char *text)
+{
+       unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+       unsigned div = step;
+       unsigned first = 0;
+       unsigned len = strlen(text);
+       unsigned p;
+
+       if (font8x16 == NULL || basep == NULL)
+               return;
+
+       /* Checks if it is possible to show string */
+       if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
+               return;
+
+       if (len > (tpg->compose.width - x) / 8)
+               len = (tpg->compose.width - x) / 8;
+       if (tpg->vflip)
+               y = tpg->compose.height - y - 16;
+       if (tpg->hflip)
+               x = tpg->compose.width - x - 8;
+       y += tpg->compose.top;
+       x += tpg->compose.left;
+       if (tpg->field == V4L2_FIELD_BOTTOM)
+               first = 1;
+       else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
+               div = 2;
+
+       for (p = 0; p < tpg->planes; p++) {
+               /* Print text */
                switch (tpg->twopixelsize[p]) {
                case 2:
-                       PRINTSTR(u8); break;
+                       tpg_print_str_2(tpg, basep, p, first, div, step, y, x,
+                                       text, len);
+                       break;
                case 4:
-                       PRINTSTR(u16); break;
+                       tpg_print_str_4(tpg, basep, p, first, div, step, y, x,
+                                       text, len);
+                       break;
                case 6:
-                       PRINTSTR(x24); break;
+                       tpg_print_str_6(tpg, basep, p, first, div, step, y, x,
+                                       text, len);
+                       break;
                case 8:
-                       PRINTSTR(u32); break;
+                       tpg_print_str_8(tpg, basep, p, first, div, step, y, x,
+                                       text, len);
+                       break;
                }
        }
 }
@@ -1583,50 +1783,23 @@ static void tpg_recalc(struct tpg_data *tpg)
        if (tpg->recalc_colors) {
                tpg->recalc_colors = false;
                tpg->recalc_lines = true;
+               tpg->real_xfer_func = tpg->xfer_func;
                tpg->real_ycbcr_enc = tpg->ycbcr_enc;
                tpg->real_quantization = tpg->quantization;
-               if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
-                       switch (tpg->colorspace) {
-                       case V4L2_COLORSPACE_REC709:
-                               tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_709;
-                               break;
-                       case V4L2_COLORSPACE_SRGB:
-                               tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SYCC;
-                               break;
-                       case V4L2_COLORSPACE_BT2020:
-                               tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_BT2020;
-                               break;
-                       case V4L2_COLORSPACE_SMPTE240M:
-                               tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SMPTE240M;
-                               break;
-                       case V4L2_COLORSPACE_SMPTE170M:
-                       case V4L2_COLORSPACE_470_SYSTEM_M:
-                       case V4L2_COLORSPACE_470_SYSTEM_BG:
-                       case V4L2_COLORSPACE_ADOBERGB:
-                       default:
-                               tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_601;
-                               break;
-                       }
-               }
-               if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) {
-                       tpg->real_quantization = V4L2_QUANTIZATION_FULL_RANGE;
-                       if (tpg->is_yuv) {
-                               switch (tpg->real_ycbcr_enc) {
-                               case V4L2_YCBCR_ENC_SYCC:
-                               case V4L2_YCBCR_ENC_XV601:
-                               case V4L2_YCBCR_ENC_XV709:
-                                       break;
-                               default:
-                                       tpg->real_quantization =
-                                               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;
-                       }
-               }
+
+               if (tpg->xfer_func == V4L2_XFER_FUNC_DEFAULT)
+                       tpg->real_xfer_func =
+                               V4L2_MAP_XFER_FUNC_DEFAULT(tpg->colorspace);
+
+               if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
+                       tpg->real_ycbcr_enc =
+                               V4L2_MAP_YCBCR_ENC_DEFAULT(tpg->colorspace);
+
+               if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT)
+                       tpg->real_quantization =
+                               V4L2_MAP_QUANTIZATION_DEFAULT(!tpg->is_yuv,
+                                       tpg->colorspace, tpg->real_ycbcr_enc);
+
                tpg_precalculate_colors(tpg);
        }
        if (tpg->recalc_square_border) {
@@ -1670,6 +1843,23 @@ static int tpg_pattern_avg(const struct tpg_data *tpg,
        return -1;
 }
 
+void tpg_log_status(struct tpg_data *tpg)
+{
+       pr_info("tpg source WxH: %ux%u (%s)\n",
+                       tpg->src_width, tpg->src_height,
+                       tpg->is_yuv ? "YCbCr" : "RGB");
+       pr_info("tpg field: %u\n", tpg->field);
+       pr_info("tpg crop: %ux%u@%dx%d\n", tpg->crop.width, tpg->crop.height,
+                       tpg->crop.left, tpg->crop.top);
+       pr_info("tpg compose: %ux%u@%dx%d\n", tpg->compose.width, tpg->compose.height,
+                       tpg->compose.left, tpg->compose.top);
+       pr_info("tpg colorspace: %d\n", tpg->colorspace);
+       pr_info("tpg transfer function: %d/%d\n", tpg->xfer_func, tpg->real_xfer_func);
+       pr_info("tpg Y'CbCr encoding: %d/%d\n", tpg->ycbcr_enc, tpg->real_ycbcr_enc);
+       pr_info("tpg quantization: %d/%d\n", tpg->quantization, tpg->real_quantization);
+       pr_info("tpg RGB range: %d/%d\n", tpg->rgb_range, tpg->real_rgb_range);
+}
+
 /*
  * This struct contains common parameters used by both the drawing of the
  * test pattern and the drawing of the extras (borders, square, etc.)