Update from Jonathon for the camera driver.

Andres Salomon dilinger at debian.org
Tue Nov 7 22:37:12 EST 2006


Commit:     ed322a452160f16f807941b262a26fdd16b937f7
Parent:     5851cc8ffe8818200d1613a6463d011475b2d6cb
commit ed322a452160f16f807941b262a26fdd16b937f7
Author:     Andres Salomon <dilinger at debian.org>
AuthorDate: Tue Oct 17 19:20:06 2006 -0400
Commit:     Andres Salomon <dilinger at debian.org>
CommitDate: Tue Oct 17 19:20:06 2006 -0400

    Update from Jonathon for the camera driver.
---
 Documentation/video4linux/m88alp01-ccic |   41 ++++--
 drivers/media/video/m88alp01-ccic.c     |  152 ++++++++++++++-------
 drivers/media/video/m88alp01-regs.h     |   18 +-
 drivers/media/video/ovcamchip/ov7670.c  |  227 +++++++++++++++++++++++++++++--
 include/linux/videodev2.h               |    1 
 5 files changed, 352 insertions(+), 87 deletions(-)

diff --git a/Documentation/video4linux/m88alp01-ccic b/Documentation/video4linux/m88alp01-ccic
index 8386d9d..0c682a8 100644
--- a/Documentation/video4linux/m88alp01-ccic
+++ b/Documentation/video4linux/m88alp01-ccic
@@ -9,10 +9,6 @@ to go along with it...
    found in the draft spec.  If we want to change its name (and Marvell
    could well change theirs) now is probably the right time to do so.
 
- - Once patched into the kernel, it should build and load just fine.
-   There's a number of tweakable parameters, but there should be no need to
-   mess with them.
-
  - It is rock-solid with mplayer.  The command I use is:
 
      mplayer tv:// -tv driver=v4l2:width=640:height=480 -nosound
@@ -22,18 +18,35 @@ to go along with it...
    contrast, '3' and '4' for brightness); no other controls are hooked up
    yet. 
 
- - It does not behave at all well with gqcam - which appears to be trying
-   to use the read() method.  There is some residual weirdness with read()
-   that I've not yet had a chance to track down.
+ - Also supported are the RGB565 and RGB444 formats.  RGB565 can be
+   displayed directly with a command like:
 
- - Only VGA YUV video is supported so far.  Other formats will come soon.
+     mplayer tv:// -tv driver=v4l2:width=640:height=480:outfmt=bgr16 -nosound
 
- - I'm mostly unashamed to show the code, but there are some remaining
-   issues.  The biggest has to do with the use of the "ovcamchip" layer,
-   which was really meant for a very different sort of device.  Much of
-   stuff which should really be dealt with at the lower layer (so that the
-   Cafe could work with sensors other than the OV7670) is currently "known"
-   at the Cafe level instead.  It is my intent to fix that.
+   No application currently knows how to do RGB444, so the driver currently
+   implements a fake RGB32 mode generated from RGB444. V4L2 drivers are not
+   supposed to do video format transformations, so this options will
+   probably come out eventually, but it is useful for testing now; use
+   outfmt=rgb32 with mplayer to get this mode.
+
+ - It does not behave at all well with gqcam - which appears to be trying
+   to use the read() method.  There is some residual weirdness with read()
+   that I've not yet had a chance to track down.  Xawtv also does strange
+   stuff. 
+
+ - This driver uses the "ovcamchip" layer because it was convenient, but it
+   is at most a marriage of convenience.  This layer has traditionally been
+   used to configure OmniVision sensors for a specific back-end processor;
+   here, instead, we want to see the sensor's output data directly.  So the
+   supplied OV7670 driver will not work with the older ovcamchip users, and
+   the other OmniVision sensor drivers are not useful for the m88alp01
+   camera controller.  
+
+   I have made a beginning attempt to shove some V4L2 functionality down
+   into the ovcamchip layer as a way of making it a bit more general, but
+   (1) this work is rough as yet, and (2) I have not been able to talk with
+   the ovcamchip maintainer about how he would like things done.  So
+   changes may be required in this interface.
 
 Please do look it over, try it out, and get back to me with any questions
 or comments.
diff --git a/drivers/media/video/m88alp01-ccic.c b/drivers/media/video/m88alp01-ccic.c
index 8caafd1..ee02102 100644
--- a/drivers/media/video/m88alp01-ccic.c
+++ b/drivers/media/video/m88alp01-ccic.c
@@ -70,7 +70,7 @@ MODULE_PARM_DESC(alloc_bufs_at_load,
 		"those buffers, but at the cost of nailing down the memory from "
 		"the outset.");
 
-static int n_dma_bufs = 3; 
+static int n_dma_bufs = 2; 
 module_param(n_dma_bufs, uint, 0644);
 MODULE_PARM_DESC(n_dma_bufs,
 		"The number of DMA buffers to allocate.  Can be either two "
@@ -619,8 +619,31 @@ static void m88_ctlr_image(struct m88_ca
 			    C0_DF_MASK);
 	    break;
 
+    	/*
+	 * For "fake rgb32" get the image pitch right.
+	 */
+	case V4L2_PIX_FMT_RGB32:
+	    m88_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);
+	    m88_reg_write(cam, REG_IMGSIZE, imgsz);
+    	    /* fall into ... */
+	case V4L2_PIX_FMT_RGB444:
+	    m88_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
+			    C0_DF_MASK);
+    		/* Alpha value? */
+	    break;
+
+	case V4L2_PIX_FMT_RGB565:
+	    m88_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
+			    C0_DF_MASK);
+	    break;
+
 	default:
-	    cam_err(cam, "Unknown format %x", cam->pix_format.pixelformat);
+	    cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
 	    break;
 	}
 	/*
@@ -838,6 +861,7 @@ static int m88_cam_init(struct m88_camer
  */
 static int m88_cam_configure(struct m88_camera *cam)
 {
+#if 0
 	int ret;
 	struct ovcamchip_window win;
 
@@ -848,6 +872,13 @@ static int m88_cam_configure(struct m88_
 	win.height = cam->pix_format.height;
 	ret = __m88_cam_cmd(cam, OVCAMCHIP_CMD_S_MODE, &win);
 	return ret;
+#endif
+	struct v4l2_format fmt;
+	
+	if (cam->state != S_IDLE)
+		return -EINVAL;
+	fmt.fmt.pix = cam->pix_format;
+	return __m88_cam_cmd(cam, VIDIOC_S_FMT, &fmt);
 }
 
 /* -------------------------------------------------------------------- */
@@ -866,8 +897,11 @@ static int m88_alloc_dma_bufs(struct m88
 	m88_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;
 
@@ -972,6 +1006,7 @@ static int m88_read_setup(struct m88_cam
 			return -ENOMEM;
 
 	if (m88_needs_config(cam)) {
+		m88_cam_configure(cam);
 		ret = m88_ctlr_configure(cam);
 		if (ret)
 			return ret;
@@ -1270,7 +1305,11 @@ static int m88_vidioc_qbuf(struct file *
 	if (buf->index < 0 || buf->index >= cam->n_sbufs)
 		goto out;
 	sbuf = cam->sb_bufs + buf->index;
-	if (sbuf->v4lbuf.flags & (V4L2_BUF_FLAG_QUEUED|V4L2_BUF_FLAG_DONE)) {
+	if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) {
+		ret = 0; /* Already queued?? */
+		goto out;
+	}
+	if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) {
 		/* Spec doesn't say anything, seems appropriate tho */
 		ret = -EBUSY;
 		goto out;
@@ -1309,7 +1348,7 @@ static int m88_vidioc_dqbuf(struct file 
 		if (wait_event_interruptible(cam->iowait,
 						!list_empty(&cam->sb_full))) {
 			ret = -ERESTARTSYS;
-			goto out_unlock;
+			goto out;
 		}
 		mutex_lock(&cam->s_mutex);
 	}
@@ -1330,6 +1369,7 @@ static int m88_vidioc_dqbuf(struct file 
 
   out_unlock:
 	mutex_unlock(&cam->s_mutex);
+  out:
 	return ret;
 }
 
@@ -1588,36 +1628,6 @@ static int m88_vidioc_querycap(struct fi
 	return 0;
 }
 
-/*
- * What formats do we support?  More to be added here later,
- * but this will also have to move into the ovcamchip layer.
- */
-
-static struct m88_format
-{
-	__u8 *desc;
-	__u32 pixelformat;
-	int bytes_per_pixel;
-} m88_format_list [] = {
-	{
-		.desc		= "YUYV 4:2:2",
-		.pixelformat	= V4L2_PIX_FMT_YUYV,
-		.bytes_per_pixel = 2
-	},
-};
-
-#define M88_N_FORMATS 1
-		
-		
-/*
- * Image sizes.  For now, stick to the canned sizes offered
- * by the ov7670.  The sensor is more flexible than this, though.
- *
- * This, too, needs to be pushed down into the camera layer.
- */
-static int m88_width_options[] = { 176, 320, 352, 640 };
-static int m88_height_options[] = { 144, 240, 288, 480 };
-#define N_WINDOW_SIZES 4
 
 /*
  * The default format we use until somebody says otherwise.
@@ -1634,18 +1644,16 @@ static struct v4l2_pix_format m88_def_pi
 static int m88_vidioc_enum_fmt_cap(struct file *filp,
 		void *priv, struct v4l2_fmtdesc *fmt)
 {
-	struct m88_format *mfmt;
-	
-	if (fmt->index >= M88_N_FORMATS)
-		return -EINVAL;
+	struct m88_user *user = priv;
+	struct m88_camera *cam = user->cam;
+	int ret;
+
 	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
-
-	mfmt = m88_format_list + fmt->index;
-	fmt->flags = 0;
-	strcpy(fmt->description, mfmt->desc);
-	fmt->pixelformat = mfmt->pixelformat;
-	return 0;
+	mutex_lock(&cam->s_mutex);
+	ret = __m88_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
 }
 
 
@@ -1654,6 +1662,8 @@ static int m88_vidioc_try_fmt_cap (struc
 {
 	struct m88_user *user = priv;
 	struct m88_camera *cam = user->cam;
+
+#if 0
 	int index, sizeindex;
 	struct v4l2_pix_format *pix = &fmt->fmt.pix;
 
@@ -1690,6 +1700,13 @@ static int m88_vidioc_try_fmt_cap (struc
 	pix->bytesperline = pix->width*m88_format_list[index].bytes_per_pixel;
 	pix->sizeimage = pix->height*pix->bytesperline;
 	return 0;
+#endif
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __m88_cam_cmd(cam, VIDIOC_TRY_FMT, fmt);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
 }
 
 static int m88_vidioc_s_fmt_cap(struct file *filp, void *priv,
@@ -1868,6 +1885,39 @@ static struct video_device m88_v4l_templ
  * Interrupt handler stuff
  */
 
+/*
+ * Create RGB32 from RGB444 so it can be displayed before the applications
+ * know about the latter format.
+ */
+static void m88_fake_rgb32(struct m88_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 m88_frame_tasklet(unsigned long data)
 {
 	struct m88_camera *cam = (struct m88_camera *) data;
@@ -1895,8 +1945,12 @@ static void m88_frame_tasklet(unsigned l
 		 */
 		sbuf = list_entry(cam->sb_avail.next,
 				struct m88_sio_buffer, list);
-		memcpy(sbuf->buffer, cam->dma_bufs[bufno],
-				cam->pix_format.sizeimage);
+		if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32)
+			m88_fake_rgb32(cam, sbuf->buffer, cam->dma_bufs[bufno]);
+		else
+			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;
 		sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
@@ -1984,7 +2038,7 @@ static void m88_frame_irq(struct m88_cam
 
 
 
-static irqreturn_t m88_irq(int irq, void *data, struct pt_regs *regs)
+static irqreturn_t m88_irq(int irq, void *data)
 {
 	struct m88_camera *cam = data;
 	unsigned int irqs;
@@ -2195,6 +2249,7 @@ static int m88_pci_probe(struct pci_dev 
 	mutex_lock(&cam->s_mutex);
 	cam->v4ldev = m88_v4l_template;
 	cam->v4ldev.debug = 0;
+//	cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
 	ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
 	if (ret)
 		goto out_smbus;
@@ -2271,6 +2326,7 @@ static void m88_pci_remove(struct pci_de
 static struct pci_device_id m88_ids[] = {
 	{ PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */
 	{ PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
+	{ PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
 	{ 0, }
 };
 
diff --git a/drivers/media/video/m88alp01-regs.h b/drivers/media/video/m88alp01-regs.h
index 9701f17..aaf6b25 100644
--- a/drivers/media/video/m88alp01-regs.h
+++ b/drivers/media/video/m88alp01-regs.h
@@ -44,18 +44,20 @@ #define REG_IMGOFFSET	0x38	/* IMage offs
 
 #define REG_CTRL0	0x3c	/* Control 0 */
 #define   C0_ENABLE	  0x00000001	/* Makes the whole thing go */
-#define	  C0_RGBE_5RGGB	  0x00000000	/* RGB565 rrrrrggg gggbbbbb */
-#define   C0_RGBE_5GRBG	  0x00000004	/* RGB565 gggrrrrr bbbbbggg */
-#define   C0_RGBE_5GBRG	  0x00000008	/* RGB565 gggbbbbb rrrrrggg */
-#define   C0_RGBE_5BGGR	  0x0000000c	/* RGB565 bbbbbggg gggrrrrr */
-#define   C0_RGBE_4RGBX	  0x00000000	/* RGB444 rrrrgggg bbbb.... */
-#define   C0_RGBE_4XRGB	  0x00000004	/* RGB444 ....rrrr ggggbbbb */
-#define   C0_RGBE_4BGRX	  0x00000008	/* RGB444 bbbbgggg rrrrxxxx */
-#define   C0_RGBE_4XBGR	  0x0000000c	/* RGB444 ....bbbb ggggrrrr */
 
 /* Mask for all the format bits */
 #define   C0_DF_MASK	  0x00fffffc    /* Bits 2-23 */
 
+/* RGB ordering */
+#define   C0_RGB4_RGBX	  0x00000000
+#define	  C0_RGB4_XRGB	  0x00000004
+#define	  C0_RGB4_BGRX	  0x00000008
+#define   C0_RGB4_XBGR	  0x0000000c
+#define   C0_RGB5_RGGB	  0x00000000
+#define	  C0_RGB5_GRBG	  0x00000004
+#define	  C0_RGB5_GBRG	  0x00000008
+#define   C0_RGB5_BGGR	  0x0000000c
+
 /* Spec has two fields for DIN and DOUT, but they must match, so
    combine them here. */
 #define   C0_DF_YUV	  0x00000000    /* Data is YUV	    */
diff --git a/drivers/media/video/ovcamchip/ov7670.c b/drivers/media/video/ovcamchip/ov7670.c
index 2c679b5..004113f 100644
--- a/drivers/media/video/ovcamchip/ov7670.c
+++ b/drivers/media/video/ovcamchip/ov7670.c
@@ -16,6 +16,7 @@
 #define DEBUG
 
 #include <linux/slab.h>
+#include <linux/videodev2.h>
 #include "ovcamchip_priv.h"
 
 /* Registers */
@@ -99,6 +100,7 @@ #define REG_COM12	0x3c	/* Control 12 */
 #define   COM12_HREF	  0x80	  /* HREF always */
 #define REG_COM13	0x3d	/* Control 13 */
 #define   COM13_GAMMA	  0x80	  /* Gamma enable */
+#define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
 #define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
 #define REG_COM14	0x3e	/* Control 14 */
 #define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
@@ -107,8 +109,8 @@ #define REG_COM15	0x40	/* Control 15 */
 #define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
 #define	  COM15_R01FE	  0x80	  /*            01 to FE */
 #define   COM15_R00FF	  0xc0	  /*            00 to FF */
-#define   COM15_RBG565	  0x08	  /* RGB565 output */
-#define   COM15_RGB555	  0x18	  /* RGB555 output */
+#define   COM15_RGB565	  0x10	  /* RGB565 output */
+#define   COM15_RGB555	  0x30	  /* RGB555 output */
 #define REG_COM16	0x41	/* Control 16 */
 #define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
 #define REG_COM17	0x42	/* Control 17 */
@@ -127,14 +129,12 @@ #define   R444_RGBX	  0x01	  /* Empty ni
 #define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
 #define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
 
+#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
 #define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
 #define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
 #define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
 #define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
 #define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
-
-#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
-
 #define REG_BD60MAX	0xab	/* 60hz banding step limit */
 
 
@@ -171,13 +171,15 @@ struct ov7670 {
 
 
 /*
- * Register settings.  This seems awfully short, compared to the others,
- * there must be a dozen weird tweaks needed.
+ * The default register settings, as obtained from OmniVision.  There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
  *
- * "OV" indicates settings from Omnivision.
+ * These settings give VGA YUYV.
  */
 static struct ovcamchip_regvals regvals_init_7670[] = {
 	{ REG_COM7, COM7_RESET },
+//	{ 0xff, 0xff },	/* REMOVE ME DAMMIT */
 /*
  * Clock scale: 3 = 15fps
  *              2 = 20fps
@@ -201,14 +203,11 @@ static struct ovcamchip_regvals regvals_
 	{ 0x70, 0x3a },
 	{ 0x71, 0x35 },
 	{ 0x72, 0x11 },
-	{ 0x72, 0xf0 },  /* "reserved" */
-	{ 0xa2, 0x01 },
+	{ 0x73, 0xf0 },  /* "reserved" */
+	{ 0xa2, 0x02 },
 
 /* EXPERIMENT */
 	{ REG_COM10, 0x0 },
-//	{ REG_COM10, 0x40 },
-//	{ 0x30, 0x20 },
-//	{ 0x31, 0x20 },
 	
 	/* Gamma curve values */
 	{ 0x7a, 0x20 },
@@ -371,15 +370,65 @@ static struct ovcamchip_regvals regvals_
 	{ 0x0f, 0x1f },
 
 
-//	{ REG_COM10, 0 }, /* HREF, don't reverse anything */
-				    /* 7620 sets href negative */
-//	{ REG_PSHFT, 0 },  	/* No pixel delay after href */
 //	{ REG_COM11, COM11_HZAUTO }, /* Auto freq detect */
 //	{ REG_COM13, COM13_GAMMA },
 
 	{ 0xff, 0xff },	/* END MARKER */
 };
 
+/*
+ * Here we'll try to encapsulate the changes for just the output
+ * video format.
+ *
+ * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
+ */
+
+static struct ovcamchip_regvals reg_fmt_rgb565[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0x0 },
+	{ REG_COM15, COM15_RGB565 },
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct ovcamchip_regvals reg_fmt_yuv422[] = {
+	{ REG_COM7, 0x0 },  /* Selects YUV mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0 },
+	{ REG_COM15, COM15_R00FF },
+	{ REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
+	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct ovcamchip_regvals reg_fmt_rgb444[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
+	{ REG_COM1, 0x40 },	/* Magic reserved bit */
+	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
+	{ 0xff, 0xff },
+};
+
+
 
 /* This initializes the OV7670 camera chip and relevant variables. */
 static int ov7670_init(struct i2c_client *c)
@@ -577,6 +626,142 @@ #endif
 	return 0;
 }
 
+/*
+ * The beginnings of V4L2 awareness.
+ */
+
+static struct ov7670_format_struct {
+	__u8 *desc;
+	__u32 pixelformat;
+	struct ovcamchip_regvals *regs;
+} ov7670_formats[] = {
+	{
+		.desc		= "YUYV 4:2:2",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.regs 		= reg_fmt_yuv422,
+	},
+	{
+		.desc		= "RGB 444",
+		.pixelformat	= V4L2_PIX_FMT_RGB444,
+		.regs		= reg_fmt_rgb444,
+	},
+	{
+		.desc		= "RGB 565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.regs		= reg_fmt_rgb565,
+	},
+	/*
+	 * Pretend we do RGB32.  This is here on the assumption that the
+	 * upper layer will reformat RGB444 appropriately.  It's a test
+	 * function (no easy way to display RGB444 otherwise) and can
+	 * eventually come out.
+	 */
+	{
+		.desc		= "RGB32 (faked)",
+		.pixelformat	= V4L2_PIX_FMT_RGB32,
+		.regs		= reg_fmt_rgb444,
+	},
+};
+#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+
+/*
+ * All formats we support are 2 bytes/pixel.
+ */
+#define BYTES_PER_PIXEL 2
+
+
+static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
+{
+	struct ov7670_format_struct *ofmt;
+	
+	if (fmt->index >= N_OV7670_FMTS)
+		return -EINVAL;
+
+	ofmt = ov7670_formats + fmt->index;
+	fmt->flags = 0;
+	strcpy(fmt->description, ofmt->desc);
+	fmt->pixelformat = ofmt->pixelformat;
+	return 0;
+}
+
+
+static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+{
+	int index;
+	struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+	for (index = 0; index < N_OV7670_FMTS; index++)
+		if (ov7670_formats[index].pixelformat == pix->pixelformat)
+			break;
+	if (index >= N_OV7670_FMTS) {
+		printk(KERN_ERR "OV try fmt fail, fmt = %x\n", pix->pixelformat);
+		return -EINVAL;
+	}
+	/*
+	 * Fields: the OV devices claim to be progressive.
+	 */
+	if (pix->field == V4L2_FIELD_ANY)
+		pix->field = V4L2_FIELD_NONE;
+	else if (pix->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+	/*
+	 * Round requested image size down to the nearest
+	 * we support, but not below the smallest.
+	 */
+#if 0
+	if (pix->height < m88_height_options[0] ||
+			pix->width < m88_width_options[0])
+		sizeindex = 0;
+	else {
+		for (sizeindex = N_WINDOW_SIZES-1; sizeindex > 0; sizeindex--)
+			if (pix->height >= m88_height_options[sizeindex] &&
+			    pix->width >= m88_width_options[sizeindex])
+				break;
+	}
+	pix->width = m88_width_options[sizeindex];
+	pix->height = m88_height_options[sizeindex];
+#endif
+	/*
+	 * Wire VGA for now; we'll add flexibility later
+	 */
+	pix->width = 640;
+	pix->height = 480;
+	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;
+
+}
+
+/*
+ * Set a format.
+ */
+static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+{
+	int ret, i;
+	struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+	ret = ov7670_try_fmt(c, fmt);
+	if (ret)
+		return ret;
+	/*
+	 * Resolution wired to VGA for the moment.
+	 */
+	ov_write_mask(c, REG_COM7, COM7_FMT_VGA, COM7_FMT_MASK);
+	/*
+	 * Find our format again.  Kind of silly that we have to
+	 * search twice.  We know it exists, though, or try_fmt()
+	 * would have barfed.
+	 */
+	for (i = 0; i < N_OV7670_FMTS; i++)
+		if (ov7670_formats[i].pixelformat == pix->pixelformat)
+			break;
+	ov_write_regvals(c, ov7670_formats[i].regs);
+	return 0;
+}
+
+
 static int ov7670_command(struct i2c_client *c, unsigned int cmd, void *arg)
 {
 	switch (cmd) {
@@ -586,6 +771,16 @@ static int ov7670_command(struct i2c_cli
 		return ov7670_get_v4l1_control(c, arg);
 	case OVCAMCHIP_CMD_S_MODE:
 		return ov7670_set_window(c, arg);
+	/*
+	 * Accept a few V4L2 ioctls directly.
+	 */
+	case VIDIOC_ENUM_FMT:
+		return ov7670_enum_fmt(c, (struct v4l2_fmtdesc *) arg);
+	case VIDIOC_TRY_FMT:
+		return ov7670_try_fmt(c, (struct v4l2_format *) arg);
+	case VIDIOC_S_FMT:
+		return ov7670_s_fmt(c, (struct v4l2_format *) arg);
+		
 	default:
 		DDEBUG(2, &c->dev, "command not supported: %d", cmd);
 		return -ENOIOCTLCMD;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 8d7f03a..4de8b05 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -245,7 +245,6 @@ #define V4L2_PIX_FMT_YYUV    v4l2_fourcc
 #define V4L2_PIX_FMT_HI240   v4l2_fourcc('H','I','2','4') /*  8  8-bit color   */
 #define V4L2_PIX_FMT_HM12    v4l2_fourcc('H','M','1','2') /*  8  YUV 4:1:1 16x16 macroblocks */
 #define V4L2_PIX_FMT_RGB444  v4l2_fourcc('R','4','4','4') /* 16  xxxxrrrr ggggbbbb */
-#define V4L2_PIX_FMT_RGB565N v4l2_fourcc('R','G','B','6') /* 16  rrrrrggg gggbbbbb */
 
 /* see http://www.siliconimaging.com/RGB%20Bayer.htm */
 #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B','A','8','1') /*  8  BGBG.. GRGR.. */


More information about the Commits-kernel mailing list