V4L/DVB (4909): Add s/g_parm to cafe_ccic

Add s/g_parm support allowing applications to tweak the frame rate.

Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 9d9844e..e347c7e 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -1671,6 +1671,37 @@
 	return 0;
 }
 
+/*
+ * G/S_PARM.  Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int cafe_vidioc_g_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+static int cafe_vidioc_s_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+
 static void cafe_v4l_dev_release(struct video_device *vd)
 {
 	struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev);
@@ -1724,7 +1755,8 @@
 	.vidioc_queryctrl	= cafe_vidioc_queryctrl,
 	.vidioc_g_ctrl		= cafe_vidioc_g_ctrl,
 	.vidioc_s_ctrl		= cafe_vidioc_s_ctrl,
-	/* Do cropping someday */
+	.vidioc_g_parm		= cafe_vidioc_g_parm,
+	.vidioc_s_parm		= cafe_vidioc_s_parm,
 };
 
 
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 7d380d7..89dd18c3 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -36,6 +36,11 @@
 #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
@@ -291,7 +296,7 @@
 	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
 	{ 0x56, 0x40 },
 
-	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP },
+	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
 	{ 0xa4, 0x88 },		{ 0x96, 0 },
 	{ 0x97, 0x30 },		{ 0x98, 0x20 },
 	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
@@ -722,6 +727,63 @@
 }
 
 /*
+ * Implement G/S_PARM.  There is a "high quality" mode we could try
+ * to do someday; for now, we just do the frame rate tweak.
+ */
+static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	unsigned char clkrc;
+	int ret;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	ret = ov7670_read(c, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+	memset(cp, 0, sizeof(struct v4l2_captureparm));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = 1;
+	cp->timeperframe.denominator = OV7670_FRAME_RATE;
+	if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1)
+		cp->timeperframe.denominator /= (clkrc & CLK_SCALE);
+	return 0;
+}
+
+static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	unsigned char clkrc;
+	int ret, div;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+	/*
+	 * CLKRC has a reserved bit, so let's preserve it.
+	 */
+	ret = ov7670_read(c, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		div = 1;  /* Reset to full rate */
+	else
+		div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator;
+	if (div == 0)
+		div = 1;
+	else if (div > CLK_SCALE)
+		div = CLK_SCALE;
+	clkrc = (clkrc & 0x80) | div;
+	tpf->numerator = 1;
+	tpf->denominator = OV7670_FRAME_RATE/div;
+	return ov7670_write(c, REG_CLKRC, clkrc);
+}
+
+
+
+/*
  * Code for dealing with controls.
  */
 
@@ -1231,10 +1293,10 @@
 		return ov7670_s_ctrl(client, (struct v4l2_control *) arg);
 	case VIDIOC_G_CTRL:
 		return ov7670_g_ctrl(client, (struct v4l2_control *) arg);
-	/* Todo:
-	   g/s_parm
-	   initialization
-	*/
+	case VIDIOC_S_PARM:
+		return ov7670_s_parm(client, (struct v4l2_streamparm *) arg);
+	case VIDIOC_G_PARM:
+		return ov7670_g_parm(client, (struct v4l2_streamparm *) arg);
 	}
 	return -EINVAL;
 }