Here's a little cafe camera patch:

Jonathan Corbet corbet at lwn.unroutablenet
Tue Dec 5 13:09:37 EST 2006


Commit:     5a9f682f7b0560b3c1e46518d11268f4029c7341
Parent:     b9e5120610df36b708e5574fb8ebd47063a7a82a
commit 5a9f682f7b0560b3c1e46518d11268f4029c7341
Author:     Jonathan Corbet <corbet at lwn.net>
AuthorDate: Tue Dec 5 13:14:21 2006 -0500
Commit:     Andres Salomon <dilinger at debian.org>
CommitDate: Tue Dec 5 13:14:21 2006 -0500

    Here's a little cafe camera patch:
    
     - Adds support for VIDIOC_S_PARM and VIDIOC_G_PARM.  That, in turn,
       allows an application to control the video frame rate.
    
     - Remove the faked RGB32 format.  It was useful for debugging, but the
       driver won't get into the mainline with kernel-space reformatting
       code.
    
    There's also a hack to make it easier to manipulate ov7670 registers
    directly from the command line; that will probably come out eventually.
---
 drivers/media/video/cafe_ccic.c |   87 +++++++++++++-------------------
 drivers/media/video/ov7670.c    |  106 +++++++++++++++++++++++++++++++--------
 2 files changed, 120 insertions(+), 73 deletions(-)

diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 67c2478..3d83348 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -618,16 +618,6 @@ static void cafe_ctlr_image(struct cafe_
 			    C0_DF_MASK);
 	    break;
 
-	/*
-	 * For "fake rgb32" get the image pitch right.
-	 */
-	case V4L2_PIX_FMT_RGB32:
-	    cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline/2,
-			    IMGP_YP_MASK);
-	    imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
-		    ((fmt->bytesperline/2) & IMGSZ_H_MASK);
-	    cafe_reg_write(cam, REG_IMGSIZE, imgsz);
-	    /* fall into ... */
 	case V4L2_PIX_FMT_RGB444:
 	    cafe_reg_write_mask(cam, REG_CTRL0,
 			    C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
@@ -908,11 +898,8 @@ static int cafe_alloc_dma_bufs(struct ca
 	cafe_set_config_needed(cam, 1);
 	if (loadtime)
 		cam->dma_buf_size = dma_buf_size;
-	else {
+	else
 		cam->dma_buf_size = cam->pix_format.sizeimage;
-		if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32)
-			cam->dma_buf_size /= 2;
-	}
 	if (n_dma_bufs > 3)
 		n_dma_bufs = 3;
 
@@ -1690,6 +1677,37 @@ static int cafe_vidioc_s_std(struct file
 	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;
+}
+
+
 
 /*
  * The TV Norm stuff is weird - we're a camera with little to do with TV,
@@ -1757,7 +1775,8 @@ static struct video_device cafe_v4l_temp
 	.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,
 };
 
 
@@ -1771,37 +1790,6 @@ static struct video_device cafe_v4l_temp
  * Interrupt handler stuff
  */
 
-/*
- * Create RGB32 from RGB444 so it can be displayed before the applications
- * know about the latter format.
- */
-static void cafe_fake_rgb32(struct cafe_camera *cam, char *dest, char *src)
-{
-	int i;
-	u16 *ssrc = (u16 *) src;
-
-	/* RGB444 version */
-	for (i = 0; i < cam->pix_format.sizeimage; i += 4) {
-	//		dest[0] = (*ssrc & 0xf000) >> 8;
-		dest[0] = (*ssrc & 0x000f) << 4;
-		dest[1] = (*ssrc & 0x00f0);
-		dest[2] = (*ssrc & 0x0f00) >> 4;
-		dest[3] = (*ssrc & 0xf000);   /* Alpha */
-		dest += 4;
-		ssrc++;
-	}
-#if 0
-	/* RGB565 version */
-	for (i = 0; i < cam->pix_format.sizeimage; i += 4) {
-		dest[0] = (*ssrc & 0xf800) >> 8;
-		dest[1] = (*ssrc & 0x07e0) >> 3;
-		dest[2] = (*ssrc & 0x001f) << 3;
-		dest[3] = 0;
-		dest += 4;
-		ssrc++;
-	}
-#endif
-}
 
 
 static void cafe_frame_tasklet(unsigned long data)
@@ -1831,11 +1819,8 @@ static void cafe_frame_tasklet(unsigned 
 		 */
 		sbuf = list_entry(cam->sb_avail.next,
 				struct cafe_sio_buffer, list);
-		if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32)
-			cafe_fake_rgb32(cam, sbuf->buffer, cam->dma_bufs[bufno]);
-		else
-			memcpy(sbuf->buffer, cam->dma_bufs[bufno],
-					cam->pix_format.sizeimage);
+		memcpy(sbuf->buffer, cam->dma_bufs[bufno],
+				cam->pix_format.sizeimage);
 		sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
 		sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
 		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 7206b8e..4fceabd 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -36,6 +36,11 @@ #define QCIF_WIDTH	176
 #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 @@ static struct regval_list ov7670_default
 	{ 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 },
@@ -518,19 +523,6 @@ static struct ov7670_format_struct {
 		.regs		= ov7670_fmt_rgb565,
 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
 	},
-	/*
-	 * Pretend we do RGB32.  This is here on the assumption that the
-	 * upper layer will reformat RGB444 appropriately.
-	 *
-	 * The entire purpose for this thing's existence is to enable easy
-	 * display of RGB444 for debugging purposes.  It will come out soon.
-	 */
-	{
-		.desc		= "RGB32 (faked)",
-		.pixelformat	= V4L2_PIX_FMT_RGB32,
-		.regs		= ov7670_fmt_rgb444,
-		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
-	},
 };
 #define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
 
@@ -715,8 +707,6 @@ static int ov7670_try_fmt(struct i2c_cli
 	pix->width = wsize->width;
 	pix->height = wsize->height;
 	pix->bytesperline = pix->width*BYTES_PER_PIXEL;
-	if (pix->pixelformat == V4L2_PIX_FMT_RGB32)
-		pix->bytesperline *= 2;
 	pix->sizeimage = pix->height*pix->bytesperline;
 	return 0;
 }
@@ -758,6 +748,63 @@ static int ov7670_s_fmt(struct i2c_clien
 }
 
 /*
+ * 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.
  */
 
@@ -1171,9 +1218,19 @@ static int ov7670_queryctrl(struct i2c_c
 
 static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
 {
-	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+	struct ov7670_control *octrl;
 	int ret;
 
+	if (ctrl->id > V4L2_CID_PRIVATE_BASE) {
+		unsigned char reg = ctrl->id & 0xff;
+		unsigned char v;
+		
+		ret = ov7670_read(client, reg, &v);
+		if (ret >= 0)
+			ctrl->value = v;
+		return ret;
+	}
+	octrl = ov7670_find_control(ctrl->id);
 	if (octrl == NULL)
 		return -EINVAL;
 	ret = octrl->query(client, &ctrl->value);
@@ -1184,9 +1241,14 @@ static int ov7670_g_ctrl(struct i2c_clie
 
 static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
 {
-	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+	struct ov7670_control *octrl;
 	int ret;
 
+	if (ctrl->id > V4L2_CID_PRIVATE_BASE) {
+		unsigned char reg = ctrl->id & 0xff;
+		return ov7670_write(client, reg, ctrl->value);
+	}
+	octrl = ov7670_find_control(ctrl->id);
 	if (octrl == NULL)
 		return -EINVAL;
 	ret =  octrl->tweak(client, ctrl->value);
@@ -1289,10 +1351,10 @@ static int ov7670_command(struct i2c_cli
 		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;
 }


More information about the Commits-kernel mailing list