@@ -16,6 +16,7 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
+#include <linux/dmi.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-mediabus.h>
@@ -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);
}