From c30447f399e79a6a30d4bbfc08c72cdaba882bff Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 17 Aug 2011 16:18:43 +0200 Subject: Support NV12, NV21, NV16, NV61, NV24 and NV42 formats Signed-off-by: Laurent Pinchart --- fbdev.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/fbdev.c b/fbdev.c index 4b83f29..10e7612 100644 --- a/fbdev.c +++ b/fbdev.c @@ -72,9 +72,20 @@ struct fb_format_info { const char *name; }; +#ifndef V4L2_PIX_FMT_NV24 +#define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') +#define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') +#endif + static const struct fb_format_info formats[] = { { V4L2_PIX_FMT_BGR24, "BGR24" }, { V4L2_PIX_FMT_BGR32, "BGR32" }, + { V4L2_PIX_FMT_NV12, "NV12" }, + { V4L2_PIX_FMT_NV16, "NV16" }, + { V4L2_PIX_FMT_NV21, "NV21" }, + { V4L2_PIX_FMT_NV24, "NV24" }, + { V4L2_PIX_FMT_NV42, "NV42" }, + { V4L2_PIX_FMT_NV61, "NV61" }, { V4L2_PIX_FMT_RGB24, "RGB24" }, { V4L2_PIX_FMT_RGB32, "RGB32" }, { V4L2_PIX_FMT_RGB565, "RGB565" }, @@ -850,6 +861,129 @@ static int fb_pan(struct device *dev, unsigned int x, unsigned int y) * Test pattern */ +struct fb_color_yuv { + unsigned char y; + unsigned char u; + unsigned char v; +}; + +enum fb_yuv_order { + FB_YUV_YCbCr, + FB_YUV_YCrCb, +}; + +#define FB_MAKE_YUV_601(dev, r, g, b) \ + { .y = (( 66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16, \ + .u = ((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128, \ + .v = ((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128 } + +static void +fb_fill_smpte_nv(struct device *dev, unsigned int xoffset, unsigned int yoffset, + unsigned int xres, unsigned int yres, + unsigned int xsub, unsigned int ysub, + enum fb_yuv_order order) +{ + const struct fb_color_yuv colors_top[] = { + FB_MAKE_YUV_601(dev, 192, 192, 192), /* grey */ + FB_MAKE_YUV_601(dev, 192, 192, 0), /* yellow */ + FB_MAKE_YUV_601(dev, 0, 192, 192), /* cyan */ + FB_MAKE_YUV_601(dev, 0, 192, 0), /* green */ + FB_MAKE_YUV_601(dev, 192, 0, 192), /* magenta */ + FB_MAKE_YUV_601(dev, 192, 0, 0), /* red */ + FB_MAKE_YUV_601(dev, 0, 0, 192), /* blue */ + }; + const struct fb_color_yuv colors_middle[] = { + FB_MAKE_YUV_601(dev, 0, 0, 192), /* blue */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 192, 0, 192), /* magenta */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 0, 192, 192), /* cyan */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 192, 192, 192), /* grey */ + }; + const struct fb_color_yuv colors_bottom[] = { + FB_MAKE_YUV_601(dev, 0, 33, 76), /* in-phase */ + FB_MAKE_YUV_601(dev, 255, 255, 255), /* super white */ + FB_MAKE_YUV_601(dev, 50, 0, 106), /* quadrature */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 9, 9, 9), /* 3.5% */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* 7.5% */ + FB_MAKE_YUV_601(dev, 29, 29, 29), /* 11.5% */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + }; + uint8_t *y_mem = dev->mem + dev->fix_info.line_length * yoffset + + xoffset; + uint8_t *c_mem = dev->mem + + dev->fix_info.line_length * dev->var_info.yres_virtual + + dev->fix_info.line_length * yoffset / ysub + + xoffset * 2 / xsub; + unsigned int u = order != FB_YUV_YCbCr; + unsigned int v = order == FB_YUV_YCbCr; + unsigned int x; + unsigned int y; + + /* Luma */ + for (y = 0; y < yres * 6 / 9; ++y) { + for (x = 0; x < xres; ++x) + y_mem[x] = colors_top[x * 7 / xres].y; + y_mem += dev->fix_info.line_length; + } + + for (; y < yres * 7 / 9; ++y) { + for (x = 0; x < xres; ++x) + y_mem[x] = colors_middle[x * 7 / xres].y; + y_mem += dev->fix_info.line_length; + } + + for (; y < yres; ++y) { + for (x = 0; x < xres * 5 / 7; ++x) + y_mem[x] = colors_bottom[x * 4 / (xres * 5 / 7)].y; + for (; x < xres * 6 / 7; ++x) + y_mem[x] = colors_bottom[(x - xres * 5 / 7) * 3 + / (xres / 7) + 4].y; + for (; x < xres; ++x) + y_mem[x] = colors_bottom[7].y; + y_mem += dev->fix_info.line_length; + } + + /* Chroma */ + for (y = 0; y < yres / ysub * 6 / 9; ++y) { + for (x = 0; x < xres; x += xsub) { + c_mem[2/xsub*x+u] = colors_top[x * 7 / xres].u; + c_mem[2/xsub*x+v] = colors_top[x * 7 / xres].v; + } + c_mem += dev->fix_info.line_length * 2 / xsub; + } + + for (; y < yres / ysub * 7 / 9; ++y) { + for (x = 0; x < xres; x += xsub) { + c_mem[2/xsub*x+u] = colors_middle[x * 7 / xres].u; + c_mem[2/xsub*x+v] = colors_middle[x * 7 / xres].v; + } + c_mem += dev->fix_info.line_length * 2 / xsub; + } + + for (; y < yres / ysub; ++y) { + for (x = 0; x < xres * 5 / 7; x += xsub) { + c_mem[2/xsub*x+u] = + colors_bottom[x * 4 / (xres * 5 / 7)].u; + c_mem[2/xsub*x+v] = + colors_bottom[x * 4 / (xres * 5 / 7)].v; + } + for (; x < xres * 6 / 7; x += xsub) { + c_mem[2/xsub*x+u] = colors_bottom[(x - xres * 5 / 7) * + 3 / (xres / 7) + 4].u; + c_mem[2/xsub*x+v] = colors_bottom[(x - xres * 5 / 7) * + 3 / (xres / 7) + 4].v; + } + for (; x < xres; x += xsub) { + c_mem[2/xsub*x+u] = colors_bottom[7].u; + c_mem[2/xsub*x+v] = colors_bottom[7].v; + } + c_mem += dev->fix_info.line_length * 2 / xsub; + } +} + #define FB_MAKE_RGB(dev, r, g, b) \ ((((r) >> (8 - (dev)->red.length)) << (dev)->red.offset) | \ (((g) >> (8 - (dev)->green.length)) << (dev)->green.offset) | \ @@ -1069,6 +1203,24 @@ fb_fill_smpte(struct device *dev, unsigned int xoffset, unsigned int yoffset, unsigned int xres, unsigned int yres) { switch (dev->fourcc) { + case V4L2_PIX_FMT_NV12: + return fb_fill_smpte_nv(dev, xoffset, yoffset, xres, yres, + 2, 2, FB_YUV_YCbCr); + case V4L2_PIX_FMT_NV21: + return fb_fill_smpte_nv(dev, xoffset, yoffset, xres, yres, + 2, 2, FB_YUV_YCrCb); + case V4L2_PIX_FMT_NV16: + return fb_fill_smpte_nv(dev, xoffset, yoffset, xres, yres, + 2, 1, FB_YUV_YCbCr); + case V4L2_PIX_FMT_NV61: + return fb_fill_smpte_nv(dev, xoffset, yoffset, xres, yres, + 2, 1, FB_YUV_YCrCb); + case V4L2_PIX_FMT_NV24: + return fb_fill_smpte_nv(dev, xoffset, yoffset, xres, yres, + 1, 1, FB_YUV_YCbCr); + case V4L2_PIX_FMT_NV42: + return fb_fill_smpte_nv(dev, xoffset, yoffset, xres, yres, + 1, 1, FB_YUV_YCrCb); case V4L2_PIX_FMT_RGB565: return fb_fill_smpte_rgb16(dev, xoffset, yoffset, xres, yres); case V4L2_PIX_FMT_BGR24: @@ -1080,6 +1232,58 @@ fb_fill_smpte(struct device *dev, unsigned int xoffset, unsigned int yoffset, } } +static void +fb_fill_lines_nv(struct device *dev, unsigned int xoffset, unsigned int yoffset, + unsigned int xres, unsigned int yres, + unsigned int xsub, unsigned int ysub, + enum fb_yuv_order order) +{ + const struct fb_color_yuv colors[] = { + FB_MAKE_YUV_601(dev, 192, 192, 0), /* yellow */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 0, 192, 0), /* green */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 192, 0, 0), /* red */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + FB_MAKE_YUV_601(dev, 0, 0, 192), /* blue */ + FB_MAKE_YUV_601(dev, 19, 19, 19), /* black */ + }; + uint8_t *y_mem = dev->mem + dev->fix_info.line_length * yoffset + + xoffset; + uint8_t *c_mem = dev->mem + + dev->fix_info.line_length * dev->var_info.yres_virtual + + dev->fix_info.line_length * yoffset / ysub + + xoffset * 2 / xsub; + unsigned int u = order != FB_YUV_YCbCr; + unsigned int v = order == FB_YUV_YCbCr; + unsigned int x_min; + unsigned int y_min; + unsigned int x; + unsigned int y; + + for (y = 0; y < yres; ++y) { + y_min = min(y, yres - y - 1); + for (x = 0; x < xres; ++x) { + x_min = min(x, xres - x - 1); + y_mem[x] = colors[min(x_min, y_min) % + ARRAY_SIZE(colors)].y; + } + y_mem += dev->fix_info.line_length; + } + + for (y = 0; y < yres; y += ysub) { + y_min = min(y, yres - y - 1); + for (x = 0; x < xres; x += xsub) { + x_min = min(x, xres - x - 1); + c_mem[2/xsub*x+u] = colors[min(x_min, y_min) % + ARRAY_SIZE(colors)].u; + c_mem[2/xsub*x+v] = colors[min(x_min, y_min) % + ARRAY_SIZE(colors)].v; + } + c_mem += dev->fix_info.line_length * 2 / xsub; + } +} + static void fb_fill_lines_rgb16(struct device *dev, unsigned int xoffset, unsigned int yoffset, unsigned int xres, unsigned int yres) @@ -1192,6 +1396,24 @@ fb_fill_lines(struct device *dev, unsigned int xoffset, unsigned int yoffset, unsigned int xres, unsigned int yres) { switch (dev->fourcc) { + case V4L2_PIX_FMT_NV12: + return fb_fill_lines_nv(dev, xoffset, yoffset, xres, yres, + 2, 2, FB_YUV_YCbCr); + case V4L2_PIX_FMT_NV21: + return fb_fill_lines_nv(dev, xoffset, yoffset, xres, yres, + 2, 2, FB_YUV_YCrCb); + case V4L2_PIX_FMT_NV16: + return fb_fill_lines_nv(dev, xoffset, yoffset, xres, yres, + 2, 1, FB_YUV_YCbCr); + case V4L2_PIX_FMT_NV61: + return fb_fill_lines_nv(dev, xoffset, yoffset, xres, yres, + 2, 1, FB_YUV_YCrCb); + case V4L2_PIX_FMT_NV24: + return fb_fill_lines_nv(dev, xoffset, yoffset, xres, yres, + 1, 1, FB_YUV_YCbCr); + case V4L2_PIX_FMT_NV42: + return fb_fill_lines_nv(dev, xoffset, yoffset, xres, yres, + 1, 1, FB_YUV_YCrCb); case V4L2_PIX_FMT_RGB565: return fb_fill_lines_rgb16(dev, xoffset, yoffset, xres, yres); case V4L2_PIX_FMT_BGR24: @@ -1231,6 +1453,12 @@ fb_fill(struct device *dev, enum fb_fill_mode mode, enum fb_fill_pattern pattern if (dev->fourcc != V4L2_PIX_FMT_BGR24 && dev->fourcc != V4L2_PIX_FMT_BGR32 && + dev->fourcc != V4L2_PIX_FMT_NV12 && + dev->fourcc != V4L2_PIX_FMT_NV16 && + dev->fourcc != V4L2_PIX_FMT_NV21 && + dev->fourcc != V4L2_PIX_FMT_NV24 && + dev->fourcc != V4L2_PIX_FMT_NV42 && + dev->fourcc != V4L2_PIX_FMT_NV61 && dev->fourcc != V4L2_PIX_FMT_RGB24 && dev->fourcc != V4L2_PIX_FMT_RGB32 && dev->fourcc != V4L2_PIX_FMT_RGB565) { -- cgit v1.2.3