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

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

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

parent cbc1c94c
Loading
Loading
Loading
Loading
+127 −358
Original line number Diff line number Diff line
@@ -225,6 +225,15 @@ MODULE_LICENSE("GPL");
#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
				sd->params.version.firmwareRevision == (y))

#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
#define BRIGHTNESS_DEF 50
#define CONTRAST_DEF 48
#define SATURATION_DEF 50
#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
#define ILLUMINATORS_1_DEF 0
#define ILLUMINATORS_2_DEF 0
#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY

/* Developer's Guide Table 5 p 3-34
 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
static u8 flicker_jumps[2][2][4] =
@@ -360,135 +369,9 @@ struct sd {
	atomic_t fps;
	int exposure_count;
	u8 exposure_status;
	struct v4l2_ctrl *freq;
	u8 mainsFreq;				/* 0 = 50hz, 1 = 60hz */
	u8 first_frame;
	u8 freq;
};

/* 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_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val);

static const struct ctrl sd_ctrls[] = {
	{
#define BRIGHTNESS_IDX 0
	    {
		.id      = V4L2_CID_BRIGHTNESS,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Brightness",
		.minimum = 0,
		.maximum = 100,
		.step = 1,
#define BRIGHTNESS_DEF 50
		.default_value = BRIGHTNESS_DEF,
		.flags = 0,
	    },
	    .set = sd_setbrightness,
	    .get = sd_getbrightness,
	},
#define CONTRAST_IDX 1
	{
	    {
		.id      = V4L2_CID_CONTRAST,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Contrast",
		.minimum = 0,
		.maximum = 96,
		.step    = 8,
#define CONTRAST_DEF 48
		.default_value = CONTRAST_DEF,
	    },
	    .set = sd_setcontrast,
	    .get = sd_getcontrast,
	},
#define SATURATION_IDX 2
	{
	    {
		.id      = V4L2_CID_SATURATION,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Saturation",
		.minimum = 0,
		.maximum = 100,
		.step    = 1,
#define SATURATION_DEF 50
		.default_value = SATURATION_DEF,
	    },
	    .set = sd_setsaturation,
	    .get = sd_getsaturation,
	},
#define POWER_LINE_FREQUENCY_IDX 3
	{
		{
			.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,
#define FREQ_DEF 1
			.default_value = FREQ_DEF,
		},
		.set = sd_setfreq,
		.get = sd_getfreq,
	},
#define ILLUMINATORS_1_IDX 4
	{
		{
			.id	 = V4L2_CID_ILLUMINATORS_1,
			.type    = V4L2_CTRL_TYPE_BOOLEAN,
			.name    = "Illuminator 1",
			.minimum = 0,
			.maximum = 1,
			.step    = 1,
#define ILLUMINATORS_1_DEF 0
			.default_value = ILLUMINATORS_1_DEF,
		},
		.set = sd_setilluminator1,
		.get = sd_getilluminator1,
	},
#define ILLUMINATORS_2_IDX 5
	{
		{
			.id	 = V4L2_CID_ILLUMINATORS_2,
			.type    = V4L2_CTRL_TYPE_BOOLEAN,
			.name    = "Illuminator 2",
			.minimum = 0,
			.maximum = 1,
			.step    = 1,
#define ILLUMINATORS_2_DEF 0
			.default_value = ILLUMINATORS_2_DEF,
		},
		.set = sd_setilluminator2,
		.get = sd_getilluminator2,
	},
#define COMP_TARGET_IDX 6
	{
		{
#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
			.id	 = V4L2_CID_COMP_TARGET,
			.type    = V4L2_CTRL_TYPE_MENU,
			.name    = "Compression Target",
			.minimum = 0,
			.maximum = 1,
			.step    = 1,
#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
			.default_value = COMP_TARGET_DEF,
		},
		.set = sd_setcomptarget,
		.get = sd_getcomptarget,
	},
};

static const struct v4l2_pix_format mode[] = {
@@ -770,15 +653,6 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
	params->apcor.gain2 = 0x16;
	params->apcor.gain4 = 0x24;
	params->apcor.gain8 = 0x34;
	params->flickerControl.flickerMode = 0;
	params->flickerControl.disabled = 1;

	params->flickerControl.coarseJump =
		flicker_jumps[sd->mainsFreq]
			     [params->sensorFps.baserate]
			     [params->sensorFps.divisor];
	params->flickerControl.allowableOverExposure =
		find_over_exposure(params->colourParams.brightness);
	params->vlOffset.gain1 = 20;
	params->vlOffset.gain2 = 24;
	params->vlOffset.gain4 = 26;
@@ -798,6 +672,15 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
	params->sensorFps.divisor = 1;
	params->sensorFps.baserate = 1;

	params->flickerControl.flickerMode = 0;
	params->flickerControl.disabled = 1;
	params->flickerControl.coarseJump =
		flicker_jumps[sd->mainsFreq]
			     [params->sensorFps.baserate]
			     [params->sensorFps.divisor];
	params->flickerControl.allowableOverExposure =
		find_over_exposure(params->colourParams.brightness);

	params->yuvThreshold.yThreshold = 6; /* From windows driver */
	params->yuvThreshold.uvThreshold = 6; /* From windows driver */

@@ -1110,9 +993,6 @@ static int command_setlights(struct gspca_dev *gspca_dev)
	struct sd *sd = (struct sd *) gspca_dev;
	int ret, p1, p2;

	if (!sd->params.qx3.qx3_detected)
		return 0;

	p1 = (sd->params.qx3.bottomlight == 0) << 1;
	p2 = (sd->params.qx3.toplight == 0) << 3;

@@ -1551,8 +1431,10 @@ static void restart_flicker(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
			const struct usb_device_id *id)
{
	struct sd *sd = (struct sd *) gspca_dev;
	struct cam *cam;

	sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
	reset_camera_params(gspca_dev);

	PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
@@ -1562,8 +1444,25 @@ static int sd_config(struct gspca_dev *gspca_dev,
	cam->cam_mode = mode;
	cam->nmodes = ARRAY_SIZE(mode);

	sd_setfreq(gspca_dev, FREQ_DEF);
	goto_low_power(gspca_dev);
	/* Check the firmware version. */
	sd->params.version.firmwareVersion = 0;
	get_version_information(gspca_dev);
	if (sd->params.version.firmwareVersion != 1) {
		PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
		       sd->params.version.firmwareVersion);
		return -ENODEV;
	}

	/* A bug in firmware 1-02 limits gainMode to 2 */
	if (sd->params.version.firmwareRevision <= 2 &&
	    sd->params.exposure.gainMode > 2) {
		sd->params.exposure.gainMode = 2;
	}

	/* set QX3 detected flag */
	sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
				       sd->params.pnpID.product == 0x0001);
	return 0;
}

@@ -1602,21 +1501,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
	/* Check the firmware version. */
	sd->params.version.firmwareVersion = 0;
	get_version_information(gspca_dev);
	if (sd->params.version.firmwareVersion != 1) {
		PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
		       sd->params.version.firmwareVersion);
		return -ENODEV;
	}

	/* A bug in firmware 1-02 limits gainMode to 2 */
	if (sd->params.version.firmwareRevision <= 2 &&
	    sd->params.exposure.gainMode > 2) {
		sd->params.exposure.gainMode = 2;
	}

	/* set QX3 detected flag */
	sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
				       sd->params.pnpID.product == 0x0001);

	/* The fatal error checking should be done after
	 * the camera powers up (developer's guide p 3-38) */
@@ -1785,9 +1669,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
	   or disable the illuminator controls, if this isn't a QX3 */
	if (sd->params.qx3.qx3_detected)
		command_setlights(gspca_dev);
	else
		gspca_dev->ctrl_dis |=
			((1 << ILLUMINATORS_1_IDX) | (1 << ILLUMINATORS_2_IDX));

	sd_stopN(gspca_dev);

@@ -1871,235 +1752,123 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
	do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
}

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

	sd->params.colourParams.brightness = val;
	sd->params.flickerControl.allowableOverExposure =
		find_over_exposure(sd->params.colourParams.brightness);
	if (gspca_dev->streaming) {
		ret = command_setcolourparams(gspca_dev);
		if (ret)
			return ret;
		return command_setflickerctrl(gspca_dev);
	}
	return 0;
}

static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	gspca_dev->usb_err = 0;

	*val = sd->params.colourParams.brightness;
	if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
		return 0;
}

static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	sd->params.colourParams.contrast = val;
	if (gspca_dev->streaming)
		return command_setcolourparams(gspca_dev);

	return 0;
}

static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	*val = sd->params.colourParams.contrast;
	return 0;
}

static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	sd->params.colourParams.saturation = val;
	if (gspca_dev->streaming)
		return command_setcolourparams(gspca_dev);

	return 0;
}

static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	*val = sd->params.colourParams.saturation;
	return 0;
}

static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	int on;

	switch (val) {
	case 0:		/* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
		on = 0;
	switch (ctrl->id) {
	case V4L2_CID_BRIGHTNESS:
		sd->params.colourParams.brightness = ctrl->val;
		sd->params.flickerControl.allowableOverExposure =
			find_over_exposure(sd->params.colourParams.brightness);
		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
		if (!gspca_dev->usb_err)
			gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
		break;
	case 1:		/* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
		on = 1;
		sd->mainsFreq = 0;
	case V4L2_CID_CONTRAST:
		sd->params.colourParams.contrast = ctrl->val;
		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
		break;
	case 2:		/* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
		on = 1;
		sd->mainsFreq = 1;
	case V4L2_CID_SATURATION:
		sd->params.colourParams.saturation = ctrl->val;
		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
		break;
	default:
		return -EINVAL;
	}

	sd->freq = val;
	case V4L2_CID_POWER_LINE_FREQUENCY:
		sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
		sd->params.flickerControl.coarseJump =
			flicker_jumps[sd->mainsFreq]
			[sd->params.sensorFps.baserate]
			[sd->params.sensorFps.divisor];

	return set_flicker(gspca_dev, on, gspca_dev->streaming);
}

static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	*val = sd->freq;
	return 0;
}

static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	sd->params.compressionTarget.frTargeting = val;
	if (gspca_dev->streaming)
		return command_setcompressiontarget(gspca_dev);

	return 0;
}

static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;

	*val = sd->params.compressionTarget.frTargeting;
	return 0;
}

static int sd_setilluminator(struct gspca_dev *gspca_dev, __s32 val, int n)
{
	struct sd *sd = (struct sd *) gspca_dev;
	int ret;

	if (!sd->params.qx3.qx3_detected)
		return -EINVAL;

	switch (n) {
	case 1:
		sd->params.qx3.bottomlight = val ? 1 : 0;
		gspca_dev->usb_err = set_flicker(gspca_dev,
			ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
			gspca_dev->streaming);
		break;
	case 2:
		sd->params.qx3.toplight = val ? 1 : 0;
	case V4L2_CID_ILLUMINATORS_1:
		sd->params.qx3.bottomlight = ctrl->val;
		gspca_dev->usb_err = command_setlights(gspca_dev);
		break;
	default:
		return -EINVAL;
	}

	ret = command_setlights(gspca_dev);
	if (ret && ret != -EINVAL)
		ret = -EBUSY;

	return ret;
}

static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
{
	return sd_setilluminator(gspca_dev, val, 1);
}

static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
{
	return sd_setilluminator(gspca_dev, val, 2);
}

static int sd_getilluminator(struct gspca_dev *gspca_dev, __s32 *val, int n)
{
	struct sd *sd = (struct sd *) gspca_dev;

	if (!sd->params.qx3.qx3_detected)
		return -EINVAL;

	switch (n) {
	case 1:
		*val = sd->params.qx3.bottomlight;
	case V4L2_CID_ILLUMINATORS_2:
		sd->params.qx3.toplight = ctrl->val;
		gspca_dev->usb_err = command_setlights(gspca_dev);
		break;
	case 2:
		*val = sd->params.qx3.toplight;
	case CPIA1_CID_COMP_TARGET:
		sd->params.compressionTarget.frTargeting = ctrl->val;
		gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
		break;
	default:
		return -EINVAL;
	}
	return 0;
	return gspca_dev->usb_err;
}

static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val)
{
	return sd_getilluminator(gspca_dev, val, 1);
}
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
	.s_ctrl = sd_s_ctrl,
};

static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val)
static int sd_init_controls(struct gspca_dev *gspca_dev)
{
	return sd_getilluminator(gspca_dev, val, 2);
}
	struct sd *sd = (struct sd *)gspca_dev;
	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
	static const char * const comp_target_menu[] = {
		"Quality",
		"Framerate",
		NULL
	};
	static const struct v4l2_ctrl_config comp_target = {
		.ops = &sd_ctrl_ops,
		.id = CPIA1_CID_COMP_TARGET,
		.type = V4L2_CTRL_TYPE_MENU,
		.name = "Compression Target",
		.qmenu = comp_target_menu,
		.max = 1,
		.def = COMP_TARGET_DEF,
	};

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");
			return 0;
		case 2:		/* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
			strcpy((char *) menu->name, "60 Hz");
			return 0;
	gspca_dev->vdev.ctrl_handler = hdl;
	v4l2_ctrl_handler_init(hdl, 7);
	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
			V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
			V4L2_CID_POWER_LINE_FREQUENCY,
			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
			FREQ_DEF);
	if (sd->params.qx3.qx3_detected) {
		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
				V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
				ILLUMINATORS_1_DEF);
		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
				V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
				ILLUMINATORS_2_DEF);
	}
	v4l2_ctrl_new_custom(hdl, &comp_target, NULL);

	if (hdl->error) {
		pr_err("Could not initialize controls\n");
		return hdl->error;
	}
		break;
	case V4L2_CID_COMP_TARGET:
		switch (menu->index) {
		case CPIA_COMPRESSION_TARGET_QUALITY:
			strcpy((char *) menu->name, "Quality");
			return 0;
		case CPIA_COMPRESSION_TARGET_FRAMERATE:
			strcpy((char *) menu->name, "Framerate");
	return 0;
}
		break;
	}
	return -EINVAL;
}

/* sub-driver description */
static const struct sd_desc sd_desc = {
	.name = MODULE_NAME,
	.ctrls = sd_ctrls,
	.nctrls = ARRAY_SIZE(sd_ctrls),
	.config = sd_config,
	.init = sd_init,
	.init_controls = sd_init_controls,
	.start = sd_start,
	.stopN = sd_stopN,
	.dq_callback = sd_dq_callback,
	.pkt_scan = sd_pkt_scan,
	.querymenu = sd_querymenu,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
	.other_input = 1,
#endif