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

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

[media] gspca_sonixj: Convert to the control framework

parent 5ce60d79
Loading
Loading
Loading
Loading
+164 −381
Original line number Diff line number Diff line
@@ -31,32 +31,26 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
MODULE_LICENSE("GPL");

/* controls */
enum e_ctrl {
	BRIGHTNESS,
	CONTRAST,
	COLORS,
	BLUE,
	RED,
	GAMMA,
	EXPOSURE,
	AUTOGAIN,
	GAIN,
	HFLIP,
	VFLIP,
	SHARPNESS,
	ILLUM,
	FREQ,
	NCTRLS		/* number of controls */
};

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

	struct gspca_ctrl ctrls[NCTRLS];

	atomic_t avg_lum;
	struct v4l2_ctrl *brightness;
	struct v4l2_ctrl *contrast;
	struct v4l2_ctrl *saturation;
	struct { /* red/blue balance control cluster */
		struct v4l2_ctrl *red_bal;
		struct v4l2_ctrl *blue_bal;
	};
	struct { /* hflip/vflip control cluster */
		struct v4l2_ctrl *vflip;
		struct v4l2_ctrl *hflip;
	};
	struct v4l2_ctrl *gamma;
	struct v4l2_ctrl *illum;
	struct v4l2_ctrl *sharpness;
	struct v4l2_ctrl *freq;
	u32 exposure;

	struct work_struct work;
@@ -127,283 +121,6 @@ static void qual_upd(struct work_struct *work);
#define SEN_CLK_EN	0x20	/* enable sensor clock */
#define DEF_EN		0x80	/* defect pixel by 0: soft, 1: hard */

/* V4L2 controls supported by the driver */
static void setbrightness(struct gspca_dev *gspca_dev);
static void setcontrast(struct gspca_dev *gspca_dev);
static void setcolors(struct gspca_dev *gspca_dev);
static void setredblue(struct gspca_dev *gspca_dev);
static void setgamma(struct gspca_dev *gspca_dev);
static void setexposure(struct gspca_dev *gspca_dev);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static void setgain(struct gspca_dev *gspca_dev);
static void sethvflip(struct gspca_dev *gspca_dev);
static void setsharpness(struct gspca_dev *gspca_dev);
static void setillum(struct gspca_dev *gspca_dev);
static void setfreq(struct gspca_dev *gspca_dev);

static const struct ctrl sd_ctrls[NCTRLS] = {
[BRIGHTNESS] = {
	    {
		.id      = V4L2_CID_BRIGHTNESS,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Brightness",
		.minimum = 0,
		.maximum = 0xff,
		.step    = 1,
		.default_value = 0x80,
	    },
	    .set_control = setbrightness
	},
[CONTRAST] = {
	    {
		.id      = V4L2_CID_CONTRAST,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Contrast",
		.minimum = 0,
#define CONTRAST_MAX 127
		.maximum = CONTRAST_MAX,
		.step    = 1,
		.default_value = 20,
	    },
	    .set_control = setcontrast
	},
[COLORS] = {
	    {
		.id      = V4L2_CID_SATURATION,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Saturation",
		.minimum = 0,
		.maximum = 40,
		.step    = 1,
#define COLORS_DEF 25
		.default_value = COLORS_DEF,
	    },
	    .set_control = setcolors
	},
[BLUE] = {
	    {
		.id      = V4L2_CID_BLUE_BALANCE,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Blue Balance",
		.minimum = 24,
		.maximum = 40,
		.step    = 1,
		.default_value = 32,
	    },
	    .set_control = setredblue
	},
[RED] = {
	    {
		.id      = V4L2_CID_RED_BALANCE,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Red Balance",
		.minimum = 24,
		.maximum = 40,
		.step    = 1,
		.default_value = 32,
	    },
	    .set_control = setredblue
	},
[GAMMA] = {
	    {
		.id      = V4L2_CID_GAMMA,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Gamma",
		.minimum = 0,
		.maximum = 40,
		.step    = 1,
#define GAMMA_DEF 20
		.default_value = GAMMA_DEF,
	    },
	    .set_control = setgamma
	},
[EXPOSURE] = {
	    {
		.id      = V4L2_CID_EXPOSURE,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Exposure",
		.minimum = 500,
		.maximum = 1500,
		.step    = 1,
		.default_value = 1024
	    },
	    .set_control = setexposure
	},
[AUTOGAIN] = {
	    {
		.id      = V4L2_CID_AUTOGAIN,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Auto Gain",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 1
	    },
	    .set = sd_setautogain,
	},
[GAIN] = {
	    {
		.id      = V4L2_CID_GAIN,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Gain",
		.minimum = 4,
		.maximum = 49,
		.step    = 1,
		.default_value = 15
	    },
	    .set_control = setgain
	},
[HFLIP] = {
	    {
		.id      = V4L2_CID_HFLIP,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Mirror",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 0,
	    },
	    .set_control = sethvflip
	},
[VFLIP] = {
	    {
		.id      = V4L2_CID_VFLIP,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Vflip",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 0,
	    },
	    .set_control = sethvflip
	},
[SHARPNESS] = {
	    {
		.id	 = V4L2_CID_SHARPNESS,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Sharpness",
		.minimum = 0,
		.maximum = 255,
		.step    = 1,
		.default_value = 90,
	    },
	    .set_control = setsharpness
	},
[ILLUM] = {
	    {
		.id      = V4L2_CID_ILLUMINATORS_1,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Illuminator / infrared",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 0,
	    },
	    .set_control = setillum
	},
/* ov7630/ov7648/ov7660 only */
[FREQ] = {
	    {
		.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
		.type    = V4L2_CTRL_TYPE_MENU,
		.name    = "Light frequency filter",
		.minimum = 0,
		.maximum = 2,	/* 0: 0, 1: 50Hz, 2:60Hz */
		.step    = 1,
		.default_value = 1,
	    },
	    .set_control = setfreq
	},
};

/* table of the disabled controls */
static const __u32 ctrl_dis[] = {
[SENSOR_ADCM1700] =	(1 << EXPOSURE) |
			(1 << AUTOGAIN) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_GC0307] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_HV7131R] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << FREQ),

[SENSOR_MI0360] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_MI0360B] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_MO4000] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_MT9V111] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_OM6802] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_OV7630] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP),

[SENSOR_OV7648] =	(1 << EXPOSURE) |
			(1 << GAIN) |
			(1 << HFLIP),

[SENSOR_OV7660] =	(1 << EXPOSURE) |
			(1 << AUTOGAIN) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP),

[SENSOR_PO1030] =	(1 << EXPOSURE) |
			(1 << AUTOGAIN) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_PO2030N] =	(1 << FREQ),

[SENSOR_SOI768] =	(1 << EXPOSURE) |
			(1 << AUTOGAIN) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),

[SENSOR_SP80708] =	(1 << EXPOSURE) |
			(1 << AUTOGAIN) |
			(1 << GAIN) |
			(1 << HFLIP) |
			(1 << VFLIP) |
			(1 << FREQ),
};

static const struct v4l2_pix_format cif_mode[] = {
	{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
		.bytesperline = 352,
@@ -1822,7 +1539,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
		cam->nmodes = ARRAY_SIZE(vga_mode);
	}
	cam->npkt = 24;			/* 24 packets per ISOC message */
	cam->ctrls = sd->ctrls;

	sd->ag_cnt = -1;
	sd->quality = QUALITY_DEF;
@@ -1888,9 +1604,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
		break;
	}

	if (sd->sensor == SENSOR_OM6802)
		sd->ctrls[SHARPNESS].def = 0x10;

	/* Note we do not disable the sensor clock here (power saving mode),
	   as that also disables the button on the cam. */
	reg_w1(gspca_dev, 0xf1, 0x00);
@@ -1899,13 +1612,92 @@ static int sd_init(struct gspca_dev *gspca_dev)
	sn9c1xx = sn_tb[sd->sensor];
	sd->i2c_addr = sn9c1xx[9];

	gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
	if (!(sd->flags & F_ILLUM))
		gspca_dev->ctrl_dis |= (1 << ILLUM);

	return gspca_dev->usb_err;
}

static int sd_s_ctrl(struct v4l2_ctrl *ctrl);

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

/* this function is called at probe time */
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, 14);

	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
#define CONTRAST_MAX 127
	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_CONTRAST, 0, CONTRAST_MAX, 1, 20);
#define COLORS_DEF 25
	sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_SATURATION, 0, 40, 1, COLORS_DEF);
	sd->red_bal = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_RED_BALANCE, 24, 40, 1, 32);
	sd->blue_bal = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_BLUE_BALANCE, 24, 40, 1, 32);
#define GAMMA_DEF 20
	sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_GAMMA, 0, 40, 1, GAMMA_DEF);

	if (sd->sensor == SENSOR_OM6802)
		sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_SHARPNESS, 0, 255, 1, 16);
	else
		sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_SHARPNESS, 0, 255, 1, 90);

	if (sd->flags & F_ILLUM)
		sd->illum = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);

	if (sd->sensor == SENSOR_PO2030N) {
		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_EXPOSURE, 500, 1500, 1, 1024);
		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_GAIN, 4, 49, 1, 15);
		sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_HFLIP, 0, 1, 1, 0);
	}

	if (sd->sensor != SENSOR_ADCM1700 && sd->sensor != SENSOR_OV7660 &&
	    sd->sensor != SENSOR_PO1030 && sd->sensor != SENSOR_SOI768 &&
	    sd->sensor != SENSOR_SP80708)
		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);

	if (sd->sensor == SENSOR_HV7131R || sd->sensor == SENSOR_OV7630 ||
	    sd->sensor == SENSOR_OV7648 || sd->sensor == SENSOR_PO2030N)
		sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_VFLIP, 0, 1, 1, 0);

	if (sd->sensor == SENSOR_OV7630 || sd->sensor == SENSOR_OV7648 ||
	    sd->sensor == SENSOR_OV7660)
		sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
			V4L2_CID_POWER_LINE_FREQUENCY,
			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);

	if (hdl->error) {
		pr_err("Could not initialize controls\n");
		return hdl->error;
	}

	v4l2_ctrl_cluster(2, &sd->red_bal);
	if (sd->sensor == SENSOR_PO2030N) {
		v4l2_ctrl_cluster(2, &sd->vflip);
		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
	}

	return 0;
}

static u32 expo_adjust(struct gspca_dev *gspca_dev,
			u32 expo)
{
@@ -2014,10 +1806,9 @@ static void setbrightness(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
	unsigned int expo;
	int brightness;
	int brightness = sd->brightness->val;
	u8 k2;

	brightness = sd->ctrls[BRIGHTNESS].val;
	k2 = (brightness - 0x80) >> 2;
	switch (sd->sensor) {
	case SENSOR_ADCM1700:
@@ -2064,7 +1855,7 @@ static void setcontrast(struct gspca_dev *gspca_dev)
	u8 k2;
	u8 contrast[6];

	k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1)
	k2 = sd->contrast->val * 37 / (CONTRAST_MAX + 1)
				+ 37;		/* 37..73 */
	contrast[0] = (k2 + 1) / 2;		/* red */
	contrast[1] = 0;
@@ -2090,7 +1881,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
		 60, -51, -9		/* VR VG VB */
	};

	colors = sd->ctrls[COLORS].val;
	colors = sd->saturation->val;
	if (sd->sensor == SENSOR_MI0360B)
		uv = uv_mi0360b;
	else
@@ -2112,14 +1903,14 @@ static void setredblue(struct gspca_dev *gspca_dev)
			{0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10};

		/* 0x40 = normal value = gain x 1 */
		rg1b[3] = sd->ctrls[RED].val * 2;
		rg1b[5] = sd->ctrls[BLUE].val * 2;
		rg1b[3] = sd->red_bal->val * 2;
		rg1b[5] = sd->blue_bal->val * 2;
		i2c_w8(gspca_dev, rg1b);
		return;
	}
	reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val);
	reg_w1(gspca_dev, 0x05, sd->red_bal->val);
/*	reg_w1(gspca_dev, 0x07, 32); */
	reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val);
	reg_w1(gspca_dev, 0x06, sd->blue_bal->val);
}

static void setgamma(struct gspca_dev *gspca_dev)
@@ -2153,7 +1944,7 @@ static void setgamma(struct gspca_dev *gspca_dev)
		break;
	}

	val = sd->ctrls[GAMMA].val;
	val = sd->gamma->val;
	for (i = 0; i < sizeof gamma; i++)
		gamma[i] = gamma_base[i]
			+ delta[i] * (val - GAMMA_DEF) / 32;
@@ -2168,11 +1959,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
		u8 rexpo[] =		/* 1a: expo H, 1b: expo M */
			{0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};

		rexpo[3] = sd->ctrls[EXPOSURE].val >> 8;
		rexpo[3] = gspca_dev->exposure->val >> 8;
		i2c_w8(gspca_dev, rexpo);
		msleep(6);
		rexpo[2] = 0x1b;
		rexpo[3] = sd->ctrls[EXPOSURE].val;
		rexpo[3] = gspca_dev->exposure->val;
		i2c_w8(gspca_dev, rexpo);
	}
}
@@ -2181,8 +1972,6 @@ static void setautogain(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
		return;
	switch (sd->sensor) {
	case SENSOR_OV7630:
	case SENSOR_OV7648: {
@@ -2192,13 +1981,13 @@ static void setautogain(struct gspca_dev *gspca_dev)
			comb = 0xc0;
		else
			comb = 0xa0;
		if (sd->ctrls[AUTOGAIN].val)
		if (gspca_dev->autogain->val)
			comb |= 0x03;
		i2c_w1(&sd->gspca_dev, 0x13, comb);
		return;
	    }
	}
	if (sd->ctrls[AUTOGAIN].val)
	if (gspca_dev->autogain->val)
		sd->ag_cnt = AG_CNT_START;
	else
		sd->ag_cnt = -1;
@@ -2212,7 +2001,7 @@ static void setgain(struct gspca_dev *gspca_dev)
		u8 rgain[] =		/* 15: gain */
			{0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};

		rgain[3] = sd->ctrls[GAIN].val;
		rgain[3] = gspca_dev->gain->val;
		i2c_w8(gspca_dev, rgain);
	}
}
@@ -2225,19 +2014,19 @@ static void sethvflip(struct gspca_dev *gspca_dev)
	switch (sd->sensor) {
	case SENSOR_HV7131R:
		comn = 0x18;			/* clkdiv = 1, ablcen = 1 */
		if (sd->ctrls[VFLIP].val)
		if (sd->vflip->val)
			comn |= 0x01;
		i2c_w1(gspca_dev, 0x01, comn);	/* sctra */
		break;
	case SENSOR_OV7630:
		comn = 0x02;
		if (!sd->ctrls[VFLIP].val)
		if (!sd->vflip->val)
			comn |= 0x80;
		i2c_w1(gspca_dev, 0x75, comn);
		break;
	case SENSOR_OV7648:
		comn = 0x06;
		if (sd->ctrls[VFLIP].val)
		if (sd->vflip->val)
			comn |= 0x80;
		i2c_w1(gspca_dev, 0x75, comn);
		break;
@@ -2251,9 +2040,9 @@ static void sethvflip(struct gspca_dev *gspca_dev)
		 * bit3-0: X
		 */
		comn = 0x0a;
		if (sd->ctrls[HFLIP].val)
		if (sd->hflip->val)
			comn |= 0x80;
		if (sd->ctrls[VFLIP].val)
		if (sd->vflip->val)
			comn |= 0x40;
		i2c_w1(&sd->gspca_dev, 0x1e, comn);
		break;
@@ -2264,23 +2053,21 @@ static void setsharpness(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;

	reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val);
	reg_w1(gspca_dev, 0x99, sd->sharpness->val);
}

static void setillum(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (gspca_dev->ctrl_dis & (1 << ILLUM))
		return;
	switch (sd->sensor) {
	case SENSOR_ADCM1700:
		reg_w1(gspca_dev, 0x02,				/* gpio */
			sd->ctrls[ILLUM].val ? 0x64 : 0x60);
			sd->illum->val ? 0x64 : 0x60);
		break;
	case SENSOR_MT9V111:
		reg_w1(gspca_dev, 0x02,
			sd->ctrls[ILLUM].val ? 0x77 : 0x74);
			sd->illum->val ? 0x77 : 0x74);
/* should have been: */
/*						0x55 : 0x54);	* 370i */
/*						0x66 : 0x64);	* Clip */
@@ -2292,13 +2079,11 @@ static void setfreq(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (gspca_dev->ctrl_dis & (1 << FREQ))
		return;
	if (sd->sensor == SENSOR_OV7660) {
		u8 com8;

		com8 = 0xdf;		/* auto gain/wb/expo */
		switch (sd->ctrls[FREQ].val) {
		switch (sd->freq->val) {
		case 0: /* Banding filter disabled */
			i2c_w1(gspca_dev, 0x13, com8 | 0x20);
			break;
@@ -2326,7 +2111,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
			break;
		}

		switch (sd->ctrls[FREQ].val) {
		switch (sd->freq->val) {
		case 0: /* Banding filter disabled */
			break;
		case 1: /* 50 hz (filter on and framerate adj) */
@@ -2698,17 +2483,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
	sd->reg01 = reg01;
	sd->reg17 = reg17;

	sethvflip(gspca_dev);
	setbrightness(gspca_dev);
	setcontrast(gspca_dev);
	setcolors(gspca_dev);
	setautogain(gspca_dev);
	if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) {
		setexposure(gspca_dev);
		setgain(gspca_dev);
	}
	setfreq(gspca_dev);

	sd->pktsz = sd->npkt = 0;
	sd->nchg = sd->short_mark = 0;
	sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
@@ -2803,9 +2577,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
	}
}

#define WANT_REGULAR_AUTOGAIN
#include "autogain_functions.h"

static void do_autogain(struct gspca_dev *gspca_dev)
{
	struct sd *sd = (struct sd *) gspca_dev;
@@ -2825,7 +2596,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
	PDEBUG(D_FRAM, "mean lum %d", delta);

	if (sd->sensor == SENSOR_PO2030N) {
		auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta,
		gspca_expo_autogain(gspca_dev, delta, luma_mean, luma_delta,
					15, 1024);
		return;
	}
@@ -3042,40 +2813,54 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
	}
}

static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct sd *sd = (struct sd *) gspca_dev;
	struct gspca_dev *gspca_dev =
		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);

	sd->ctrls[AUTOGAIN].val = val;
	if (val)
		gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
	else
		gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN);
	if (gspca_dev->streaming)
		setautogain(gspca_dev);
	return gspca_dev->usb_err;
}
	gspca_dev->usb_err = 0;

static int sd_querymenu(struct gspca_dev *gspca_dev,
			struct v4l2_querymenu *menu)
{
	switch (menu->id) {
	case V4L2_CID_POWER_LINE_FREQUENCY:
		switch (menu->index) {
		case 0:		/* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
			strcpy((char *) menu->name, "NoFliker");
			return 0;
		case 1:		/* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
			strcpy((char *) menu->name, "50 Hz");
	if (!gspca_dev->streaming)
		return 0;
		case 2:		/* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
			strcpy((char *) menu->name, "60 Hz");
			return 0;
		}

	switch (ctrl->id) {
	case V4L2_CID_BRIGHTNESS:
		setbrightness(gspca_dev);
		break;
	}
	case V4L2_CID_CONTRAST:
		setcontrast(gspca_dev);
		break;
	case V4L2_CID_SATURATION:
		setcolors(gspca_dev);
		break;
	case V4L2_CID_RED_BALANCE:
		setredblue(gspca_dev);
		break;
	case V4L2_CID_GAMMA:
		setgamma(gspca_dev);
		break;
	case V4L2_CID_AUTOGAIN:
		setautogain(gspca_dev);
		setexposure(gspca_dev);
		setgain(gspca_dev);
		break;
	case V4L2_CID_VFLIP:
		sethvflip(gspca_dev);
		break;
	case V4L2_CID_SHARPNESS:
		setsharpness(gspca_dev);
		break;
	case V4L2_CID_ILLUMINATORS_1:
		setillum(gspca_dev);
		break;
	case V4L2_CID_POWER_LINE_FREQUENCY:
		setfreq(gspca_dev);
		break;
	default:
		return -EINVAL;
	}
	return gspca_dev->usb_err;
}

#if IS_ENABLED(CONFIG_INPUT)
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
@@ -3099,16 +2884,14 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
	.name = MODULE_NAME,
	.ctrls = sd_ctrls,
	.nctrls = NCTRLS,
	.config = sd_config,
	.init = sd_init,
	.init_controls = sd_init_controls,
	.start = sd_start,
	.stopN = sd_stopN,
	.stop0 = sd_stop0,
	.pkt_scan = sd_pkt_scan,
	.dq_callback = do_autogain,
	.querymenu = sd_querymenu,
#if IS_ENABLED(CONFIG_INPUT)
	.int_pkt_scan = sd_int_pkt_scan,
#endif