All of lore.kernel.org
 help / color / mirror / Atom feed
* PATCH: gspca-sn9c102-autoexposure.patch
@ 2008-07-09 13:31 Hans de Goede
  0 siblings, 0 replies; only message in thread
From: Hans de Goede @ 2008-07-09 13:31 UTC (permalink / raw)
  To: Jean-Francois Moine; +Cc: video4linux-list

[-- Attachment #1: Type: text/plain, Size: 257 bytes --]

Hi,

This patch adds exposure control and autoexposure for sn9c102 cams which use
a tas5110 or a ov6650 image sensor.

It also renames the contrast control to gain, as that is what it is.

Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>

Regards,

Hans

[-- Attachment #2: gspca-sn9c102-autoexposure.patch --]
[-- Type: text/plain, Size: 13369 bytes --]

This patch adds exposure control and autoexposure for sn9c102 cams which use
a tas5110 or a ov6650 image sensor.

It also renames the contrast control to gain, as that is what it is.

Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>

diff -r f69de2a58412 linux/drivers/media/video/gspca/gspca.c
--- a/linux/drivers/media/video/gspca/gspca.c	Wed Jul 09 09:49:21 2008 +0200
+++ b/linux/drivers/media/video/gspca/gspca.c	Wed Jul 09 13:14:35 2008 +0200
@@ -1835,6 +1835,9 @@
 	   desired lumination fast (with the risc of a slight overshoot) */
 	steps = abs(desired_avg_lum - avg_lum) / deadzone;
 
+	PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
+		avg_lum, desired_avg_lum, steps);
+
 	for (i = 0; i < steps; i++) {
 		if (avg_lum > desired_avg_lum) {
 			if (gain > gain_knee)
diff -r f69de2a58412 linux/drivers/media/video/gspca/sonixb.c
--- a/linux/drivers/media/video/gspca/sonixb.c	Wed Jul 09 09:49:21 2008 +0200
+++ b/linux/drivers/media/video/gspca/sonixb.c	Wed Jul 09 13:14:35 2008 +0200
@@ -35,8 +35,15 @@
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
 
+	struct sd_desc sd_desc;		/* our nctrls differ dependend upon the
+					   sensor, so we use a per cam copy */
+	atomic_t avg_lum;
+
 	unsigned char brightness;
-	unsigned char contrast;
+	unsigned char gain;
+	unsigned char exposure;
+	unsigned char autogain;
+	unsigned char autogain_ignore_frames;
 
 	unsigned char fr_h_sz;		/* size of frame header */
 	char sensor;			/* Type of image sensor chip */
@@ -59,11 +66,24 @@
 
 #define SYS_CLK 0x04
 
+/* We calculate 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 AUTOGAIN_IGNORE_FRAMES 3
+#define AUTOGAIN_DEADZONE 500
+#define DESIRED_AVG_LUM 7000
+
 /* 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_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(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 struct ctrl sd_ctrls[] = {
 #define SD_BRIGHTNESS 0
@@ -75,24 +95,59 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-		.default_value = 127,
+#define BRIGHTNESS_DEF 127
+		.default_value = BRIGHTNESS_DEF,
 	    },
 	    .set = sd_setbrightness,
 	    .get = sd_getbrightness,
 	},
-#define SD_CONTRAST 1
+#define SD_GAIN 1
 	{
 	    {
-		.id      = V4L2_CID_CONTRAST,
+		.id      = V4L2_CID_GAIN,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
-		.name    = "Contrast",
+		.name    = "Gain",
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-		.default_value = 127,
+#define GAIN_DEF 127
+#define GAIN_KNEE 200
+		.default_value = GAIN_DEF,
 	    },
-	    .set = sd_setcontrast,
-	    .get = sd_getcontrast,
+	    .set = sd_setgain,
+	    .get = sd_getgain,
+	},
+#define SD_EXPOSURE 2
+	{
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Exposure",
+#define EXPOSURE_DEF 0
+#define EXPOSURE_KNEE 176 /* 10 fps */
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = EXPOSURE_DEF,
+			.flags = 0,
+		},
+		.set = sd_setexposure,
+		.get = sd_getexposure,
+	},
+#define SD_AUTOGAIN 3
+	{
+		{
+			.id = V4L2_CID_AUTOGAIN,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Automatic Gain (and Exposure)",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.flags = 0,
+		},
+		.set = sd_setautogain,
+		.get = sd_getautogain,
 	},
 };
 
@@ -161,8 +216,12 @@
 	/* Bright, contrast, etc are set througth SCBB interface.
 	 * AVCAP on win2 do not send any data on this 	controls. */
 	/* Anyway, some registers appears to alter bright and constrat */
+	
+	/* Reset sensor */
 	{0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},
+	/* Set clock register 0x11 low nibble is clock divider */
 	{0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10},
+	/* Next some unknown stuff */
 	{0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10},
 /*	{0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10},
 		 * THIS SET GREEN SCREEN
@@ -171,31 +230,36 @@
 	{0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */
 	{0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10},
 	{0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10},
-	{0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10},
+	/* Disable autobright ? */
+	{0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, 
+	/* Some more unknown stuff */
 	{0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10},
 	{0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */
-	{0xa0, 0x60, 0x10, 0x5d, 0x99, 0x04, 0x94, 0x16},
+	{0xa0, 0x60, 0x10, 0x57, 0x99, 0x04, 0x94, 0x16},
+	/* Framerate adjust register for artificial light 50 hz flicker
+	   compensation, identical to ov6630 0x2b register, see 6630 datasheet.
+	   0x4f -> (30 fps -> 25 fps), 0x00 -> no adjustment */
+	{0xa0, 0x60, 0x2b, 0x4f, 0x99, 0x04, 0x94, 0x15},
+#if 0
+	/* HDG, don't change registers 0x2d, 0x32 & 0x33 from their reset
+	   defaults, doing so mucks up the framerate, where as the defaults
+	   seem to work good, the combinations below have been observed
+	   under windows and are kept for future reference */
 	{0xa0, 0x60, 0x2d, 0x0a, 0x99, 0x04, 0x94, 0x16},
 	{0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16},
 	{0xa0, 0x60, 0x33, 0x40, 0x99, 0x04, 0x94, 0x16},
-	{0xa0, 0x60, 0x11, 0xc0, 0x99, 0x04, 0x94, 0x16},
-	{0xa0, 0x60, 0x00, 0x16, 0x99, 0x04, 0x94, 0x15}, /* bright / Lumino */
-	{0xa0, 0x60, 0x2b, 0xab, 0x99, 0x04, 0x94, 0x15},
-							/* ?flicker o brillo */
 	{0xa0, 0x60, 0x2d, 0x2a, 0x99, 0x04, 0x94, 0x15},
 	{0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16},
 	{0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16},
 	{0xa0, 0x60, 0x33, 0x00, 0x99, 0x04, 0x94, 0x16},
-	{0xa0, 0x60, 0x10, 0x57, 0x99, 0x04, 0x94, 0x16},
 	{0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16},
 	{0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16},
 		/* Low Light (Enabled: 0x32 0x1 | Disabled: 0x32 0x00) */
 	{0xa0, 0x60, 0x33, 0x29, 0x99, 0x04, 0x94, 0x16},
 		/* Low Ligth (Enabled: 0x33 0x13 | Disabled: 0x33 0x29) */
-/*	{0xa0, 0x60, 0x11, 0xc1, 0x99, 0x04, 0x94, 0x16}, */
-	{0xa0, 0x60, 0x00, 0x17, 0x99, 0x04, 0x94, 0x15}, /* clip? r */
-	{0xa0, 0x60, 0x00, 0x18, 0x99, 0x04, 0x94, 0x15}, /* clip? r */
+#endif
 };
+
 static const __u8 initOv7630[] = {
 	0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,	/* r01 .. r08 */
 	0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* r09 .. r10 */
@@ -494,19 +558,69 @@
 err:
 	PDEBUG(D_ERR, "i2c error brightness");
 }
-static void setcontrast(struct gspca_dev *gspca_dev)
+
+static void setgain(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 	__u8 gain;
 	__u8 rgb_value;
 
-	gain = sd->contrast >> 4;
+	gain = sd->gain >> 4;
+
 	/* red and blue gain */
 	rgb_value = gain << 4 | gain;
 	reg_w(gspca_dev->dev, 0x10, &rgb_value, 1);
 	/* green gain */
 	rgb_value = gain;
 	reg_w(gspca_dev->dev, 0x11, &rgb_value, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	/* translate 0 - 255 to a number of fps in a 30 - 1 scale */
+	int fps = 30 - sd->exposure * 29 / 255;
+
+	switch(sd->sensor) {
+	case SENSOR_TAS5110: {
+		__u8 reg;
+
+		/* register 19's high nibble contains the sn9c10x clock divider
+		   The high nibble configures the no fps according to the
+		   formula: 60 / high_nibble. With a maximum of 30 fps */
+		reg = 60 / fps;
+		if (reg > 15)
+			reg = 15;
+		reg = (reg << 4) | 0x0b;
+		reg_w(gspca_dev->dev, 0x19, &reg, 1);
+		break; }
+	case SENSOR_OV6650: {
+		__u8 i2c[] = {0xa0, 0x60, 0x11, 0xc0, 0x00, 0x00, 0x00, 0x10};
+		i2c[3] = 30 / fps - 1;
+		if (i2c[3] > 15)
+			i2c[3] = 15;
+		i2c[3] |= 0xc0;
+		if (i2c_w(gspca_dev->dev, i2c) < 0)
+			PDEBUG(D_ERR, "i2c error exposure");
+		break; }
+	}
+}
+
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int avg_lum = atomic_read(&sd->avg_lum);
+
+	if (avg_lum == -1)
+		return;
+
+	if (sd->autogain_ignore_frames > 0)
+		sd->autogain_ignore_frames--;
+	else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
+			sd->brightness * DESIRED_AVG_LUM / 127,
+			AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE))
+		sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
 }
 
 /* this function is called at probe time */
@@ -519,7 +633,13 @@
 	__u16 product;
 	int sif = 0;
 
+	/* nctrls depends upon the sensor, so we use a per cam copy */
+	memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc));
+	gspca_dev->sd_desc = &sd->sd_desc;
+
 	sd->fr_h_sz = 12;		/* default size of the frame header */
+	sd->sd_desc.nctrls = 2;		/* default no ctrls */
+
 /*	vendor = id->idVendor; */
 	product = id->idProduct;
 /*	switch (vendor) { */
@@ -529,6 +649,8 @@
 		case 0x6005:			/* SN9C101 */
 		case 0x6007:			/* SN9C101 */
 			sd->sensor = SENSOR_TAS5110;
+			sd->sd_desc.nctrls = 4;
+			sd->sd_desc.dq_callback = do_autogain;
 			sif = 1;
 			break;
 		case 0x6009:			/* SN9C101 */
@@ -539,6 +661,8 @@
 			break;
 		case 0x6011:			/* SN9C101 - SN9C101G */
 			sd->sensor = SENSOR_OV6650;
+			sd->sd_desc.nctrls = 4;
+			sd->sd_desc.dq_callback = do_autogain;
 			sif = 1;
 			break;
 		case 0x6019:			/* SN9C101 */
@@ -578,8 +702,10 @@
 		cam->cam_mode = sif_mode;
 		cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
 	}
-	sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
-	sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+	sd->brightness = BRIGHTNESS_DEF;
+	sd->gain = GAIN_DEF;
+	sd->exposure = EXPOSURE_DEF;
+	sd->autogain = 1;
 	if (sd->sensor == SENSOR_OV7630_3)	/* jfm: from win trace */
 		reg_w(gspca_dev->dev, 0x01, probe_ov7630, sizeof probe_ov7630);
 	return 0;
@@ -762,8 +888,12 @@
 	reg_w(dev, 0x18, &reg17_19[1], 2);
 	msleep(20);
 
-	setcontrast(gspca_dev);
+	setgain(gspca_dev);
 	setbrightness(gspca_dev);
+	setexposure(gspca_dev);
+
+	sd->autogain_ignore_frames = 0;
+	atomic_set(&sd->avg_lum, -1);
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -787,8 +917,8 @@
 			unsigned char *data,		/* isoc packet */
 			int len)			/* iso packet length */
 {
-	struct sd *sd;
 	int i;
+	struct sd *sd = (struct sd *) gspca_dev;
 
 	if (len > 6 && len < 24) {
 		for (i = 0; i < len - 6; i++) {
@@ -800,7 +930,16 @@
 			    && data[5 + i] == 0x96) {	/* start of frame */
 				frame = gspca_frame_add(gspca_dev, LAST_PACKET,
 							frame, data, 0);
-				sd = (struct sd *) gspca_dev;
+				if (i < (len - 10)) {
+					atomic_set(&sd->avg_lum, data[i + 8] +
+							(data[i + 9] << 8));
+				} else {
+					atomic_set(&sd->avg_lum, -1);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+					PDEBUG(D_STREAM, "packet too short to "
+						"get avg brightness");
+#endif
+				}
 				data += i + sd->fr_h_sz;
 				len -= i + sd->fr_h_sz;
 				gspca_frame_add(gspca_dev, FIRST_PACKET,
@@ -831,26 +970,74 @@
 	return 0;
 }
 
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	sd->contrast = val;
+	sd->gain = val;
 	if (gspca_dev->streaming)
-		setcontrast(gspca_dev);
+		setgain(gspca_dev);
 	return 0;
 }
 
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	*val = sd->contrast;
+	*val = sd->gain;
+	return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->exposure = val;
+	if (gspca_dev->streaming)
+		setexposure(gspca_dev);
+	return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->exposure;
+	return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->autogain = val;
+	/* 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 = EXPOSURE_DEF;
+		sd->gain = GAIN_DEF;
+		if (gspca_dev->streaming) {
+			sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+			setexposure(gspca_dev);
+			setgain(gspca_dev);
+		}
+	}
+
+	return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->autogain;
 	return 0;
 }
 
 /* sub-driver description */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
 	.name = MODULE_NAME,
 	.ctrls = sd_ctrls,
 	.nctrls = ARRAY_SIZE(sd_ctrls),

[-- Attachment #3: Type: text/plain, Size: 164 bytes --]

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-07-09 13:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-09 13:31 PATCH: gspca-sn9c102-autoexposure.patch Hans de Goede

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.