From patchwork Fri Oct 8 21:04:32 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Drake X-Patchwork-Id: 242571 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o98L5kUR007398 for ; Fri, 8 Oct 2010 21:05:46 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759655Ab0JHVFn (ORCPT ); Fri, 8 Oct 2010 17:05:43 -0400 Received: from mtaout01-winn.ispmail.ntl.com ([81.103.221.47]:22980 "EHLO mtaout01-winn.ispmail.ntl.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1759611Ab0JHVFm (ORCPT ); Fri, 8 Oct 2010 17:05:42 -0400 Received: from aamtaout03-winn.ispmail.ntl.com ([81.103.221.35]) by mtaout01-winn.ispmail.ntl.com (InterMail vM.7.08.04.00 201-2186-134-20080326) with ESMTP id <20101008210438.FDWL3266.mtaout01-winn.ispmail.ntl.com@aamtaout03-winn.ispmail.ntl.com>; Fri, 8 Oct 2010 22:04:38 +0100 Received: from zog.reactivated.net ([86.14.215.141]) by aamtaout03-winn.ispmail.ntl.com (InterMail vG.2.02.00.01 201-2161-120-102-20060912) with ESMTP id <20101008210435.SEMD1807.aamtaout03-winn.ispmail.ntl.com@zog.reactivated.net>; Fri, 8 Oct 2010 22:04:35 +0100 Received: by zog.reactivated.net (Postfix, from userid 1000) id 126649D401B; Fri, 8 Oct 2010 22:04:33 +0100 (BST) From: Daniel Drake To: corbet@lwn.net To: mchehab@infradead.org Cc: linux-media@vger.kernel.org Subject: [PATCH 3/3] ov7670: Support customization of clock speed Message-Id: <20101008210433.126649D401B@zog.reactivated.net> Date: Fri, 8 Oct 2010 22:04:32 +0100 (BST) X-Cloudmark-Analysis: v=1.1 cv=4QByPj+6Iq2k/6L54d+eVKTdgQxdscpRskJJReCfdXo= c=1 sm=0 a=r4Azqkxo7PMA:10 a=Op-mwl0xAAAA:8 a=6wXkpFXQMbkomtTpViYA:9 a=1exxbdspJcd6ODhpTs4A:7 a=K46ERg0aMYRJyuHigTM1UfbYnz0A:4 a=d4CUUju0HPYA:10 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Fri, 08 Oct 2010 21:05:47 +0000 (UTC) diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 9fffcdd..c4d9ed0 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,19 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* + * What is our fastest frame rate? It's a function of how the chip + * is clocked, and this is an external clock, so we don't know. If we have + * a DMI entry describing the platform, use it. If not, assume 30. In both + * cases, accept override from a module parameter. + */ +static int clock_speed = 30; +static bool clock_speed_from_param = false; +static int set_clock_speed_from_param(const char *val, struct kernel_param *kp); +module_param_call(clock_speed, set_clock_speed_from_param, param_get_int, + &clock_speed, 0440); +MODULE_PARM_DESC(clock_speed, "External clock speed (Hz)"); + +/* * Basic window sizes. These probably belong somewhere more globally * useful. */ @@ -43,11 +57,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define QCIF_HEIGHT 144 /* - * Our nominal (default) frame rate. - */ -#define OV7670_FRAME_RATE 30 - -/* * The 7670 sits on i2c with ID 0x42 */ #define OV7670_I2C_ADDR 0x42 @@ -188,6 +197,44 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define REG_BD60MAX 0xab /* 60hz banding step limit */ +static int set_clock_speed_from_param(const char *val, struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + if (ret == 0) + clock_speed_from_param = true; + return ret; +} + +static int __init set_clock_speed_from_dmi(const struct dmi_system_id *dmi) +{ + if (clock_speed_from_param) + return 0; /* module param beats DMI */ + + clock_speed = (int) dmi->driver_data; + return 0; +} + +static const struct dmi_system_id __initconst dmi_clock_speeds[] = { + { + .callback = set_clock_speed_from_dmi, + .driver_data = (void *) 45, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OLPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "XO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1"), + }, + }, + { + .callback = set_clock_speed_from_dmi, + .driver_data = (void *) 90, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OLPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "XO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.5"), + }, + }, + { } +}; /* * Information we maintain about a known sensor. @@ -861,7 +908,7 @@ 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 = OV7670_FRAME_RATE; + cp->timeperframe.denominator = clock_speed; if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); return 0; @@ -882,14 +929,14 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ else - div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; + div = (tpf->numerator*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 = OV7670_FRAME_RATE/div; + tpf->denominator = clock_speed/div; return ov7670_write(sd, REG_CLKRC, info->clkrc); } @@ -1510,10 +1557,15 @@ static int ov7670_probe(struct i2c_client *client, } v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); + /* + * Make sure the clock speed is rational. + */ + if (clock_speed < 1 || clock_speed > 100) + clock_speed = 30; info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ - info->clkrc = 1; /* 30fps */ + info->clkrc = clock_speed / 30; return 0; } @@ -1546,6 +1598,7 @@ static struct i2c_driver ov7670_driver = { static __init int init_ov7670(void) { + dmi_check_system(dmi_clock_speeds); return i2c_add_driver(&ov7670_driver); }