diff options
| author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-08-17 16:18:43 +0200 | 
|---|---|---|
| committer | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-11-29 11:21:22 +0100 | 
| commit | c30447f399e79a6a30d4bbfc08c72cdaba882bff (patch) | |
| tree | 5275feea721445ae647f735fca9f703dad5f08af | |
| parent | fefa2bf71af1de4e9faaf9efee2c11b79c170760 (diff) | |
Support NV12, NV21, NV16, NV61, NV24 and NV42 formats
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
| -rw-r--r-- | fbdev.c | 228 | 
1 files changed, 228 insertions, 0 deletions
| @@ -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) { | 
