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