From patchwork Wed Sep 26 09:47:55 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javier Martin X-Patchwork-Id: 1508741 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 400FD40B1E for ; Wed, 26 Sep 2012 09:48:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754342Ab2IZJsV (ORCPT ); Wed, 26 Sep 2012 05:48:21 -0400 Received: from mail-wi0-f172.google.com ([209.85.212.172]:57306 "EHLO mail-wi0-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753739Ab2IZJsS (ORCPT ); Wed, 26 Sep 2012 05:48:18 -0400 Received: by wibhq12 with SMTP id hq12so4654977wib.1 for ; Wed, 26 Sep 2012 02:48:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=pVzbumqZcgGOfdHARbLZdCGP4bZ8Oj2XaFeDvBIIpEc=; b=MOFXy5bK35QaBvIq1lp31rEqeH0rAXF4ixMOkd227Ux7xtBLwTcWlgUv1G3d4cRO7i L7Cpo06zIcMm6Id5B0Wl6tWMGBEuLVKIiPLr/mZ8JWk8kTCaqsD25J5v9F6cjKD2KNJE +l4AN0z3ROJEdxYUBSD3PWS6KbEz4EjQTuQGuU0JUHRlG9U/XuCNvYKJpDcbkq850g9a rUIIh5CcpXrbx2lCrJI7Pc3WLoFEU2++X5lVI32biwFB/g8+1RADmw5NfowiyVZRltfK IdmcF67nVNGF8XFT2IpE+EHGsSbnEokizfrBpywPDu7E0apRPsafUN4/DU/JGUBQB4RV Wdew== Received: by 10.180.76.69 with SMTP id i5mr86668wiw.9.1348652897565; Wed, 26 Sep 2012 02:48:17 -0700 (PDT) Received: from piscis.vsilicon.net (122.251.106.212.dynamic.jazztel.es. [212.106.251.122]) by mx.google.com with ESMTPS id bn7sm5826230wib.8.2012.09.26.02.48.16 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 26 Sep 2012 02:48:17 -0700 (PDT) From: Javier Martin To: linux-media@vger.kernel.org Cc: corbet@lwn.net, mchehab@infradead.org, hverkuil@xs4all.nl, Javier Martin Subject: [PATCH 3/5] media: ov7670: calculate framerate properly for ov7675. Date: Wed, 26 Sep 2012 11:47:55 +0200 Message-Id: <1348652877-25816-4-git-send-email-javier.martin@vista-silicon.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1348652877-25816-1-git-send-email-javier.martin@vista-silicon.com> References: <1348652877-25816-1-git-send-email-javier.martin@vista-silicon.com> X-Gm-Message-State: ALoCoQlZBtByGZDojHeyuaS4NFyeyewZgSNwbQhKN+gYPRT5bZAS+Wz02zKmkd4sga3vglG1rYLl Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org According to the datasheet ov7675 uses a formula to achieve the desired framerate that is different from the operations done in the current code. In fact, this formula should apply to ov7670 too. This would mean that current code is wrong but, in order to preserve compatibility, the new formula will be used for ov7675 only. Signed-off-by: Javier Martin --- drivers/media/i2c/ov7670.c | 122 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 627fe5f..175fbfc 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -47,6 +47,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); */ #define OV7670_I2C_ADDR 0x42 +#define PLL_FACTOR 4 + /* Registers */ #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ @@ -164,6 +166,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_GFIX 0x69 /* Fix gain control */ +#define REG_DBLV 0x6b /* PLL control an debugging */ +#define DBLV_BYPASS 0x00 /* Bypass PLL */ +#define DBLV_X4 0x01 /* clock x4 */ +#define DBLV_X6 0x10 /* clock x6 */ +#define DBLV_X8 0x11 /* clock x8 */ + #define REG_REG76 0x76 /* OV's name */ #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ #define R76_WHTPCOR 0x40 /* White pixel correction enable */ @@ -765,6 +773,67 @@ static struct ov7670_win_size ov7670_win_sizes[2][4] = { } }; +static void ov7670_get_framerate(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + u32 clkrc = info->clkrc; + u32 pll_factor = PLL_FACTOR; + + clkrc++; + if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) + clkrc = (clkrc >> 1); + + tpf->numerator = 1; + tpf->denominator = (5 * pll_factor * info->clock_speed) / + (4 * clkrc); +} + +static int ov7670_set_framerate(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + u32 clkrc; + u32 pll_factor = PLL_FACTOR; + int ret; + + /* + * The formula is fps = 5/4*pixclk for YUV/RGB and + * fps = 5/2*pixclk for RAW. + * + * pixclk = clock_speed / (clkrc + 1) * PLLfactor + * + */ + if (tpf->numerator == 0 || tpf->denominator == 0) { + clkrc = 0; + } else { + clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / + (4 * tpf->denominator); + if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) + clkrc = (clkrc << 1); + clkrc--; + } + + /* + * The datasheet claims that clkrc = 0 will divide the input clock by 1 + * but we've checked with an oscilloscope that it divides by 2 instead. + * So, if clkrc = 0 just bypass the divider. + */ + if (clkrc <= 0) + clkrc = CLK_EXT; + else if (clkrc > CLK_SCALE) + clkrc = CLK_SCALE; + info->clkrc = clkrc; + + /* Recalculate frame rate */ + ov7670_get_framerate(sd, tpf); + + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + if (ret < 0) + return ret; + return ov7670_write(sd, REG_DBLV, DBLV_X4); +} + /* * Store a set of start/stop values into the camera. */ @@ -939,10 +1008,15 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = info->clock_speed; - if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); + if (info->model == MODEL_OV7670) { + /* legacy */ + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = info->clock_speed; + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); + } else { + ov7670_get_framerate(sd, &cp->timeperframe); + } return 0; } @@ -958,18 +1032,23 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) if (cp->extendedmode != 0) return -EINVAL; - if (tpf->numerator == 0 || tpf->denominator == 0) - div = 1; /* Reset to full rate */ - else - div = (tpf->numerator * info->clock_speed) / tpf->denominator; - if (div == 0) - div = 1; - else if (div > CLK_SCALE) - div = CLK_SCALE; - info->clkrc = (info->clkrc & 0x80) | div; - tpf->numerator = 1; - tpf->denominator = info->clock_speed / div; - return ov7670_write(sd, REG_CLKRC, info->clkrc); + if (info->model == MODEL_OV7670) { + /* legacy */ + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator * info->clock_speed) / tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + info->clkrc = (info->clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = info->clock_speed / div; + return ov7670_write(sd, REG_CLKRC, info->clkrc); + } else { + return ov7670_set_framerate(sd, tpf); + } } @@ -1585,6 +1664,7 @@ static const struct v4l2_subdev_ops ov7670_ops = { static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_fract tpf; struct v4l2_subdev *sd; struct ov7670_info *info; int ret; @@ -1626,7 +1706,15 @@ static int ov7670_probe(struct i2c_client *client, info->model = id->driver_data; info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ - info->clkrc = info->clock_speed / 30; + /* Set default frame rate to 30 fps */ + if (info->model == MODEL_OV7670) { + /* legacy */ + info->clkrc = info->clock_speed / 30; + } else { + tpf.numerator = 1; + tpf.denominator = 30; + ov7670_set_framerate(sd, &tpf); + } return 0; }