Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0cfe9de2 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab
Browse files

[media] gspca-topro: convert to the control framework

parent 930806cb
Loading
Loading
Loading
Loading
+226 −233
Original line number Diff line number Diff line
@@ -120,24 +120,13 @@ static const u8 jpeg_head[] = {
#define JPEG_HDR_SZ 521
};

enum e_ctrl {
	EXPOSURE,
	QUALITY,
	SHARPNESS,
	RGAIN,
	GAIN,
	BGAIN,
	GAMMA,
	AUTOGAIN,
	NCTRLS		/* number of controls */
};

#define AUTOGAIN_DEF 1

struct sd {
	struct gspca_dev gspca_dev;	/* !! must be the first item */

	struct gspca_ctrl ctrls[NCTRLS];
	struct v4l2_ctrl *jpegqual;
	struct v4l2_ctrl *sharpness;
	struct v4l2_ctrl *gamma;
	struct v4l2_ctrl *blue;
	struct v4l2_ctrl *red;

	u8 framerate;
	u8 quality;		/* webcam current JPEG quality (0..16) */
@@ -1415,32 +1404,33 @@ static void soi763a_6810_init(struct gspca_dev *gspca_dev)
}

/* set the gain and exposure */
static void setexposure(struct gspca_dev *gspca_dev)
static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
							s32 blue, s32 red)
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (sd->sensor == SENSOR_CX0342) {
		int expo;

		expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
		expo = (expo << 2) - 1;
		i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
		i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
		if (sd->bridge == BRIDGE_TP6800)
			i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
						sd->ctrls[GAIN].val >> 8);
		i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
						gain >> 8);
		i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
		if (sd->bridge == BRIDGE_TP6800)
			i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
						sd->ctrls[GAIN].val >> 8);
		i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
					gain >> 8);
		i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
		if (sd->sensor == SENSOR_CX0342) {
			if (sd->bridge == BRIDGE_TP6800)
				i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
						sd->ctrls[BGAIN].val >> 8);
		i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
						blue >> 8);
			i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
			if (sd->bridge == BRIDGE_TP6800)
				i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
						sd->ctrls[RGAIN].val >> 8);
		i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
						red >> 8);
			i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
		}
		i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
				sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
		return;
@@ -1448,10 +1438,10 @@ static void setexposure(struct gspca_dev *gspca_dev)

	/* soi763a */
	i2c_w(gspca_dev, 0x10,		/* AEC_H (exposure time) */
			 sd->ctrls[EXPOSURE].val);
			 expo);
/*	i2c_w(gspca_dev, 0x76, 0x02);	 * AEC_L ([1:0] */
	i2c_w(gspca_dev, 0x00,		/* gain */
			 sd->ctrls[GAIN].val);
			 gain);
}

/* set the JPEG quantization tables */
@@ -1472,12 +1462,10 @@ static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
}

/* set the JPEG compression quality factor */
static void setquality(struct gspca_dev *gspca_dev)
static void setquality(struct gspca_dev *gspca_dev, s32 q)
{
	struct sd *sd = (struct sd *) gspca_dev;
	u16 q;

	q = sd->ctrls[QUALITY].val;
	if (q != 16)
		q = 15 - q;

@@ -1508,10 +1496,9 @@ static const u8 color_gain[NSENSORS][18] = {
	 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03},	/* V R/G/B */
};

static void setgamma(struct gspca_dev *gspca_dev)
static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
{
	struct sd *sd = (struct sd *) gspca_dev;
	int gamma;
#define NGAMMA 6
	static const u8 gamma_tb[NGAMMA][3][1024] = {
	    {				/* gamma 0 - from tp6800 + soi763a */
@@ -3836,7 +3823,6 @@ static void setgamma(struct gspca_dev *gspca_dev)
	if (sd->bridge == BRIDGE_TP6810)
		reg_w(gspca_dev, 0x02, 0x28);
/*	msleep(50); */
	gamma = sd->ctrls[GAMMA].val;
	bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
	bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
	bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
@@ -3864,43 +3850,35 @@ static void setgamma(struct gspca_dev *gspca_dev)
/*	msleep(50); */
}

static void setsharpness(struct gspca_dev *gspca_dev)
static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	u8 val;

	if (sd->bridge == BRIDGE_TP6800) {
		val = sd->ctrls[SHARPNESS].val
				| 0x08;		/* grid compensation enable */
		val |= 0x08;		/* grid compensation enable */
		if (gspca_dev->width == 640)
			reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
		else
			val |= 0x04;		/* scaling down enable */
		reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
	} else {
		val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
		val = (val << 5) | 0x08;
		reg_w(gspca_dev, 0x59, val);
	}
}

static void setautogain(struct gspca_dev *gspca_dev)
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
		return;
	if (sd->ctrls[AUTOGAIN].val) {
		sd->ag_cnt = AG_CNT_START;
		gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
	} else {
		sd->ag_cnt = -1;
		gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
	}
	sd->ag_cnt = val ? AG_CNT_START : -1;
}

/* set the resolution for sensor cx0342 */
static void set_resolution(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;

	reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
	if (gspca_dev->width == 320) {
		reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
@@ -3926,8 +3904,9 @@ static void set_resolution(struct gspca_dev *gspca_dev)
	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
				ARRAY_SIZE(color_gain[0]));
	setgamma(gspca_dev);
	setquality(gspca_dev);
	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
	if (sd->sensor == SENSOR_SOI763A)
		setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
}

/* convert the frame rate to a tp68x0 value */
@@ -3963,7 +3942,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
	return i;
}

static void setframerate(struct gspca_dev *gspca_dev)
static void setframerate(struct gspca_dev *gspca_dev, s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	u8 fr_idx;
@@ -3974,7 +3953,7 @@ static void setframerate(struct gspca_dev *gspca_dev)
		reg_r(gspca_dev, 0x7b);
		reg_w(gspca_dev, 0x7b,
			sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
		if (sd->ctrls[EXPOSURE].val >= 128)
		if (val >= 128)
			fr_idx = 0xf0;		/* lower frame rate */
	}

@@ -3984,43 +3963,43 @@ static void setframerate(struct gspca_dev *gspca_dev)
		i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
}

static void setrgain(struct gspca_dev *gspca_dev)
static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
{
	struct sd *sd = (struct sd *) gspca_dev;
	int rgain;

	rgain = sd->ctrls[RGAIN].val;
	i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
	i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
}

static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
static int sd_setgain(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	s32 val = gspca_dev->gain->val;

	if (sd->sensor == SENSOR_CX0342) {
		sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
					* val / sd->ctrls[GAIN].val;
		if (sd->ctrls[BGAIN].val > 4095)
			sd->ctrls[BGAIN].val = 4095;
		sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
					* val / sd->ctrls[GAIN].val;
		if (sd->ctrls[RGAIN].val > 4095)
			sd->ctrls[RGAIN].val = 4095;
		s32 old = gspca_dev->gain->cur.val ?
					gspca_dev->gain->cur.val : 1;

		sd->blue->val = sd->blue->val * val / old;
		if (sd->blue->val > 4095)
			sd->blue->val = 4095;
		sd->red->val = sd->red->val * val / old;
		if (sd->red->val > 4095)
			sd->red->val = 4095;
	}
	if (gspca_dev->streaming) {
		if (sd->sensor == SENSOR_CX0342)
			setexposure(gspca_dev, gspca_dev->exposure->val,
					gspca_dev->gain->val,
					sd->blue->val, sd->red->val);
		else
			setexposure(gspca_dev, gspca_dev->exposure->val,
					gspca_dev->gain->val, 0, 0);
	}
	sd->ctrls[GAIN].val = val;
	if (gspca_dev->streaming)
		setexposure(gspca_dev);
	return gspca_dev->usb_err;
}

static void setbgain(struct gspca_dev *gspca_dev)
static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
{
	struct sd *sd = (struct sd *) gspca_dev;
	int bgain;

	bgain = sd->ctrls[BGAIN].val;
	i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
	i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
@@ -4040,7 +4019,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
			framerates : framerates_6810;

	sd->framerate = 30;		/* default: 30 fps */
	gspca_dev->cam.ctrls = sd->ctrls;
	return 0;
}

@@ -4108,32 +4086,16 @@ static int sd_init(struct gspca_dev *gspca_dev)
	}
	if (sd->sensor == SENSOR_SOI763A) {
		pr_info("Sensor soi763a\n");
		sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
		sd->ctrls[GAIN].max = 15;
		sd->ctrls[GAIN].def = 3;
		gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
		if (sd->bridge == BRIDGE_TP6810) {
			soi763a_6810_init(gspca_dev);
#if AUTOGAIN_DEF
			gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
#endif
		} else {
			gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
		}
	} else {
		pr_info("Sensor cx0342\n");
		if (sd->bridge == BRIDGE_TP6810) {
			cx0342_6810_init(gspca_dev);
#if AUTOGAIN_DEF
			gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
#endif
		} else {
			gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
		}
	}

	if (sd->bridge == BRIDGE_TP6810)
		sd->ctrls[QUALITY].def = 0;	/* auto quality */
	set_dqt(gspca_dev, 0);
	return 0;
}
@@ -4207,8 +4169,9 @@ static void set_led(struct gspca_dev *gspca_dev, int on)

static void cx0342_6800_start(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	static const struct cmd reg_init[] = {
/*fixme: is this usefull?*/
		/* fixme: is this useful? */
		{TP6800_R17_GPIO_IO, 0x9f},
		{TP6800_R16_GPIO_PD, 0x40},
		{TP6800_R10_SIF_TYPE, 0x00},	/* i2c 8 bits */
@@ -4279,13 +4242,21 @@ static void cx0342_6800_start(struct gspca_dev *gspca_dev)
	reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
	i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
	setexposure(gspca_dev);
	if (sd->sensor == SENSOR_CX0342)
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain),
			v4l2_ctrl_g_ctrl(sd->blue),
			v4l2_ctrl_g_ctrl(sd->red));
	else
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
	set_led(gspca_dev, 1);
	set_resolution(gspca_dev);
}

static void cx0342_6810_start(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	static const struct cmd sensor_init_2[] = {
		{CX0342_EXPO_LINE_L, 0x6f},
		{CX0342_EXPO_LINE_H, 0x02},
@@ -4366,10 +4337,10 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
		reg_w(gspca_dev, 0x07, 0x85);
		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);	/* qvga */
	}
	setgamma(gspca_dev);
	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
	reg_w_buf(gspca_dev, tp6810_bridge_start,
			ARRAY_SIZE(tp6810_bridge_start));
	setsharpness(gspca_dev);
	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
				ARRAY_SIZE(color_gain[0]));
	reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
@@ -4380,11 +4351,12 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
	i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));

	set_led(gspca_dev, 1);
/*	setquality(gspca_dev); */
/*	setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
}

static void soi763a_6800_start(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	static const struct cmd reg_init[] = {
		{TP6800_R79_QUALITY, 0x04},
		{TP6800_R79_QUALITY, 0x01},
@@ -4484,19 +4456,28 @@ static void soi763a_6800_start(struct gspca_dev *gspca_dev)
	reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
	reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);

	setsharpness(gspca_dev);
	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));

	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
				ARRAY_SIZE(color_gain[0]));

	set_led(gspca_dev, 1);
	setexposure(gspca_dev);
	setquality(gspca_dev);
	setgamma(gspca_dev);
	if (sd->sensor == SENSOR_CX0342)
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain),
			v4l2_ctrl_g_ctrl(sd->blue),
			v4l2_ctrl_g_ctrl(sd->red));
	else
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
	if (sd->sensor == SENSOR_SOI763A)
		setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
}

static void soi763a_6810_start(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	static const struct cmd bridge_init_2[] = {
		{TP6800_R7A_BLK_THRLD, 0x00},
		{TP6800_R79_QUALITY, 0x04},
@@ -4520,7 +4501,14 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
	reg_w(gspca_dev, 0x22, gspca_dev->alt);
	bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
	reg_w(gspca_dev, 0x59, 0x40);
	setexposure(gspca_dev);
	if (sd->sensor == SENSOR_CX0342)
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain),
			v4l2_ctrl_g_ctrl(sd->blue),
			v4l2_ctrl_g_ctrl(sd->red));
	else
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
	reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
	reg_w_buf(gspca_dev, tp6810_ov_init_common,
			ARRAY_SIZE(tp6810_ov_init_common));
@@ -4534,7 +4522,7 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
		reg_w(gspca_dev, 0x07, 0x85);
		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);	/* qvga */
	}
	setgamma(gspca_dev);
	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
	reg_w_buf(gspca_dev, tp6810_bridge_start,
			ARRAY_SIZE(tp6810_bridge_start));

@@ -4545,12 +4533,19 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)

	reg_w(gspca_dev, 0x00, 0x00);

	setsharpness(gspca_dev);
	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
				ARRAY_SIZE(color_gain[0]));
	set_led(gspca_dev, 1);
	reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
	setexposure(gspca_dev);
	if (sd->sensor == SENSOR_CX0342)
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain),
			v4l2_ctrl_g_ctrl(sd->blue),
			v4l2_ctrl_g_ctrl(sd->red));
	else
		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
	reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
}

@@ -4576,12 +4571,25 @@ static int sd_start(struct gspca_dev *gspca_dev)
		reg_w(gspca_dev, 0x80, 0x03);
		reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);

		setexposure(gspca_dev);
		setquality(gspca_dev);
		setautogain(gspca_dev);
		if (sd->sensor == SENSOR_CX0342)
			setexposure(gspca_dev,
				v4l2_ctrl_g_ctrl(gspca_dev->exposure),
				v4l2_ctrl_g_ctrl(gspca_dev->gain),
				v4l2_ctrl_g_ctrl(sd->blue),
				v4l2_ctrl_g_ctrl(sd->red));
		else
			setexposure(gspca_dev,
				v4l2_ctrl_g_ctrl(gspca_dev->exposure),
				v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
		if (sd->sensor == SENSOR_SOI763A)
			setquality(gspca_dev,
				   v4l2_ctrl_g_ctrl(sd->jpegqual));
		if (sd->bridge == BRIDGE_TP6810)
			setautogain(gspca_dev,
				    v4l2_ctrl_g_ctrl(gspca_dev->autogain));
	}

	setframerate(gspca_dev);
	setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));

	return gspca_dev->usb_err;
}
@@ -4672,12 +4680,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
	}
}

/* -- do autogain -- */
/* gain setting is done in setexposure() for tp6810 */
static void setgain(struct gspca_dev *gspca_dev) {}
#define WANT_REGULAR_AUTOGAIN
#include "autogain_functions.h"

static void sd_dq_callback(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
@@ -4739,17 +4741,19 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
			luma /= 4;
		reg_w(gspca_dev, 0x7d, 0x00);

		expo = sd->ctrls[EXPOSURE].val;
		ret = auto_gain_n_exposure(gspca_dev, luma,
		expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
		ret = gspca_expo_autogain(gspca_dev, luma,
				60,	/* desired luma */
				6,	/* dead zone */
				2,	/* gain knee */
				70);	/* expo knee */
		sd->ag_cnt = AG_CNT_START;
		if (sd->bridge == BRIDGE_TP6810) {
			if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
			 || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
				setframerate(gspca_dev);
			int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);

			if ((expo >= 128 && new_expo < 128)
			 || (expo < 128 && new_expo >= 128))
				setframerate(gspca_dev, new_expo);
		}
		break;
	}
@@ -4789,7 +4793,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,

	sd->framerate = tpf->denominator / tpf->numerator;
	if (gspca_dev->streaming)
		setframerate(gspca_dev);
		setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));

	/* Return the actual framerate */
	i = get_fr_idx(gspca_dev);
@@ -4806,12 +4810,10 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (sd->sensor == SENSOR_SOI763A)
		jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
/*	else
		fixme: TODO
*/
	return gspca_dev->usb_err;
	if (sd->sensor != SENSOR_SOI763A)
		return -ENOTTY;
	v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
	return 0;
}

static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -4819,118 +4821,109 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (sd->sensor != SENSOR_SOI763A)
		return -ENOTTY;
	memset(jcomp, 0, sizeof *jcomp);
	jcomp->quality = jpeg_q[sd->quality];
	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
			| V4L2_JPEG_MARKER_DQT;
	return 0;
}

static struct ctrl sd_ctrls[NCTRLS] = {
[EXPOSURE] = {
	    {
		.id = V4L2_CID_EXPOSURE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "Exposure",
		.minimum = 0x01,
		.maximum = 0xdc,
		.step = 1,
		.default_value = 0x4e,
	    },
	    .set_control = setexposure
	},
[QUALITY] = {
	    {
		.id = V4L2_CID_PRIVATE_BASE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "Compression quality",
		.minimum = 0,
		.maximum = 15,
		.step = 1,
		.default_value = 13,
	    },
	    .set_control = setquality
	},
[RGAIN] = {
	    {
		.id = V4L2_CID_RED_BALANCE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "Red balance",
		.minimum = 0,
		.maximum = 4095,
		.step = 1,
		.default_value = 256,
	    },
	    .set_control = setrgain
	},
[GAIN] = {
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
		.id = V4L2_CID_GAIN,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "Gain",
		.minimum = 0,
		.maximum = 4095,
		.step = 1,
		.default_value = 256,
	    },
	    .set = sd_setgain
	},
[BGAIN] = {
	    {
		.id = V4L2_CID_BLUE_BALANCE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "Blue balance",
		.minimum = 0,
		.maximum = 4095,
		.step = 1,
		.default_value = 256,
	    },
	    .set_control = setbgain
	},
[SHARPNESS] = {
	    {
		.id	 = V4L2_CID_SHARPNESS,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Sharpness",
		.minimum = 0,
		.maximum = 3,
		.step    = 1,
		.default_value = 2,
	    },
	    .set_control = setsharpness
	},
[GAMMA] = {
	    {
		.id      = V4L2_CID_GAMMA,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Gamma",
		.minimum = 0,
		.maximum = NGAMMA - 1,
		.step    = 1,
		.default_value = 1,
	    },
	    .set_control = setgamma
	},
[AUTOGAIN] = {
	    {
		.id      = V4L2_CID_AUTOGAIN,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Auto Gain",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = AUTOGAIN_DEF
	    },
	    .set_control = setautogain
	},
	struct gspca_dev *gspca_dev =
		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
	struct sd *sd = (struct sd *)gspca_dev;

	gspca_dev->usb_err = 0;

	if (!gspca_dev->streaming)
		return 0;

	switch (ctrl->id) {
	case V4L2_CID_SHARPNESS:
		setsharpness(gspca_dev, ctrl->val);
		break;
	case V4L2_CID_GAMMA:
		setgamma(gspca_dev, ctrl->val);
		break;
	case V4L2_CID_BLUE_BALANCE:
		setbgain(gspca_dev, ctrl->val);
		break;
	case V4L2_CID_RED_BALANCE:
		setrgain(gspca_dev, ctrl->val);
		break;
	case V4L2_CID_EXPOSURE:
		sd_setgain(gspca_dev);
		break;
	case V4L2_CID_AUTOGAIN:
		if (ctrl->val)
			break;
		sd_setgain(gspca_dev);
		break;
	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
		jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
		break;
	}
	return gspca_dev->usb_err;
}

static const struct v4l2_ctrl_ops sd_ctrl_ops = {
	.s_ctrl = sd_s_ctrl,
};

static int sd_init_controls(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *)gspca_dev;
	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;

	gspca_dev->vdev.ctrl_handler = hdl;
	v4l2_ctrl_handler_init(hdl, 4);
	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
	if (sd->sensor == SENSOR_CX0342) {
		sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
		sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
	}
	if (sd->sensor == SENSOR_SOI763A)
		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_GAIN, 0, 15, 1, 3);
	else
		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_GAIN, 0, 4095, 1, 256);
	sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_SHARPNESS, 0, 3, 1, 2);
	sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
			(sd->sensor == SENSOR_SOI763A &&
			 sd->bridge == BRIDGE_TP6800) ? 0 : 1);
	if (sd->bridge == BRIDGE_TP6810)
		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
	if (sd->sensor == SENSOR_SOI763A)
		sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_JPEG_COMPRESSION_QUALITY,
			0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);

	if (hdl->error) {
		pr_err("Could not initialize controls\n");
		return hdl->error;
	}
	if (gspca_dev->autogain)
		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
	else
		v4l2_ctrl_cluster(2, &gspca_dev->exposure);
	return 0;
}

static const struct sd_desc sd_desc = {
	.name = KBUILD_MODNAME,
	.ctrls = sd_ctrls,
	.nctrls = NCTRLS,
	.config = sd_config,
	.init = sd_init,
	.init_controls = sd_init_controls,
	.isoc_init = sd_isoc_init,
	.start = sd_start,
	.stopN = sd_stopN,