summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2011-08-17 16:18:43 +0200
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2011-11-29 11:21:22 +0100
commitc30447f399e79a6a30d4bbfc08c72cdaba882bff (patch)
tree5275feea721445ae647f735fca9f703dad5f08af
parentfefa2bf71af1de4e9faaf9efee2c11b79c170760 (diff)
Support NV12, NV21, NV16, NV61, NV24 and NV42 formats
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-rw-r--r--fbdev.c228
1 files changed, 228 insertions, 0 deletions
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:
@@ -1081,6 +1233,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) {