summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ov7670.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov7670.c')
-rw-r--r--drivers/media/i2c/ov7670.c136
1 files changed, 118 insertions, 18 deletions
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 88e64739fefd..dea2917a2b12 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 */
@@ -203,6 +211,9 @@ struct ov7670_devtype {
/* formats supported for each model */
struct ov7670_win_size *win_sizes;
unsigned int n_win_sizes;
+ /* callbacks for frame rate control */
+ int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
+ void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
};
/*
@@ -739,6 +750,98 @@ static struct ov7670_win_size ov7675_win_sizes[] = {
}
};
+static void ov7675_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 ov7675_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 */
+ ov7675_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);
+}
+
+static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd,
+ struct v4l2_fract *tpf)
+{
+ struct ov7670_info *info = to_state(sd);
+
+ tpf->numerator = 1;
+ tpf->denominator = info->clock_speed;
+ if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
+ tpf->denominator /= (info->clkrc & CLK_SCALE);
+}
+
+static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd,
+ struct v4l2_fract *tpf)
+{
+ struct ov7670_info *info = to_state(sd);
+ int div;
+
+ 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);
+}
+
/*
* Store a set of start/stop values into the camera.
*/
@@ -913,10 +1016,8 @@ 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);
+ info->devtype->get_framerate(sd, &cp->timeperframe);
+
return 0;
}
@@ -925,25 +1026,13 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
struct v4l2_captureparm *cp = &parms->parm.capture;
struct v4l2_fract *tpf = &cp->timeperframe;
struct ov7670_info *info = to_state(sd);
- int div;
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
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);
+ return info->devtype->set_framerate(sd, tpf);
}
@@ -1560,16 +1649,21 @@ static const struct ov7670_devtype ov7670_devdata[] = {
[MODEL_OV7670] = {
.win_sizes = ov7670_win_sizes,
.n_win_sizes = ARRAY_SIZE(ov7670_win_sizes),
+ .set_framerate = ov7670_set_framerate_legacy,
+ .get_framerate = ov7670_get_framerate_legacy,
},
[MODEL_OV7675] = {
.win_sizes = ov7675_win_sizes,
.n_win_sizes = ARRAY_SIZE(ov7675_win_sizes),
+ .set_framerate = ov7675_set_framerate,
+ .get_framerate = ov7675_get_framerate,
},
};
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;
@@ -1611,7 +1705,13 @@ static int ov7670_probe(struct i2c_client *client,
info->devtype = &ov7670_devdata[id->driver_data];
info->fmt = &ov7670_formats[0];
info->sat = 128; /* Review this */
- info->clkrc = info->clock_speed / 30;
+ info->clkrc = 0;
+
+ /* Set default frame rate to 30 fps */
+ tpf.numerator = 1;
+ tpf.denominator = 30;
+ info->devtype->set_framerate(sd, &tpf);
+
return 0;
}