summaryrefslogtreecommitdiffstats
path: root/drivers/usb/ov511.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
commit6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch)
tree0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/usb/ov511.c
parentecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (diff)
Merge with 2.4.0-test1-ac21 + pile of MIPS cleanups to make merging
possible. Chainsawed RM200 kernel to compile again. Jazz machine status unknown.
Diffstat (limited to 'drivers/usb/ov511.c')
-rw-r--r--drivers/usb/ov511.c286
1 files changed, 148 insertions, 138 deletions
diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c
index 3ffb6f8eb..98c1c830f 100644
--- a/drivers/usb/ov511.c
+++ b/drivers/usb/ov511.c
@@ -2,8 +2,8 @@
* OmniVision OV511 Camera-to-USB Bridge Driver
*
* Copyright (c) 1999-2000 Mark W. McClelland
- * Many improvements by Bret Wallach
- * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
* Snapshot code by Kevin Moore
* OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
* Changes by Claudio Matsuoka <claudio@conectiva.com>
@@ -30,7 +30,7 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-static const char version[] = "1.15";
+static const char version[] = "1.16";
#define __NO_VERSION__
@@ -55,6 +55,8 @@ static const char version[] = "1.15";
#include "ov511.h"
+#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */
+
#define OV511_I2C_RETRIES 3
/* Video Size 640 x 480 x 3 bytes for RGB */
@@ -64,8 +66,8 @@ static const char version[] = "1.15";
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
-#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_RGB24 ? 384 : 256)
-#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_RGB24 ? 24 : 8)
+#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
+#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24)
/* PARAMETER VARIABLES: */
static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
@@ -280,6 +282,7 @@ static int ov511_read_proc(char *page, char **start, off_t off,
/* IMPORTANT: This output MUST be kept under PAGE_SIZE
* or we need to get more sophisticated. */
+ out += sprintf (out, "driver_version : %s\n", version);
out += sprintf (out, "custom_id : %d\n", ov511->customid);
out += sprintf (out, "model : %s\n", ov511->desc ?
clist[ov511->desc].description : "unknown");
@@ -300,10 +303,6 @@ static int ov511_read_proc(char *page, char **start, off_t off,
ov511->frame[i].depth);
out += sprintf (out, " size : %d %d\n",
ov511->frame[i].width, ov511->frame[i].height);
-#if 0
- out += sprintf (out, " hdr_size : %d %d\n",
- ov511->frame[i].hdrwidth, ov511->frame[i].hdrheight);
-#endif
out += sprintf (out, " format : ");
for (j = 0; plist[j].num >= 0; j++) {
if (plist[j].num == ov511->frame[i].format) {
@@ -317,10 +316,6 @@ static int ov511_read_proc(char *page, char **start, off_t off,
ov511->frame[i].segsize);
out += sprintf (out, " data_buffer : 0x%p\n",
ov511->frame[i].data);
-#if 0
- out += sprintf (out, " bytesread : %ld\n",
- ov511->frame[i].bytes_read);
-#endif
}
out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled));
out += sprintf (out, "bridge : %s\n",
@@ -846,28 +841,20 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
return 0;
}
-/* FIXME: add 400x300, 176x144, 160x140 */
+/* FIXME: 176x144, 160x140 */
+/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
static struct mode_list mlist[] = {
- { 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00,
- 0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e },
- { 640, 480, VIDEO_PALETTE_RGB24,0x4f, 0x3d, 0x00, 0x00,
- 0x4f, 0x3d, 0x00, 0x00, 0x06, 0x03, 0x24, 0x04, 0x9e },
- { 320, 240, VIDEO_PALETTE_GREY, 0x27, 0x1f, 0x00, 0x00,
- 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e },
- { 320, 240, VIDEO_PALETTE_RGB24,0x27, 0x1f, 0x00, 0x00,
- 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e },
- { 352, 288, VIDEO_PALETTE_GREY, 0x2b, 0x25, 0x00, 0x00,
- 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00,
- 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 384, 288, VIDEO_PALETTE_GREY, 0x2f, 0x25, 0x00, 0x00,
- 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 384, 288, VIDEO_PALETTE_RGB24,0x2f, 0x25, 0x00, 0x00,
- 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 448, 336, VIDEO_PALETTE_GREY, 0x37, 0x29, 0x00, 0x00,
- 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 448, 336, VIDEO_PALETTE_RGB24,0x37, 0x29, 0x00, 0x00,
- 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
+ /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COMC COML */
+ { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e },
+ { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e },
+ { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e },
+ { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e },
+ { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
{ 0, 0 }
};
@@ -875,11 +862,9 @@ static int
ov511_mode_init_regs(struct usb_ov511 *ov511,
int width, int height, int mode, int sub_flag)
{
- int rc = 0;
- struct usb_device *dev = ov511->dev;
- int hwsbase = 0;
- int hwebase = 0;
int i;
+ struct usb_device *dev = ov511->dev;
+ int hwsbase = 0, hwebase = 0;
PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
width, height, mode, sub_flag);
@@ -930,87 +915,55 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
break;
}
-#if 0
- /* FIXME: subwindow support is currently broken!
- */
- if (width == 640 && height == 480) {
- if (sub_flag) {
- /* horizontal window start */
- ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
- /* horizontal window end */
- ov511_i2c_write(dev, 0x18,
- hwebase+((ov511->subx+ov511->subw)>>2));
- /* vertical window start */
- ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
- /* vertical window end */
- ov511_i2c_write(dev, 0x1a,
- 0x5+((ov511->suby+ov511->subh)>>1));
- ov511_reg_write(dev, 0x12, (ov511->subw>>3)-1);
- ov511_reg_write(dev, 0x13, (ov511->subh>>3)-1);
- /* clock rate control */
- ov511_i2c_write(dev, 0x11, 0x01);
-
- /* Snapshot additions */
- ov511_reg_write(dev, 0x1a, (ov511->subw>>3)-1);
- ov511_reg_write(dev, 0x1b, (ov511->subh>>3)-1);
- ov511_reg_write(dev, 0x1c, 0x00);
- ov511_reg_write(dev, 0x1d, 0x00);
- } else {
- ov511_i2c_write(dev, 0x17, hwsbase);
- ov511_i2c_write(dev, 0x18, hwebase + (640>>2));
- ov511_i2c_write(dev, 0x19, 0x5);
- ov511_i2c_write(dev, 0x1a, 5 + (480>>1));
- ov511_reg_write(dev, 0x12, 0x4f);
- ov511_reg_write(dev, 0x13, 0x3d);
-
- /* Snapshot additions */
- ov511_reg_write(dev, 0x1a, 0x4f);
- ov511_reg_write(dev, 0x1b, 0x3d);
- ov511_reg_write(dev, 0x1c, 0x00);
- ov511_reg_write(dev, 0x1d, 0x00);
-
- if (mode == VIDEO_PALETTE_GREY) {
- ov511_i2c_write(dev, 0x11, 4); /* check */
- } else {
- ov511_i2c_write(dev, 0x11, 6); /* check */
- }
- }
-
- ov511_reg_write(dev, 0x14, 0x00); /* Pixel divisor */
- ov511_reg_write(dev, 0x15, 0x00); /* Line divisor */
-
- /* FIXME?? Shouldn't below be true only for YUV420? */
- ov511_reg_write(dev, 0x18, 0x03); /* YUV420/422, YFIR */
+ if (sub_flag) {
+ ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
+ ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2));
+ ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
+ ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1));
+ } else {
+ ov511_i2c_write(dev, 0x17, hwsbase);
+ ov511_i2c_write(dev, 0x18, hwebase + (640>>2));
+ ov511_i2c_write(dev, 0x19, 0x5);
+ ov511_i2c_write(dev, 0x1a, 5 + (480>>1));
+ }
- ov511_i2c_write(dev, 0x12, 0x24); /* Common A */
- ov511_i2c_write(dev, 0x14, 0x04); /* Common C */
+ for (i = 0; mlist[i].width; i++) {
+ int lncnt, pxcnt, clock;
- /* 7620 doesn't have register 0x35, so play it safe */
- if (ov511->sensor != SEN_OV7620)
- ov511_i2c_write(dev, 0x35, 0x9e);
-#endif
+ if (width != mlist[i].width || height != mlist[i].height)
+ continue;
- for (i = 0; mlist[i].width; i++) {
- if (width != mlist[i].width ||
- height != mlist[i].height ||
- mode != mlist[i].mode)
+ if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
continue;
- ov511_reg_write(dev, 0x12, mlist[i].pxcnt);
- ov511_reg_write(dev, 0x13, mlist[i].lncnt);
+ /* Here I'm assuming that snapshot size == image size.
+ * I hope that's always true. --claudio
+ */
+ pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+ lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+
+ ov511_reg_write(dev, 0x12, pxcnt);
+ ov511_reg_write(dev, 0x13, lncnt);
ov511_reg_write(dev, 0x14, mlist[i].pxdv);
ov511_reg_write(dev, 0x15, mlist[i].lndv);
ov511_reg_write(dev, 0x18, mlist[i].m420);
/* Snapshot additions */
- ov511_reg_write(dev, 0x1a, mlist[i].s_pxcnt);
- ov511_reg_write(dev, 0x1b, mlist[i].s_lncnt);
- ov511_reg_write(dev, 0x1c, mlist[i].s_pxdv);
- ov511_reg_write(dev, 0x1d, mlist[i].s_lndv);
-
- ov511_i2c_write(dev, 0x11, mlist[i].clock); /* check */
-
+ ov511_reg_write(dev, 0x1a, pxcnt);
+ ov511_reg_write(dev, 0x1b, lncnt);
+ ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
+ ov511_reg_write(dev, 0x1d, mlist[i].lndv);
+
+ /* Calculate and set the clock divisor */
+ clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
+ * (mlist[i].color ? 3 : 2) / 2) / 66000;
+ ov511_i2c_write(dev, 0x11, clock);
+
+#ifdef OV511_GBR422
+ ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08);
+#else
ov511_i2c_write(dev, 0x12, mlist[i].common_A);
+#endif
ov511_i2c_write(dev, 0x14, mlist[i].common_C);
/* 7620 doesn't have register 0x35, so play it safe */
@@ -1020,20 +973,20 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
break;
}
+ if (ov511_restart(ov511->dev) < 0)
+ return -EIO;
+
if (mlist[i].width == 0) {
err("Unknown mode (%d, %d): %d", width, height, mode);
- rc = -EINVAL;
+ return -EINVAL;
}
- if (ov511_restart(ov511->dev) < 0)
- return -EIO;
-
#ifdef OV511_DEBUG
if (debug >= 5)
ov511_dump_i2c_regs(dev);
#endif
- return rc;
+ return 0;
}
/**********************************************************************
@@ -1115,7 +1068,7 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
* 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
* 8 9 ... 15 72 73 ... 79 200 201 ... 207
* ... ... ...
- * 56 57 ... 63 120 121 127 248 249 ... 255
+ * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255
*
* Note that the U and V data in one segment represents a 16 x 16 pixel
* area, but the Y data represents a 32 x 8 pixel area.
@@ -1132,6 +1085,57 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
#undef OV511_DUMPPIX
+#ifdef OV511_GBR422
+static void
+ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
+ int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+ int k, l, m;
+ unsigned char *pIn;
+ unsigned char *pOut, *pOut1;
+
+ pIn = pIn0;
+ pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0);
+
+ for (k = 0; k < 8; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
+ *(pOut1 + iWidth*3 + 3) = *pIn++;
+ pOut1 += 6;
+ }
+ pOut += iWidth*3*2;
+ }
+
+ pIn = pIn0 + 64;
+ pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2);
+ for (k = 0; k < 8; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
+ *(pOut1 + iWidth*3 + 3) = *pIn++;
+ pOut1 += 6;
+ }
+ pOut += iWidth*3*2;
+ }
+
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY + 1;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ for (m = 0; m < 8; m++) {
+ *pOut1 = *pIn++;
+ pOut1 += 3;
+ }
+ pOut1 += (iWidth - 8) * 3;
+ }
+ pOut += 8 * 3;
+ }
+}
+
+#else
+
static void
ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
int iOutY, int iOutUV, int iHalf, int iWidth)
@@ -1225,6 +1229,7 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
}
#endif
}
+#endif
/*
* For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
@@ -1252,7 +1257,7 @@ ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0,
for (m = 0; m < 8; m++) {
*pOut1++ = *pIn++;
}
- pOut1 += iWidth - WDIV;
+ pOut1 += iWidth - 8;
}
pOut += 8;
}
@@ -1350,12 +1355,10 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
/* Frame end */
if (cdata[8] & 0x80) {
-#if 0
struct timeval *ts;
ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
- do_gettimeofday (ts);
-#endif
+ do_gettimeofday (ts);
PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
@@ -1456,9 +1459,9 @@ check_middle:
}
/*
- * iY counts segment lines
- * jY counts segment columns
- * iOutY is the offset (in bytes) of the segment upper left corner
+ * i counts segment lines
+ * j counts segment columns
+ * iOut is the offset (in bytes) of the upper left corner
*/
iY = iSegY / (frame->width / WDIV);
jY = iSegY - iY * (frame->width / WDIV);
@@ -1467,11 +1470,15 @@ check_middle:
jUV = iSegUV - iUV * (frame->width / WDIV * 2);
iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3);
- if (frame->format == VIDEO_PALETTE_GREY)
+ switch (frame->format) {
+ case VIDEO_PALETTE_GREY:
ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
- else if (frame->format == VIDEO_PALETTE_RGB24)
+ break;
+ case VIDEO_PALETTE_RGB24:
ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
iY & 1, frame->width);
+ break;
+ }
pData = &cdata[iPix];
}
@@ -1480,8 +1487,7 @@ check_middle:
if (frame->segment < frame->width * frame->height / 256) {
ov511->scratchlen = (ov511->packet_size - 1) - iPix;
if (ov511->scratchlen < frame->segsize)
- memmove(ov511->scratch, pData,
- ov511->scratchlen);
+ memmove(ov511->scratch, pData, ov511->scratchlen);
else
ov511->scratchlen = 0;
}
@@ -1887,18 +1893,11 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
return -EINVAL;
if (vc.decimation)
return -EINVAL;
-#if 0
- vc.x /= 4;
- vc.x *= 4;
- vc.y /= 2;
- vc.y *= 2;
- vc.width /= 32;
- vc.width *= 32;
-#else
+
vc.x &= ~3L;
vc.y &= ~1L;
vc.y &= ~31L;
-#endif
+
if (vc.width == 0)
vc.width = 32;
@@ -2290,6 +2289,17 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
int i, success;
int rc;
+ /* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ * shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ * contrast
+ */
static struct ov511_regvals aRegvalsNorm7610[] = {
{ OV511_I2C_BUS, 0x10, 0xff },
{ OV511_I2C_BUS, 0x16, 0x06 },
@@ -2299,16 +2309,16 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
{ OV511_I2C_BUS, 0x06, 0x00 },
{ OV511_I2C_BUS, 0x12, 0x00 },
{ OV511_I2C_BUS, 0x38, 0x81 },
- { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */
+ { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */
{ OV511_I2C_BUS, 0x05, 0x00 },
- { OV511_I2C_BUS, 0x0f, 0x05 },
+ { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */
{ OV511_I2C_BUS, 0x15, 0x01 },
{ OV511_I2C_BUS, 0x20, 0x1c },
{ OV511_I2C_BUS, 0x23, 0x2a },
{ OV511_I2C_BUS, 0x24, 0x10 },
{ OV511_I2C_BUS, 0x25, 0x8a },
{ OV511_I2C_BUS, 0x27, 0xc2 },
- { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */
+ { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */
{ OV511_I2C_BUS, 0x2a, 0x04 },
{ OV511_I2C_BUS, 0x2c, 0xfe },
{ OV511_I2C_BUS, 0x30, 0x71 },
@@ -2331,7 +2341,7 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
{ OV511_I2C_BUS, 0x12, 0x00 },
{ OV511_I2C_BUS, 0x28, 0x24 },
{ OV511_I2C_BUS, 0x05, 0x00 },
- { OV511_I2C_BUS, 0x0f, 0x05 },
+ { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */
{ OV511_I2C_BUS, 0x15, 0x01 },
{ OV511_I2C_BUS, 0x23, 0x00 },
{ OV511_I2C_BUS, 0x24, 0x10 },