Web lists-archives.com

[Spca50x-devs] PATCH: gspcav2-0.0.28 pac207 support




Hi all,

The attached patch fixes pac207 support as promised. I based it on my standalone v4l2 pac207 driver (which in turn was based on the pac207 driver from gspcav1), I basicly copied over huge amounts of code from my standalone v4l2 driver, so some changes may seem superfluous I did it this way, because my v4l2 driver already had moved from frame processing in a tasklet to frameprocessing one isoc packet at a time, which requires keeping a statemachine in the pac207 case as the pac207 sends data with mixed compressed / uncompressed lines, with a header for each line, thus one needs to only copy the real data and not the headers requiring some state tracking (on top of the standard isoc sof detecion).

Since writing and debugging this statemachine was the most work, I thus just copied over most of my code. I've also added my improved autogain (which controls both gain and exposure to get a decent range of support lighting conditions).

Currently the autogain code isn't enabled yet as that requires a callback from gscpa_main into the device specific code, autogain cannot be done in the packet parsing as that runs under irq context and one cannot send/receive usb control messages from irq context, while the autogain code needs to send usb control messages to change the gain / exposure settings.

I'll do a seperate patch adding the callback to gspca_main, and calling the autogain code, tomorrow.

Regards,

Hans
--- gspcav2-0.0.28/pac207.c	2008-04-11 11:35:49.000000000 +0200
+++ gspcav2-0.0.28.new/pac207.c	2008-04-19 22:39:31.000000000 +0200
@@ -1,5 +1,6 @@
 /****************************************************************************
 #	 	Pixart PAC207BCA library                                    #
+#		Copyright (C) 2008 Hans de Goede <j.w.r.degoede@xxxxxx>     #
 # 		Copyright (C) 2005 Thomas Kaiser thomas@xxxxxxxxxxxxxxx     #
 #               Copyleft (C) 2005 Michel Xhaard mxhaard@xxxxxxxx            #
 #									    #
@@ -23,40 +24,86 @@
 
 #define MODULE_NAME "pac207"
 
+#define PAC207_CTRL_TIMEOUT		500  /* ms */
+
+#define PAC207_BRIGHTNESS_MIN		0
+#define PAC207_BRIGHTNESS_MAX		255
+#define PAC207_BRIGHTNESS_DEFAULT	4 /* power on default: 4 */
+
+#define PAC207_EXPOSURE_MIN		4
+#define PAC207_EXPOSURE_MAX		26
+#define PAC207_EXPOSURE_DEFAULT		4 /* power on default: 3 ?? */
+#define PAC207_EXPOSURE_KNEE		15
+
+#define PAC207_GAIN_MIN			0
+#define PAC207_GAIN_MAX			31
+#define PAC207_GAIN_DEFAULT         	9 /* power on default: 9 */
+#define PAC207_GAIN_KNEE		20
+
+#define PAC207_AUTOGAIN_DEADZONE	10
+/* We calculating the autogain at the end of the transfer of a frame, at this
+   moment a frame with the old settings is being transmitted, and a frame is
+   being captured with the old settings. So if we adjust the autogain we must
+   ignore atleast the 2 next frames for the new settings to come into effect
+   before doing any other adjustments */
+#define PAC207_AUTOGAIN_IGNORE_FRAMES	3
+
 #include "gspca.h"
 
-#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(0, 0, 23)
-static const char version[] = "0.0.23";
+#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(0, 0, 29)
+static const char version[] = "0.0.29";
 
-MODULE_AUTHOR("Thomas Kaiser thomas@xxxxxxxxxxxxxxx");
+MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@xxxxxx>");
 MODULE_DESCRIPTION("Pixart PAC207");
 MODULE_LICENSE("GPL");
 
+enum pac207_line_state {
+	LINE_HEADER1,
+	LINE_HEADER2,
+	LINE_UNCOMPRESSED,
+	LINE_COMPRESSED,
+};
+
+struct pac207_decoder_state {
+	u16 line_read;
+	u16 line_marker;
+	u8 line_state;
+	u8 header_read;
+	u8 remaining_bits;
+	s8 no_remaining_bits;
+	u8 get_abs;
+	u8 discard_byte;
+	u8 line_decode_buf[352];
+};
+
 /* specific webcam descriptor */
 struct sd {
 	struct gspca_dev gspca_dev;		/* !! must be the first item */
 
-	int avg_lum;
+	struct pac207_decoder_state decoder_state;
+
+	u8 mode;
 
-	int buflen;				// current length of tmpbuf
-	unsigned char tmpbuf[640 * 480];
-	unsigned char tmpbuf2[640 * 480];
-
-	unsigned char brightness;
-	unsigned char contrast;
-	unsigned char autogain;
+	u8 brightness;
+	u8 exposure;
+	u8 autogain;
+	u8 gain;
 
-	signed char ag_cnt;
-#define AG_CNT_START 13
+	u8 sof_read;
+	u8 autogain_ignore_frames;
+
+	atomic_t avg_lum;
 };
 
 /* V4L2 controls supported by the driver */
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
 
 static struct ctrl sd_ctrls[] = {
 #define SD_BRIGHTNESS 0
@@ -65,27 +112,29 @@
 		.id      = V4L2_CID_BRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
 		.name    = "Brightness",
-		.minimum = 0,
-		.maximum = 0x20,
-		.step    = 1,
-		.default_value = 0x10,
+		.minimum = PAC207_BRIGHTNESS_MIN,
+		.maximum = PAC207_BRIGHTNESS_MAX,
+		.step = 1,
+		.default_value = PAC207_BRIGHTNESS_DEFAULT,
+		.flags = 0,
 	    },
 	    .set = sd_setbrightness,
 	    .get = sd_getbrightness,
 	},
-#define SD_CONTRAST 1
+#define SD_EXPOSURE 1
 	{
 	    {
-		.id      = V4L2_CID_CONTRAST,
-		.type    = V4L2_CTRL_TYPE_INTEGER,
-		.name    = "Contrast",
-		.minimum = 0,
-		.maximum = 0x20,
-		.step    = 1,
-		.default_value = 0x10,
+		.id = V4L2_CID_EXPOSURE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "exposure",
+		.minimum = PAC207_EXPOSURE_MIN,
+		.maximum = PAC207_EXPOSURE_MAX,
+		.step = 1,
+		.default_value = PAC207_EXPOSURE_DEFAULT,
+		.flags = 0,
 	    },
-	    .set = sd_setcontrast,
-	    .get = sd_getcontrast,
+	    .set = sd_setexposure,
+	    .get = sd_getexposure,
 	},
 #define SD_AUTOGAIN 2
 	{
@@ -97,10 +146,26 @@
 		.maximum = 1,
 		.step	= 1,
 		.default_value = 1,
+		.flags = 0,
 	    },
 	    .set = sd_setautogain,
 	    .get = sd_getautogain,
 	},
+#define SD_GAIN 3
+	{
+	    {
+		.id = V4L2_CID_GAIN,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "gain",
+		.minimum = PAC207_GAIN_MIN,
+		.maximum = PAC207_GAIN_MAX,
+		.step = 1,
+		.default_value = PAC207_GAIN_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setgain,
+	    .get = sd_getgain,
+	},
 };
 
 static struct cam_mode sif_mode[] = {
@@ -108,7 +173,7 @@
 	{V4L2_PIX_FMT_SBGGR8, 352, 288, 0},
 };
 
-static __u8 pac207_sensor_init[][8] = {
+static const __u8 pac207_sensor_init[][8] = {
 	{0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0},	//2 
 //	{0x10, 0x24, 0x06, 0x12, 0x0c, 0x01, 0x29, 0xf0},	//2 increase the times exposure decrease frame rate
 	{0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},	//a reg_10 digital gain Red Green Blue Ggain
@@ -117,92 +182,110 @@
 	{0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00},	//42 reg_66 rate control
 };
 
-static __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 };	//48 reg_72 Rate Control end BalSize_4a =0x36
+static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 };	//48 reg_72 Rate Control end BalSize_4a =0x36
 
+static const char pac207_sof_marker[5] = { 0xFF, 0xFF, 0x00, 0xFF, 0x96 };
 
-static void pac207RegRead(struct usb_device *dev,
-			__u16 reg,
-			__u16 value,
-			__u16 index,
-			__u8 *buffer)
-{
-	usb_control_msg(dev,
-			usb_rcvctrlpipe(dev, 0),
-			reg,
-			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
-			value, index, buffer, 1,
-			500);
-}
+int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
+	const u8 *buffer, u16 length)
+{
+	struct usb_device* udev = gspca_dev->dev;
+	int err;
+	u8 kbuf[8];
 
-static void pac207RegWrite(struct usb_device *dev,
-			__u16 reg,
-			__u16 value,
-			__u16 index,
-			__u8 *buffer,
-			__u16 len)
-{
-	usb_control_msg(dev,
-			usb_sndctrlpipe(dev, 0),
-			reg,
+	memcpy(kbuf, buffer, length);
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, 
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
-			value, index, buffer, len,
-			500);
+			0x00, index, kbuf, length, PAC207_CTRL_TIMEOUT);
+	if (err < 0)
+		PDEBUG(D_ERR, "Failed to write registers to index 0x%04X, error %d)",
+			index, err);
+
+	return err;
 }
 
-static void pac207_reg_read(struct usb_device *dev,
-				__u16 index,
-				__u8 *buffer)
+
+int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
 {
-	pac207RegRead(dev, 0x00, 0x00, index, buffer);
+	struct usb_device* udev = gspca_dev->dev;
+	int err;
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
+	if (err)
+		PDEBUG(D_ERR, "Failed to write a register (index 0x%04X, "
+			   "value 0x%02X, error %d)",index, value, err);
+
+	return err;
 }
 
-static void pac207_reg_write(struct usb_device *dev,
-				__u16 index,
-				__u16 value)
+
+int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
 {
-	pac207RegWrite(dev, 0x00, value, index, NULL, 0);
+	struct usb_device* udev = gspca_dev->dev;
+	u8 buff;
+	int res;
+
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			0x00, index, &buff, 1, PAC207_CTRL_TIMEOUT);
+	if (res < 0) {
+		PDEBUG(D_ERR, "Failed to read a register (index 0x%04X, error %d)",
+			index, res);
+		return res;
+	} 
+
+	return buff;
 }
 
+
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
 			const struct usb_device_id *id)
 {
-	struct sd *sd = (struct sd *) gspca_dev;
-	struct usb_device *dev = gspca_dev->dev;
 	struct cam *cam;
+	u8 idreg[2];
 
-//	PDEBUG(D_CONF, "Find Sensor PAC207");
-	pac207_reg_write(dev, 0x41, 0x00);	//00 Bit_0=Image Format, Bit_1=LED, Bit_2=Compression test mode enable
-	pac207_reg_write(dev, 0x0f, 0x00);	//Power Control
-	pac207_reg_write(dev, 0x11, 0x30);	//Analog Bias
+	idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
+	idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
+	idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
+	idreg[1] = idreg[1] & 0x0f;
+	PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X", idreg[0],
+		idreg[1]);
+
+	if (idreg[0] != 0x27) {
+		PDEBUG(D_PROBE, "Error invalid sensor ID!");
+		return -ENODEV;
+	}
+		
+	pac207_write_reg(gspca_dev, 0x41, 0x00); /* 00 Bit_0=Image Format, Bit_1=LED, Bit_2=Compression test mode enable */
+	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+	pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */
+
+	PDEBUG(D_PROBE, "Pixart PAC207BCA Image Processor and Control Chip detected "
+		   "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
 
 	cam = &gspca_dev->cam;
 	cam->dev_name = (char *) id->driver_info;
 	cam->epaddr = 0x05;
 	cam->cam_mode = sif_mode;
-	cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+	cam->nmodes = ARRAY_SIZE(sif_mode);
 
-	sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
-	sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
-	sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
 	return 0;
 }
 
 /* this function is called at open time */
 static int sd_open(struct gspca_dev *gspca_dev)
 {
-	struct usb_device *dev = gspca_dev->dev;
-	__u8 id[2];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
+	sd->exposure = PAC207_EXPOSURE_DEFAULT;
+	sd->gain = PAC207_GAIN_DEFAULT;
+	sd->autogain = 1;
 
-	pac207_reg_write(dev, 0x41, 0x00);	//Turn off LED
-	pac207_reg_read(dev, 0x00, &id[0]);
-	pac207_reg_read(dev, 0x01, &id[1]);
-	id[0] = ((id[0] & 0x0f) << 4) | ((id[1] & 0xf0) >> 4);
-	id[1] &= 0x0f;
-	PDEBUG(D_STREAM, "Pixart Sensor ID 0x%02X Chips ID 0x%02X !!",
-		id[0], id[1]);
-	if (id[0] != 0x27)
-		return -ENODEV;
 	return 0;
 }
 
@@ -210,68 +293,56 @@
 static void sd_start(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	struct usb_device *dev = gspca_dev->dev;
-	__u8 buffer;
 	__u8 mode;
 
-	pac207_reg_write(dev, 0x0f, 0x10);	//Power control (Bit 6-0)
-	pac207RegWrite(dev, 0x01, 0, 0x02, pac207_sensor_init[0], 8);
-	pac207RegWrite(dev, 0x01, 0, 0x0a, pac207_sensor_init[1], 8);
-	pac207RegWrite(dev, 0x01, 0, 0x12, pac207_sensor_init[2], 8);
-	pac207RegWrite(dev, 0x01, 0, 0x40, pac207_sensor_init[3], 8);
-	pac207RegWrite(dev, 0x01, 0, 0x42, pac207_sensor_init[4], 8);
-	pac207RegWrite(dev, 0x01, 0, 0x48, PacReg72, 4);
-//	if (sd->compress)
-//		pac207_reg_write(dev, 0x4a, 0x88);	//Compression Balance size 0x88
-//	else
-		pac207_reg_write(dev, 0x4a, 0xff);	//Compression Balance size
-
-	pac207_reg_write(dev, 0x4b, 0x00);	//Sram test value
-	pac207_reg_write(dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
-	pac207_reg_write(dev, 0x1c, 0x01);	//not documented
-	pac207_reg_write(dev, 0x41, 0x02);	//Image Format (Bit 0), LED (Bit 1), Compression test mode enable (Bit 2)
-
-	if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) {
-		/* 176x144 */
-		pac207_reg_read(dev, 0x41, &buffer);
-		mode = buffer | 0x01;
-		//mode = buffer | 0x00;
-		pac207_reg_write(dev, 0x41, mode);	//Set mode
-		pac207_reg_write(dev, 0x02, 0x04);	//PXCK = 12MHz /n
-		pac207_reg_write(dev, 0x0e, 0x0f);	//PGA global gain (Bit 4-0)
-		PDEBUG(D_STREAM, "pac207_start mode 176x144, mode: %x", mode);
-	} else {
-		/* 352x288 */
-		pac207_reg_read(dev, 0x41, &buffer);
-		mode = buffer & 0xfe;
-		pac207_reg_write(dev, 0x41, mode);	//Set mode
-//		if (sd->compress)
-//			pac207_reg_write(dev, 0x02, 0x04);	//PXCK = 12MHz / n 04
-//		else
-			pac207_reg_write(dev, 0x02, 0x0a);	//PXCK = 12MHz / n
-		pac207_reg_write(dev, 0x0e, 0x04);	//PGA global gain (Bit 4-0)
-		PDEBUG(D_STREAM, "pac207_start mode 352x288, mode: %x", mode);
-	}
-	pac207_reg_write(dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
-	pac207_reg_write(dev, 0x1c, 0x01);	//not documented
-	udelay(1000);
-	pac207_reg_write(dev, 0x40, 0x01);	//Start ISO pipe
-
-//	pac207_setbrightness(spca50x);
-
-	if (sd->autogain) {
-		sd->ag_cnt = AG_CNT_START;
-		sd->avg_lum = 0;
-	} else	sd->ag_cnt = -1;
+	pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
+	udelay(1000); /* taken from gspca */
+	
+	pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
+	udelay(1000); /* taken from gspca */
+	pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
+	udelay(1000); /* taken from gspca */
+	pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
+	udelay(1000); /* taken from gspca */
+	pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8);
+	udelay(1000); /* taken from gspca */
+	pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8);
+	udelay(1000); /* taken from gspca */
+	pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4);
+	udelay(1000); /* taken from gspca */
+
+	if (gspca_dev->width == 176)
+		pac207_write_reg(gspca_dev, 0x4a, 0xff); /* Compression Balance */
+	else
+		pac207_write_reg(gspca_dev, 0x4a, 0x88); /* Compression Balance */
+	pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
+	pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+	pac207_write_reg(gspca_dev, 0x0e, sd->gain); /* PGA global gain (Bit 4-0) */
+	pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
+
+	mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
+	if (gspca_dev->width == 176) { /* 176x144 */
+		mode |= 0x01;
+		PDEBUG(D_STREAM, "pac207_start mode 176x144");
+	} else/* 352x288 */
+		PDEBUG(D_STREAM, "pac207_start mode 352x288");
+	pac207_write_reg(gspca_dev, 0x41, mode);
+
+	pac207_write_reg(gspca_dev, 0x13, 0x01); /* load registers to sensor (Bit 0, auto clear) */
+	pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+	udelay(1000); /* taken from gspca */
+	pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
+
+	sd->sof_read = 0;
+	sd->autogain_ignore_frames = 0;
+	atomic_set (&sd->avg_lum, -1);		
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
-	struct usb_device *dev = gspca_dev->dev;
-
-	pac207_reg_write(dev, 0x40, 0x00);	//Stop ISO pipe
-	pac207_reg_write(dev, 0x41, 0x00);	//Turn of LED
-	pac207_reg_write(dev, 0x0f, 0x00);	//Power Control
+	pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
+	pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
+	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
 }
 
 static void sd_stop0(struct gspca_dev *gspca_dev)
@@ -283,39 +354,12 @@
 {
 }
 
-#ifdef GSPCA_ENABLE_REGISTERPLAY
-static void pac207_RegRead(struct gspca_dev *gspca_dev)
-{
-	struct usb_device *dev = gspca_dev->dev;
-	__u8 buffer;
-
-	RegAddress = RegAddress & 0xff;
-	pac207_reg_read(dev, RegAddress, &buffer);
-	RegValue = buffer;
-	PDEBUG(D_FRAM, "pac207_ReadReg, Reg 0x%02X value = %x", RegAddress,
-		   RegValue);
-}
-
-static void pac207_RegWrite(struct gspca_dev *gspca_dev)
-{
-	struct usb_device *dev = gspca_dev->dev;
-	__u8 buffer;
-
-	RegAddress = RegAddress & 0xff;
-	buffer = RegValue & 0xff;
-	pac207_reg_write(dev, RegAddress, buffer);
-	pac207_reg_write(dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
-	pac207_reg_write(dev, 0x1c, 0x01);	//not documented
-	PDEBUG(D_FRAM, "pac207_WriteReg,Reg 0x%02X value = %x", RegAddress, buffer);
-}
-#endif
-
 /* -- convert pixart frames to Bayer -- */
 /* Sonix decompressor struct B.S.(2004) */
 static struct {
-	int is_abs;
-	int len;
-	int val;
+	u8 is_abs;
+	u8 len;
+	s8 val;
 } table[256];
 
 void init_pixart_decoder(void)
@@ -375,121 +419,280 @@
 }
 
 #define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color)))
-static inline unsigned char getByte(unsigned char *inp,
-				    unsigned int bitpos)
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+   http://ytse.tricolour.net/docs/LowLightOptimization.html */
+static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
 {
-	unsigned char *addr;
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, steps, desired_avg_lum;
+	int orig_gain = sd->gain;
+	int orig_exposure = sd->exposure;
+	int avg_lum = atomic_read(&sd->avg_lum);
+
+	if (!sd->autogain || avg_lum == -1)
+		return;
+
+	if (sd->autogain_ignore_frames > 0) {
+		sd->autogain_ignore_frames--;
+		return;
+	}
+
+	/* correct desired lumination for the configured brightness */
+	desired_avg_lum = 100 + sd->brightness / 2;
+
+	/* If we are of a multiple of deadzone, do multiple step to reach the
+	   desired lumination fast (with the risc of a slight overshoot */
+	steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE;
+
+	for (i = 0; i < steps; i++) {
+		if (avg_lum > desired_avg_lum) {
+			if (sd->gain > PAC207_GAIN_KNEE) {
+				sd->gain--;
+			} else if (sd->exposure > PAC207_EXPOSURE_KNEE) {
+				sd->exposure--;
+			} else if (sd->gain > PAC207_GAIN_DEFAULT) {
+				sd->gain--;
+			} else if (sd->exposure > PAC207_EXPOSURE_MIN) {
+				sd->exposure--;
+			} else if (sd->gain > PAC207_GAIN_MIN) {
+				sd->gain--;
+			} else
+				break;
+		} else { 
+			if (sd->gain < PAC207_GAIN_DEFAULT) {
+				sd->gain++;
+			} else if (sd->exposure < PAC207_EXPOSURE_KNEE) {
+				sd->exposure++;
+			} else if (sd->gain < PAC207_GAIN_KNEE) {
+				sd->gain++;
+			} else if (sd->exposure < PAC207_EXPOSURE_MAX) {
+				sd->exposure++;
+			} else if (sd->gain < PAC207_GAIN_MAX) {
+				sd->gain++;
+			} else
+				break;
+		}
+	}
 
-	addr = inp + (bitpos >> 3);
-	return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7)));
+	if (sd->exposure != orig_exposure || sd->gain != orig_gain) {
+		if (sd->exposure != orig_exposure)
+			pac207_write_reg(gspca_dev, 0x0002, sd->exposure);
+		else
+			pac207_write_reg(gspca_dev, 0x000e, sd->gain);
+		pac207_write_reg(gspca_dev, 0x13, 0x01); /* load registers to sen. */
+		pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+		sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
+	}
 }
 
-static inline unsigned short getShort(unsigned char *pt)
+static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
+	unsigned char *m, int len)
 {
-	return (pt[0] << 8) | pt[1];
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+
+	/* Search for the SOF marker (fixed part) in the header */
+	for (i = 0; i < len; i++) {
+		if (m[i] == pac207_sof_marker[sd->sof_read]) {
+			sd->sof_read++;
+			if (sd->sof_read == sizeof(pac207_sof_marker)) {
+				PDEBUG(D_STREAM, "SOF found, bytes to analyze: %u. Frame"
+					"starts at byte #%u", len, i + 1);
+				sd->sof_read = 0;
+				return m + i + 1;
+			}
+		} else
+			sd->sof_read = 0;
+	}
+
+	return NULL;
 }
 
-static int pac_decompress_row(unsigned char *inp,
-				unsigned char *outp,
-				int width)
+static int pac207_decompress_row(struct gspca_dev *gspca_dev,
+	struct gspca_frame *f, unsigned char *cdata, int len)
 {
-	int col;
-	int val;
-	int bitpos;
-	unsigned char code;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pac207_decoder_state *decoder_state = &sd->decoder_state;
+	unsigned char *outp = decoder_state->line_decode_buf +
+				decoder_state->line_read;
+	int val, bitlen, bitpos = -decoder_state->no_remaining_bits;
+	u8 code;
 
 	/* first two pixels are stored as raw 8-bit */
-	*outp++ = inp[2];
-	*outp++ = inp[3];
-	bitpos = 32;
-
-	/* main decoding loop */
-	for (col = 2; col < width; col++) {
-
-		/* get bitcode */
-		code = getByte(inp, bitpos);
-		bitpos += table[code].len;
-
-		/* calculate pixel value */
-		if (table[code].is_abs) {
-			/* absolute value: get 6 more bits */
-			code = getByte(inp, bitpos);
-			bitpos += 6;
-			*outp++ = code & 0xfc;
+	while (decoder_state->line_read < 2) {
+		*outp++ = *cdata++;
+		decoder_state->line_read++;
+		len--;
+		if (len == 0)
+			goto decompress_exit;
+	}
+
+	while (decoder_state->line_read < gspca_dev->width) {
+		if (bitpos < 0) {
+			code = decoder_state->remaining_bits << (8 + bitpos) |
+				cdata[0] >> -bitpos;
 		} else {
-			/* relative to left pixel */
-			val = outp[-2] + table[code].val;
-			*outp++ = CLIP(val);
+			u8 *addr = cdata + bitpos / 8;
+			code = addr[0] << (bitpos & 7) |
+				addr[1] >> (8 - (bitpos & 7));
 		}
+
+		bitlen = decoder_state->get_abs ?
+				6 : table[code].len;
+
+		/* Stop decompressing if we're out of input data */
+		if ((bitpos + bitlen) > (len * 8))
+			break;
+
+		if (decoder_state->get_abs) {
+			*outp++ = code & 0xFC;
+			decoder_state->line_read++;
+			decoder_state->get_abs = 0;
+		} else {
+			if (table[code].is_abs)
+				decoder_state->get_abs = 1;
+			else {
+				/* relative to left pixel */
+				val = outp[-2] +
+					table[code].val;
+				*outp++ = CLIP(val);
+				decoder_state->line_read++;
+			}
+		}
+		bitpos += bitlen;
 	}
 
-	/* return line length, rounded up to next 16-bit word */
-	return 2 * ((bitpos + 15) / 16);
+	if (decoder_state->line_read == gspca_dev->width) {
+		gspca_frame_add(gspca_dev, INTER_PACKET, f,
+				decoder_state->line_decode_buf,
+				gspca_dev->width);
+		/* completely decompressed line, round pos to nearest word */
+		len -= 2 * ((bitpos + 15) / 16);
+		if (len < 0) {
+			decoder_state->discard_byte = 1;
+			len = 0;
+		}
+	} else {
+		decoder_state->remaining_bits = cdata[bitpos/8];
+		decoder_state->no_remaining_bits = (8 - bitpos) & 7;
+		len = 0;
+	}
+
+decompress_exit:
+	f->data_end = outp;
+	
+	return len;
+}
+
+static void pac207_decode_line_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pac207_decoder_state *decoder_state = &sd->decoder_state;
+
+	decoder_state->line_read = 0;
+	decoder_state->line_state = LINE_HEADER1;
+	decoder_state->no_remaining_bits = 0;
+	decoder_state->get_abs = 0; 
+}
+
+static void pac207_decode_frame_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pac207_decoder_state *decoder_state = &sd->decoder_state;
+
+	decoder_state->header_read = 0;
+	decoder_state->discard_byte = 0;
+
+	pac207_decode_line_init(gspca_dev);
 }
 
-static int pixart_decompress(struct gspca_dev *gspca_dev)
+static int pac207_decode_frame_data(struct gspca_dev *gspca_dev,
+	struct gspca_frame *f, unsigned char *data, int len)
 {
-/* we should received a whole frame with header and EOL marker
-in myframe->data and return a GBRG pattern in frame->tmpbuffer
-remove the header then copy line by line EOL is set with 0x0f 0xf0 marker
-or 0x1e 0xe1 for compressed line*/
 	struct sd *sd = (struct sd *) gspca_dev;
-	int width = gspca_dev->width;
-	int height = gspca_dev->height;
-	unsigned char *inp = sd->tmpbuf;
-	unsigned char *outp = sd->tmpbuf2;
+	struct pac207_decoder_state *decoder_state = &sd->decoder_state;
+	int needed = 0;
 
-	unsigned short word;
-	int row;
+	/* first 11 bytes after sof marker: frame header */
+	if (decoder_state->header_read < 11 ) {
+		/* get average lumination from frame header (byte 5) */
+		if (decoder_state->header_read < 5 ) {
+			needed = 5 - decoder_state->header_read;
+			if (len >= needed)
+				atomic_set(&sd->avg_lum, data[needed-1]);
+		}
+		/* skip the rest of the header */
+		needed = 11 - decoder_state->header_read;
+		if (len <= needed) {
+			decoder_state->header_read += len;
+			return 0;
+		}
+		data += needed;
+		len -= needed;
+		decoder_state->header_read = 11;
+	}
 
-	/* and ask to go at pixel +1 ?? */
-	outp++;
+	while (len) {
+		if (decoder_state->discard_byte) {
+			data++;
+			len--;
+			decoder_state->discard_byte = 0;
+			continue;
+		}
 
-	/* iterate over all rows */
-	for (row = 0; row < height; row++) {
-		word = getShort(inp);
-		switch (word) {
-		case 0x0ff0:
-			memcpy(outp, inp + 2, width);
-			inp += (2 + width);
+		switch (decoder_state->line_state) {
+		case LINE_HEADER1:
+			decoder_state->line_marker = data[0] << 8;
+			decoder_state->line_state = LINE_HEADER2;
+			needed = 1;
+			break;
+		case LINE_HEADER2:
+			decoder_state->line_marker |= data[0];
+			switch (decoder_state->line_marker) {
+			case 0x0FF0:
+				decoder_state->line_state = LINE_UNCOMPRESSED;
+				break;
+			case 0x1EE1:
+				decoder_state->line_state = LINE_COMPRESSED;
+				break;
+			default:
+				PDEBUG(D_STREAM, "Error unknown line-header %04X",
+					(int)decoder_state->line_marker);
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+				return 0;
+			}
+			needed = 1;
+			break;
+		case LINE_UNCOMPRESSED:
+			needed = gspca_dev->width - decoder_state->line_read;
+			if (needed > len)
+				needed = len;
+			gspca_frame_add(gspca_dev, INTER_PACKET, f, data,
+				needed);
+			decoder_state->line_read += needed;
 			break;
-		case 0x1ee1:
-			inp += pac_decompress_row(inp, outp, width);
+		case LINE_COMPRESSED:
+			needed = len -
+				pac207_decompress_row(gspca_dev, f, data, len);
 			break;
-		default:
-			return -1;
 		}
-		outp += width;
+
+		data += needed;
+		len -= needed;
+
+		if (decoder_state->line_read == gspca_dev->width) {
+			if ((f->data_end - f->data) ==
+				(gspca_dev->width * gspca_dev->height)) {
+				/* eureka we've got a frame */
+				return 1;
+			}
+			pac207_decode_line_init(gspca_dev);
+		}
 	}
+
 	return 0;
 }
-	
-static void setautogain(struct gspca_dev *gspca_dev, int luma)
-{
-	struct usb_device *dev = gspca_dev->dev;
-	__u8 luma_mean = 128;
-	__u8 luma_delta = 20;
-	__u8 spring = 5;
-	__u8 Pxclk;
-	int Gbright = 0;
-	
-	pac207_reg_read(dev, 0x02, &Pxclk);
-	Gbright = Pxclk;
-	
-	PDEBUG(D_FRAM, "luma mean %d", luma);
-	if (luma < luma_mean - luma_delta ||
-	    luma > luma_mean + luma_delta) {
-		Gbright += (luma_mean - luma) >> spring;
-		if (Gbright > 0x1a)
-			Gbright = 0x1a;
-		else if (Gbright < 4)
-			Gbright = 4;
-		PDEBUG(D_FRAM, "gbright %d", Gbright);
-		pac207_reg_write(dev, 0x02,(__u8) Gbright);
-		pac207_reg_write(dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
-		pac207_reg_write(dev, 0x1c, 0x01);	//not documented
-	}
-}
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 			struct gspca_frame *frame,	// target
@@ -497,95 +700,71 @@
 			int len)			// iso packet length
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	int p = 0;
-	if (len < 6) {
-		gspca_dev->last_packet_type = DISCARD_PACKET;
-		return;
-	}
+	unsigned char *sof;
+	int n;
 
-	for (p = 0; p < len - 6; p++) {
-		if (data[0 + p] == 0xff
-		    && data[1 + p] == 0xff
-		    && data[2 + p] == 0x00
-		    && data[3 + p] == 0xff
-		    && data[4 + p] == 0x96) {	/* start of frame */
+	sof = pac207_find_sof(gspca_dev, data, len);
 
-			if (gspca_dev->last_packet_type == FIRST_PACKET) {
-				pixart_decompress(gspca_dev);
+	if (sof) {
+		/* finish decoding current frame */
+		if (gspca_dev->last_packet_type == INTER_PACKET) {
+			n = sof - data;
+			if (n > sizeof(pac207_sof_marker))
+				n -= sizeof(pac207_sof_marker);
+			else
+				n = 0;
+			n = pac207_decode_frame_data(gspca_dev, frame,
+							data, n);
+			if (n)
 				frame = gspca_frame_add(gspca_dev,
-							LAST_PACKET,
-							frame,
-							sd->tmpbuf2,
-							sd->buflen);
-				if (sd->ag_cnt >= 0) {
-					sd->avg_lum += data[p +9];
-					if (--sd->ag_cnt < 0) {
-						sd->ag_cnt = AG_CNT_START;
-						setautogain(gspca_dev,
-							sd->avg_lum / AG_CNT_START);
-						sd->avg_lum = 0;
-					}
-				}
-			}
-			gspca_frame_add(gspca_dev, FIRST_PACKET,
-					frame, data, 0);
-			data += 16;
-			len -= 16;
-			if (len > 0)
-				memcpy(sd->tmpbuf, data, len);
-			else	len = 0;
-			sd->buflen = len;
-			return;
+						LAST_PACKET,
+						frame,
+						NULL,
+						0);
+			else
+				PDEBUG(D_STREAM, "Incomplete frame");
 		}
+		pac207_decode_frame_init(gspca_dev);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL,
+				0);
+		len -= sof - data;
+		data = sof;
 	}
-	if (sd->buflen + len > sizeof sd->tmpbuf) {
-		if (gspca_dev->last_packet_type != DISCARD_PACKET) {
-			PDEBUG(D_ERR, "frame too large");
-			gspca_dev->last_packet_type = DISCARD_PACKET;
-		}
-	} else {
-		memcpy(&sd->tmpbuf[sd->buflen], data, len);
-		sd->buflen += len;
-	}
-}
 
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
-	struct usb_device *dev = gspca_dev->dev;
-	struct sd *sd = (struct sd *) gspca_dev;
+	if (gspca_dev->last_packet_type == DISCARD_PACKET)
+		return;
 
-	pac207_reg_write(dev, 0x08, sd->brightness);
-	pac207_reg_write(dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
-	pac207_reg_write(dev, 0x1c, 0x01);	//not documented
+	n = pac207_decode_frame_data(gspca_dev, frame, data, len);
+	if (n)
+		frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+					frame, NULL, 0);
 }
 
-static void getbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	__u8 brightness;
 
-	pac207_reg_read(gspca_dev->dev, 0x08, &brightness);
-	sd->brightness = brightness;
+	pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+	pac207_write_reg(gspca_dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
+	pac207_write_reg(gspca_dev, 0x1c, 0x01);	//not documented
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	struct usb_device *dev = gspca_dev->dev;
 
-	pac207_reg_write(dev, 0x0e, sd->contrast >> 3);
-	pac207_reg_write(dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
-	pac207_reg_write(dev, 0x1c, 0x01);	//not documented
+	pac207_write_reg(gspca_dev, 0x02, sd->exposure);
+	pac207_write_reg(gspca_dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
+	pac207_write_reg(gspca_dev, 0x1c, 0x01);	//not documented
 }
 
-static void getcontrast(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	struct usb_device *dev = gspca_dev->dev;
-	__u8 contrast;
 
-	pac207_reg_read(dev, 0x0e, &contrast);
-	sd->contrast = contrast << 3;
+	pac207_write_reg(gspca_dev, 0x0e, sd->gain);
+	pac207_write_reg(gspca_dev, 0x13, 0x01);	//load registers to sensor (Bit 0, auto clear)
+	pac207_write_reg(gspca_dev, 0x1c, 0x01);	//not documented
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -602,27 +781,51 @@
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	getbrightness(gspca_dev);
 	*val = sd->brightness;
 	return 0;
 }
 
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	sd->contrast = val;
+	/* don't allow mucking with exposure when using autogain */
+	if (sd->autogain)
+		return -EINVAL;
+
+	sd->exposure = val;
 	if (gspca_dev->streaming)
-		setcontrast(gspca_dev);
+		setexposure(gspca_dev);
 	return 0;
 }
 
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	getcontrast(gspca_dev);
-	*val = sd->contrast;
+	*val = sd->exposure;
+	return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* don't allow mucking with gain when using autogain */
+	if (sd->autogain)
+		return -EINVAL;
+
+	sd->gain = val;
+	if (gspca_dev->streaming)
+		setgain(gspca_dev);
+	return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->gain;
 	return 0;
 }
 
@@ -631,9 +834,21 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 
 	sd->autogain = val;
-	if (val)
-		sd->ag_cnt = AG_CNT_START;
-	else	sd->ag_cnt = -1;
+	/* when switching to autogain set defaults to make sure
+	   we are on a valid point of the autogain gain /
+	   exposure knee graph, and give this change time to
+	   take effect before doing autogain. */
+	if (sd->autogain) {
+		sd->exposure = PAC207_EXPOSURE_DEFAULT;
+		sd->gain = PAC207_GAIN_DEFAULT;
+		if (gspca_dev->streaming) {
+			sd->autogain_ignore_frames =
+				PAC207_AUTOGAIN_IGNORE_FRAMES;
+			setexposure(gspca_dev);
+			setgain(gspca_dev);
+		}
+	}
+
 	return 0;
 }
 
@@ -649,7 +864,7 @@
 static struct sd_desc sd_desc = {
 	.name = MODULE_NAME,
 	.ctrls = sd_ctrls,
-	.nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0],
+	.nctrls = ARRAY_SIZE(sd_ctrls),
 	.config = sd_config,
  	.open = sd_open,
 	.start = sd_start,
@@ -695,9 +910,9 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
+	init_pixart_decoder();
 	if (usb_register(&sd_driver) < 0)
 		return -1;
-	init_pixart_decoder();
 	PDEBUG(D_PROBE, "v%s registered", version);
 	return 0;
 }
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
Spca50x-devs mailing list
Spca50x-devs@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/spca50x-devs