From patchwork Tue Jan 29 11:18:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 10786043 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AFF3E1399 for ; Tue, 29 Jan 2019 11:18:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9F40828E36 for ; Tue, 29 Jan 2019 11:18:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 903632BA09; Tue, 29 Jan 2019 11:18:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9C8BD28E36 for ; Tue, 29 Jan 2019 11:18:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726744AbfA2LSo (ORCPT ); Tue, 29 Jan 2019 06:18:44 -0500 Received: from bombadil.infradead.org ([198.137.202.133]:52340 "EHLO bombadil.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725808AbfA2LSo (ORCPT ); Tue, 29 Jan 2019 06:18:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=bombadil.20170209; h=Sender:Content-Transfer-Encoding: MIME-Version:Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=MmO116YSY43u3JuMDwQHaXqu0FMK2mvLPKKeT4bAye0=; b=o5gnADbqhPbomlwPbGOhfG1dn nmDEbvNmn5jnAzCHN3fV6FPH4EGLu3MR3YCmsR9jsuIMZUIBIymN3s/2xGxZAjWmUKd8gDAtkA/z/ l+/gCZnTV+madSaA17XNETQFYCdhnjaQvHxAPomDEfbZIBkxTbroiMHy0Mcf9AFNsNVxWIMQ9Qj57 JjUSwmXxxvuh7LtTFqcdBaj7ZcqwtxcAB0eTQBYnNBUVdayI0RvAub0PCXOpVktwL7ngcl1zJdr8r kviJxuzt2w++r/8bEYx8Zp7uVrTDpzkRmdWFqPvBXSFpEeuEHoP9YomyuF99e0oqpgKIeOwee2TgZ 0S+UInQVA==; Received: from [177.17.169.156] (helo=bombadil.infradead.org) by bombadil.infradead.org with esmtpsa (Exim 4.90_1 #2 (Red Hat Linux)) id 1goRPf-0001Mv-2y; Tue, 29 Jan 2019 11:18:43 +0000 Received: from mchehab by bombadil.infradead.org with local (Exim 4.91) (envelope-from ) id 1goRPS-0004Vy-5B; Tue, 29 Jan 2019 09:18:30 -0200 From: Mauro Carvalho Chehab To: Linux Media Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , Mauro Carvalho Chehab , Linux Doc Mailing List , Nicolas Dufresne , Hans Verkuil , Ezequiel Garcia , Sakari Ailus , Anton Leontiev Subject: [PATCH] vim2m: fix driver for it to handle handle different fourcc formats Date: Tue, 29 Jan 2019 09:18:25 -0200 Message-Id: X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Despite vim2m is reporting that it supports RGB565BE and YUYV, that's not true. Right now, it just says that it supports both format, but it doesn't actually support them. Also, horizontal flip is not properly implemented. It sounds that it was designed to do a pseudo-horizontal flip using 8 tiles. Yet, as it doesn't do format conversion, the result is a mess. I suspect that it was done this way in order to save CPU time, at the time of OMAP2 days. That's messy and doesn't really help if someone wants to use vim2m to test a pipeline. Worse than that, the unique RGB format it says it supports is RGB565BE, with is not supported by Gstreamer. That prevents practical usage of it, even for tests. So, instead, properly implement fourcc format conversions, adding a few more RGB formats: - RGB and BGR with 24 bits - RGB565LE (known as RGB16 at gstreamer) Also allows using any of the 5 supported formats as either capture or output. Note: The YUYV conversion routines are based on the conversion code written by Hans de Goede inside libv4lconvert (part of v4l-utils), released under LGPGL 2.1 (GPL 2.0 compatible). Tested all possible format combinations except for RGB565BE, as Gstreamer currently doesn't support it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 380 +++++++++++++++++++++------------ 1 file changed, 240 insertions(+), 140 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index a7a152fb3075..ccd0576c766e 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -60,8 +60,6 @@ MODULE_PARM_DESC(debug, "activates debug info"); /* Default transaction time in msec */ #define MEM2MEM_DEF_TRANSTIME 40 -#define MEM2MEM_COLOR_STEP (0xff >> 4) -#define MEM2MEM_NUM_TILES 8 /* Flags that indicate processing mode */ #define MEM2MEM_HFLIP (1 << 0) @@ -82,22 +80,24 @@ static struct platform_device vim2m_pdev = { struct vim2m_fmt { u32 fourcc; int depth; - /* Types the format can be used for */ - u32 types; }; static struct vim2m_fmt formats[] = { { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */ .depth = 16, - /* Both capture and output format */ - .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, - }, - { + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */ + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + }, { .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, - /* Output-only format */ - .types = MEM2MEM_OUTPUT, }, }; @@ -201,23 +201,222 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, return NULL; } +#define CLIP(color) \ + (u8)(((color) > 0xFF) ? 0xff : (((color) < 0) ? 0 : (color))) + +static void copy_two_pixels(struct vim2m_fmt *in, struct vim2m_fmt *out, + u8 **src, u8 **dst, bool reverse) +{ + u8 _r[2], _g[2], _b[2], *r, *g, *b; + int i, step; + + // If format is the same just copy the data, respecting the width + if (in->fourcc == out->fourcc) { + int depth = out->depth >> 3; + + if (reverse) { + if (in->fourcc == V4L2_PIX_FMT_YUYV) { + int u, v, y, y1; + + *src -= 2; + + y1 = (*src)[0]; /* copy as second point */ + u = (*src)[1]; + y = (*src)[2]; /* copy as first point */ + v = (*src)[3]; + + *src -= 2; + + *(*dst)++ = y; + *(*dst)++ = u; + *(*dst)++ = y1; + *(*dst)++ = v; + return; + } + + memcpy(*dst, *src, depth); + memcpy(*dst + depth, *src - depth, depth); + *src -= depth << 1; + } else { + memcpy(*dst, *src, depth << 1); + *src += depth << 1; + } + *dst += depth << 1; + return; + } + + /* Step 1: read two consecutive pixels from src pointer */ + + r = _r; + g = _g; + b = _b; + + if (reverse) + step = -1; + else + step = 1; + + switch (in->fourcc) { + case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ + for (i = 0; i < 2; i++) { + u16 pix = *(u16 *)*src; + + *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07; + *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03; + *b++ = (u8)((pix & 0x1f) << 3) | 0x07; + + *src += step << 1; + } + break; + case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ + for (i = 0; i < 2; i++) { + u16 pix = *(u16 *)*src; + + *r++ = (u8)(((0x00f8 & pix) >> 3) << 3) | 0x07; + *g++ = (u8)(((pix & 0x7) << 2) | + ((pix & 0xe000) >> 5)) | 0x03; + *b++ = (u8)(((pix & 0x1f00) >> 8) << 3) | 0x07; + + *src += step << 1; + } + break; + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 2; i++) { + *r++ = (*src)[0]; + *g++ = (*src)[1]; + *b++ = (*src)[2]; + + *src += step * 3; + } + break; + case V4L2_PIX_FMT_BGR24: + for (i = 0; i < 2; i++) { + *b++ = (*src)[0]; + *g++ = (*src)[1]; + *r++ = (*src)[2]; + + *src += step * 3; + } + break; + default: /* V4L2_PIX_FMT_YUYV */ + { + int u, v, y, y1, u1, v1, tmp; + + if (reverse) { + *src -= 2; + + y1 = (*src)[0]; /* copy as second point */ + u = (*src)[1]; + y = (*src)[2]; /* copy as first point */ + v = (*src)[3]; + + *src -= 2; + } else { + y = *(*src)++; + u = *(*src)++; + y1 = *(*src)++; + v = *(*src)++; + } + + u1 = (((u - 128) << 7) + (u - 128)) >> 6; + tmp = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *r++ = CLIP(y + v1); + *g++ = CLIP(y - tmp); + *b++ = CLIP(y + u1); + + *r = CLIP(y1 + v1); + *g = CLIP(y1 - tmp); + *b = CLIP(y1 + u1); + break; + } + } + + /* Step 2: store two consecutive points, reversing them if needed */ + + r = _r; + g = _g; + b = _b; + + switch (out->fourcc) { + case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ + for (i = 0; i < 2; i++) { + u16 *pix = (u16 *) *dst; + + *pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) | + (*b >> 3); + + *dst += 2; + } + return; + case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ + for (i = 0; i < 2; i++) { + u16 *pix = (u16 *) *dst; + u8 green = *g++ >> 2; + + *pix = ((green << 8) & 0xe000) | (green & 0x07) | + ((*b++ << 5) & 0x1f00) | ((*r++ & 0xf8)); + + *dst += 2; + } + return; + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 2; i++) { + *(*dst)++ = *r++; + *(*dst)++ = *g++; + *(*dst)++ = *b++; + } + return; + case V4L2_PIX_FMT_BGR24: + for (i = 0; i < 2; i++) { + *(*dst)++ = *b++; + *(*dst)++ = *g++; + *(*dst)++ = *r++; + } + return; + default: /* V4L2_PIX_FMT_YUYV */ + { + u8 y, y1, u, v; + + y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) + + 524288) >> 15); + u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b) + + 4210688) >> 15); + v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++) + + 4210688) >> 15); + y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) + + 524288) >> 15); + + *(*dst)++ = y; + *(*dst)++ = u; + + *(*dst)++ = y1; + *(*dst)++ = v; + return; + } + } +} static int device_process(struct vim2m_ctx *ctx, struct vb2_v4l2_buffer *in_vb, struct vb2_v4l2_buffer *out_vb) { struct vim2m_dev *dev = ctx->dev; - struct vim2m_q_data *q_data; - u8 *p_in, *p_out; - int x, y, t, w; - int tile_w, bytes_left; - int width, height, bytesperline; + struct vim2m_q_data *q_data_in, *q_data_out; + u8 *p_in, *p, *p_out; + int width, height, bytesperline, x, y, start, end, step; + struct vim2m_fmt *in, *out; - q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + in = q_data_in->fmt; + width = q_data_in->width; + height = q_data_in->height; + bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3; - width = q_data->width; - height = q_data->height; - bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + out = q_data_out->fmt; p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); @@ -227,100 +426,28 @@ static int device_process(struct vim2m_ctx *ctx, return -EFAULT; } - if (vb2_plane_size(&in_vb->vb2_buf, 0) > - vb2_plane_size(&out_vb->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); - return -EINVAL; - } - - tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) - / MEM2MEM_NUM_TILES; - bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; - w = 0; - - out_vb->sequence = - get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; - in_vb->sequence = q_data->sequence++; + out_vb->sequence = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; + in_vb->sequence = q_data_in->sequence++; v4l2_m2m_buf_copy_data(in_vb, out_vb, true); - switch (ctx->mode) { - case MEM2MEM_HFLIP | MEM2MEM_VFLIP: - p_out += bytesperline * height - bytes_left; - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out -= bytes_left; - } - break; + if (ctx->mode & MEM2MEM_VFLIP) { + start = height - 1; + end = -1; + step = -1; + } else { + start = 0; + end = height; + step = 1; + } + for (y = start; y != end; y += step) { + p = p_in + (y * bytesperline); + if (ctx->mode & MEM2MEM_HFLIP) + p += bytesperline - (q_data_in->fmt->depth >> 3); - case MEM2MEM_HFLIP: - for (y = 0; y < height; ++y) { - p_out += MEM2MEM_NUM_TILES * tile_w; - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x01) { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytesperline; - } - break; - - case MEM2MEM_VFLIP: - p_out += bytesperline * (height - 1); - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytes_left - 2 * bytesperline; - } - break; - - default: - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytes_left; - } + for (x = 0; x < width >> 1; x++) + copy_two_pixels(in, out, &p, &p_out, + ctx->mode & MEM2MEM_HFLIP); } return 0; @@ -433,25 +560,11 @@ static int vidioc_querycap(struct file *file, void *priv, static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) { - int i, num; struct vim2m_fmt *fmt; - num = 0; - - for (i = 0; i < NUM_FORMATS; ++i) { - if (formats[i].types & type) { - /* index-th format of type type found ? */ - if (num == f->index) - break; - /* Correct type but haven't reached our index yet, - * just increment per-type index */ - ++num; - } - } - - if (i < NUM_FORMATS) { + if (f->index < NUM_FORMATS) { /* Format found */ - fmt = &formats[i]; + fmt = &formats[f->index]; f->pixelformat = fmt->fourcc; return 0; } @@ -542,12 +655,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = formats[0].fourcc; fmt = find_format(f); } - if (!(fmt->types & MEM2MEM_CAPTURE)) { - v4l2_err(&ctx->dev->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } f->fmt.pix.colorspace = ctx->colorspace; f->fmt.pix.xfer_func = ctx->xfer_func; f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; @@ -560,19 +667,12 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vim2m_fmt *fmt; - struct vim2m_ctx *ctx = file2ctx(file); fmt = find_format(f); if (!fmt) { f->fmt.pix.pixelformat = formats[0].fourcc; fmt = find_format(f); } - if (!(fmt->types & MEM2MEM_OUTPUT)) { - v4l2_err(&ctx->dev->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;