From patchwork Wed Feb 22 13:51:41 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javier Martin X-Patchwork-Id: 10021 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1S0Cby-0005Y2-Fp for patchwork@linuxtv.org; Wed, 22 Feb 2012 14:52:02 +0100 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.75/mailfrontend-2) with esmtp for id 1S0Cbt-0005rF-Gb; Wed, 22 Feb 2012 14:51:58 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752055Ab2BVNvy (ORCPT ); Wed, 22 Feb 2012 08:51:54 -0500 Received: from mail-ww0-f42.google.com ([74.125.82.42]:45998 "EHLO mail-ww0-f42.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751921Ab2BVNvx (ORCPT ); Wed, 22 Feb 2012 08:51:53 -0500 Received: by wgbgn7 with SMTP id gn7so4911197wgb.1 for ; Wed, 22 Feb 2012 05:51:52 -0800 (PST) Received-SPF: pass (google.com: domain of javier.martin@vista-silicon.com designates 10.180.101.165 as permitted sender) client-ip=10.180.101.165; Authentication-Results: mr.google.com; spf=pass (google.com: domain of javier.martin@vista-silicon.com designates 10.180.101.165 as permitted sender) smtp.mail=javier.martin@vista-silicon.com Received: from mr.google.com ([10.180.101.165]) by 10.180.101.165 with SMTP id fh5mr35476169wib.10.1329918712153 (num_hops = 1); Wed, 22 Feb 2012 05:51:52 -0800 (PST) MIME-Version: 1.0 Received: by 10.180.101.165 with SMTP id fh5mr29344530wib.10.1329918712041; Wed, 22 Feb 2012 05:51:52 -0800 (PST) Received: from localhost.localdomain (182.50.18.95.dynamic.jazztel.es. [95.18.50.182]) by mx.google.com with ESMTPS id bg3sm2009397wib.10.2012.02.22.05.51.49 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 22 Feb 2012 05:51:51 -0800 (PST) From: Javier Martin To: linux-media@vger.kernel.org Cc: s.hauer@pengutronix.de, mchehab@infradead.org, g.liakhovetski@gmx.de, Javier Martin Subject: [PATCH v2] media: i.MX27 camera: Add resizing support. Date: Wed, 22 Feb 2012 14:51:41 +0100 Message-Id: <1329918701-16329-1-git-send-email-javier.martin@vista-silicon.com> X-Mailer: git-send-email 1.7.0.4 X-Gm-Message-State: ALoCoQmyQEJpgNKg2GP9XtaAsTRg6CcPWEJYtT14H4eCmWyumXN511jhNzT2BI9Xw+cVUfiRBBRS Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 5.6.1.2065439, Antispam-Engine: 2.7.2.376379, Antispam-Data: 2012.2.22.134215 X-PMX-Spam: Gauge=IIIIIIII, Probability=8%, Report=' MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, __ANY_URI 0, __CP_MEDIA_BODY 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __MIME_TEXT_ONLY 0, __MIME_VERSION 0, __MULTIPLE_RCPTS_CC_X2 0, __SANE_MSGID 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_PATH 0, __URI_NO_WWW 0, __URI_NS ' If the attached video sensor cannot provide the requested image size, try to use resizing engine included in the eMMa-PrP IP. This patch supports both averaging and bilinear algorithms. Signed-off-by: Javier Martin --- Changes since v1: - Fix several cosmetic issues. - Don't return -EINVAL if resizing is not possible. - Only allow resizing for YUYV format at the moment. - Simplify some lines of code. --- drivers/media/video/mx2_camera.c | 256 +++++++++++++++++++++++++++++++++++++- 1 files changed, 252 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index fcb6b3f..453ad4c 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -204,10 +205,25 @@ #define PRP_INTR_LBOVF (1 << 7) #define PRP_INTR_CH2OVF (1 << 8) +/* Resizing registers */ +#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) +#define PRP_RZ_VALID_BILINEAR (1 << 31) + #define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma) #define MAX_VIDEO_MEM 16 +#define RESIZE_NUM_MIN 1 +#define RESIZE_NUM_MAX 20 +#define BC_COEF 3 +#define SZ_COEF (1 << BC_COEF) + +#define RESIZE_DIR_H 0 +#define RESIZE_DIR_V 1 + +#define RESIZE_ALGO_BILINEAR 0 +#define RESIZE_ALGO_AVERAGING 1 + struct mx2_prp_cfg { int channel; u32 in_fmt; @@ -217,6 +233,13 @@ struct mx2_prp_cfg { u32 irq_flags; }; +/* prp resizing parameters */ +struct emma_prp_resize { + int algo; /* type of algorithm used */ + int len; /* number of coefficients */ + unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ +}; + /* prp configuration for a client-host fmt pair */ struct mx2_fmt_cfg { enum v4l2_mbus_pixelcode in_fmt; @@ -278,6 +301,8 @@ struct mx2_camera_dev { dma_addr_t discard_buffer_dma; size_t discard_size; struct mx2_fmt_cfg *emma_prp; + struct emma_prp_resize resizing[2]; + unsigned int s_width, s_height; u32 frame_count; struct vb2_alloc_ctx *alloc_ctx; }; @@ -687,7 +712,7 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *prp = pcdev->emma_prp; - writel((icd->user_width << 16) | icd->user_height, + writel((pcdev->s_width << 16) | pcdev->s_height, pcdev->base_emma + PRP_SRC_FRAME_SIZE); writel(prp->cfg.src_pixel, pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); @@ -707,6 +732,74 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); } +static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) +{ + int dir; + + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + unsigned char *s = pcdev->resizing[dir].s; + int len = pcdev->resizing[dir].len; + unsigned int coeff[2] = {0, 0}; + unsigned int valid = 0; + int i; + + if (len == 0) + continue; + + for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { + int j; + + j = i > 9 ? 1 : 0; + coeff[j] = (coeff[j] << BC_COEF) | + (s[i] & (SZ_COEF - 1)); + + if (i == 5 || i == 15) + coeff[j] <<= 1; + + valid = (valid << 1) | (s[i] >> BC_COEF); + } + + valid |= PRP_RZ_VALID_TBL_LEN(len); + + if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) + valid |= PRP_RZ_VALID_BILINEAR; + + if (pcdev->emma_prp->cfg.channel == 1) { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_VERT_VALID); + } + } else { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_VERT_VALID); + } + } + } +} + static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) { struct soc_camera_device *icd = soc_camera_from_vb2q(q); @@ -773,6 +866,8 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) list_add_tail(&pcdev->buf_discard[1].queue, &pcdev->discard); + mx2_prp_resize_commit(pcdev); + mx27_camera_emma_buf_init(icd, bytesperline); if (prp->cfg.channel == 1) { @@ -1059,6 +1154,119 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, return formats; } +static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, + struct v4l2_mbus_framefmt *mf_in, + struct v4l2_pix_format *pix_out) +{ + int num, den; + unsigned long m; + int i, dir; + + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + unsigned char *s = pcdev->resizing[dir].s; + int len = 0; + int in, out; + + if (dir == RESIZE_DIR_H) { + in = mf_in->width; + out = pix_out->width; + } else { + in = mf_in->height; + out = pix_out->height; + } + + if (in < out) + return -EINVAL; + else if (in == out) + continue; + + /* Calculate ratio */ + m = gcd(in, out); + num = in / m; + den = out / m; + if (num > RESIZE_NUM_MAX) + return -EINVAL; + + if ((num >= 2 * den) && (den == 1) && + (num < 9) && (!(num & 0x01))) { + int sum = 0; + int j; + + /* Average scaling for >= 2:1 ratios */ + /* Support can be added for num >=9 and odd values */ + + pcdev->resizing[dir].algo = RESIZE_ALGO_AVERAGING; + len = num; + + for (i = 0; i < (len / 2); i++) + s[i] = 8; + + do { + for (i = 0; i < (len / 2); i++) { + s[i] = s[i] >> 1; + sum = 0; + for (j = 0; j < (len / 2); j++) + sum += s[j]; + if (sum == 4) + break; + } + } while (sum != 4); + + for (i = (len / 2); i < len; i++) + s[i] = s[len - i - 1]; + + s[len - 1] |= SZ_COEF; + } else { + /* bilinear scaling for < 2:1 ratios */ + int v; /* overflow counter */ + int coeff, nxt; /* table output */ + int in_pos_inc = 2 * den; + int out_pos = num; + int out_pos_inc = 2 * num; + int init_carry = num - den; + int carry = init_carry; + + pcdev->resizing[dir].algo = RESIZE_ALGO_BILINEAR; + v = den + in_pos_inc; + do { + coeff = v - out_pos; + out_pos += out_pos_inc; + carry += out_pos_inc; + for (nxt = 0; v < out_pos; nxt++) { + v += in_pos_inc; + carry -= in_pos_inc; + } + + if (len > RESIZE_NUM_MAX) + return -EINVAL; + + coeff = ((coeff << BC_COEF) + + (in_pos_inc >> 1)) / in_pos_inc; + + if (coeff >= (SZ_COEF - 1)) + coeff--; + + coeff |= SZ_COEF; + s[len] = (unsigned char)coeff; + len++; + + for (i = 1; i < nxt; i++) { + if (len >= RESIZE_NUM_MAX) + return -EINVAL; + s[len] = 0; + len++; + } + } while (carry != init_carry); + } + pcdev->resizing[dir].len = len; + if (dir == RESIZE_DIR_H) + mf_in->width = pix_out->width; + else + mf_in->height = pix_out->height; + } + return 0; +} + static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -1070,6 +1278,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt mf; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { dev_warn(icd->parent, "Format %x not found\n", @@ -1087,6 +1298,22 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, if (ret < 0 && ret != -ENOIOCTLCMD) return ret; + /* Store width and height returned by the sensor for resizing */ + pcdev->s_width = mf.width; + pcdev->s_height = mf.height; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.code != xlate->code) return -EINVAL; @@ -1096,9 +1323,8 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; - if (mx27_camera_emma(pcdev)) - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); return 0; } @@ -1111,9 +1337,14 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; __u32 pixfmt = pix->pixelformat; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; unsigned int width_limit; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { dev_warn(icd->parent, "Format %x not found\n", pixfmt); @@ -1163,6 +1394,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + /* If the sensor does not support image size try PrP resizing */ + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.field == V4L2_FIELD_ANY) mf.field = V4L2_FIELD_NONE; /* @@ -1181,6 +1426,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->field = mf.field; pix->colorspace = mf.colorspace; + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + return 0; }