xref: /openbmc/linux/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
40c0d06caSMauro Carvalho Chehab  *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
50c0d06caSMauro Carvalho Chehab  * Copyright (c) 2002, 2003 Tuukka Toivonen
60c0d06caSMauro Carvalho Chehab  * Copyright (c) 2008 Erik Andrén
70c0d06caSMauro Carvalho Chehab  *
80c0d06caSMauro Carvalho Chehab  * P/N 861037:      Sensor HDCS1000        ASIC STV0600
90c0d06caSMauro Carvalho Chehab  * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
100c0d06caSMauro Carvalho Chehab  * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
110c0d06caSMauro Carvalho Chehab  * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
120c0d06caSMauro Carvalho Chehab  * P/N 861075-0040: Sensor HDCS1000        ASIC
130c0d06caSMauro Carvalho Chehab  * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
140c0d06caSMauro Carvalho Chehab  * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
150c0d06caSMauro Carvalho Chehab  */
160c0d06caSMauro Carvalho Chehab 
170c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab #include "stv06xx_vv6410.h"
200c0d06caSMauro Carvalho Chehab 
210c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format vv6410_mode[] = {
220c0d06caSMauro Carvalho Chehab 	{
230c0d06caSMauro Carvalho Chehab 		356,
240c0d06caSMauro Carvalho Chehab 		292,
250c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SGRBG8,
260c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
270c0d06caSMauro Carvalho Chehab 		.sizeimage = 356 * 292,
280c0d06caSMauro Carvalho Chehab 		.bytesperline = 356,
290c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
300c0d06caSMauro Carvalho Chehab 		.priv = 0
310c0d06caSMauro Carvalho Chehab 	}
320c0d06caSMauro Carvalho Chehab };
330c0d06caSMauro Carvalho Chehab 
vv6410_s_ctrl(struct v4l2_ctrl * ctrl)340c0d06caSMauro Carvalho Chehab static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl)
350c0d06caSMauro Carvalho Chehab {
360c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev =
370c0d06caSMauro Carvalho Chehab 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
380c0d06caSMauro Carvalho Chehab 	int err = -EINVAL;
390c0d06caSMauro Carvalho Chehab 
400c0d06caSMauro Carvalho Chehab 	switch (ctrl->id) {
410c0d06caSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
4204881127SJean-François Moine 		if (!gspca_dev->streaming)
4304881127SJean-François Moine 			return 0;
440c0d06caSMauro Carvalho Chehab 		err = vv6410_set_hflip(gspca_dev, ctrl->val);
450c0d06caSMauro Carvalho Chehab 		break;
460c0d06caSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
4704881127SJean-François Moine 		if (!gspca_dev->streaming)
4804881127SJean-François Moine 			return 0;
490c0d06caSMauro Carvalho Chehab 		err = vv6410_set_vflip(gspca_dev, ctrl->val);
500c0d06caSMauro Carvalho Chehab 		break;
510c0d06caSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
520c0d06caSMauro Carvalho Chehab 		err = vv6410_set_analog_gain(gspca_dev, ctrl->val);
530c0d06caSMauro Carvalho Chehab 		break;
540c0d06caSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
550c0d06caSMauro Carvalho Chehab 		err = vv6410_set_exposure(gspca_dev, ctrl->val);
560c0d06caSMauro Carvalho Chehab 		break;
570c0d06caSMauro Carvalho Chehab 	}
580c0d06caSMauro Carvalho Chehab 	return err;
590c0d06caSMauro Carvalho Chehab }
600c0d06caSMauro Carvalho Chehab 
610c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops vv6410_ctrl_ops = {
620c0d06caSMauro Carvalho Chehab 	.s_ctrl = vv6410_s_ctrl,
630c0d06caSMauro Carvalho Chehab };
640c0d06caSMauro Carvalho Chehab 
vv6410_probe(struct sd * sd)650c0d06caSMauro Carvalho Chehab static int vv6410_probe(struct sd *sd)
660c0d06caSMauro Carvalho Chehab {
670c0d06caSMauro Carvalho Chehab 	u16 data;
680c0d06caSMauro Carvalho Chehab 	int err;
690c0d06caSMauro Carvalho Chehab 
700c0d06caSMauro Carvalho Chehab 	err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
710c0d06caSMauro Carvalho Chehab 	if (err < 0)
720c0d06caSMauro Carvalho Chehab 		return -ENODEV;
730c0d06caSMauro Carvalho Chehab 
740c0d06caSMauro Carvalho Chehab 	if (data != 0x19)
750c0d06caSMauro Carvalho Chehab 		return -ENODEV;
760c0d06caSMauro Carvalho Chehab 
770c0d06caSMauro Carvalho Chehab 	pr_info("vv6410 sensor detected\n");
780c0d06caSMauro Carvalho Chehab 
790c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.cam_mode = vv6410_mode;
800c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
810c0d06caSMauro Carvalho Chehab 	return 0;
820c0d06caSMauro Carvalho Chehab }
830c0d06caSMauro Carvalho Chehab 
vv6410_init_controls(struct sd * sd)840c0d06caSMauro Carvalho Chehab static int vv6410_init_controls(struct sd *sd)
850c0d06caSMauro Carvalho Chehab {
860c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
870c0d06caSMauro Carvalho Chehab 
88d83fcb79SErik Andrén 	v4l2_ctrl_handler_init(hdl, 2);
89d83fcb79SErik Andrén 	/* Disable the hardware VFLIP and HFLIP as we currently lack a
90d83fcb79SErik Andrén 	   mechanism to adjust the image offset in such a way that
91d83fcb79SErik Andrén 	   we don't need to renegotiate the announced format */
92d83fcb79SErik Andrén 	/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
93d83fcb79SErik Andrén 	/*		V4L2_CID_HFLIP, 0, 1, 1, 0); */
94d83fcb79SErik Andrén 	/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
95d83fcb79SErik Andrén 	/*		V4L2_CID_VFLIP, 0, 1, 1, 0); */
960c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
970c0d06caSMauro Carvalho Chehab 			V4L2_CID_EXPOSURE, 0, 32768, 1, 20000);
980c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
990c0d06caSMauro Carvalho Chehab 			V4L2_CID_GAIN, 0, 15, 1, 10);
1000c0d06caSMauro Carvalho Chehab 	return hdl->error;
1010c0d06caSMauro Carvalho Chehab }
1020c0d06caSMauro Carvalho Chehab 
vv6410_init(struct sd * sd)1030c0d06caSMauro Carvalho Chehab static int vv6410_init(struct sd *sd)
1040c0d06caSMauro Carvalho Chehab {
1050c0d06caSMauro Carvalho Chehab 	int err = 0, i;
1060c0d06caSMauro Carvalho Chehab 
1070c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++)
1080c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
1090c0d06caSMauro Carvalho Chehab 
1100c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
1110c0d06caSMauro Carvalho Chehab 					 ARRAY_SIZE(vv6410_sensor_init));
1120c0d06caSMauro Carvalho Chehab 	return (err < 0) ? err : 0;
1130c0d06caSMauro Carvalho Chehab }
1140c0d06caSMauro Carvalho Chehab 
vv6410_start(struct sd * sd)1150c0d06caSMauro Carvalho Chehab static int vv6410_start(struct sd *sd)
1160c0d06caSMauro Carvalho Chehab {
1170c0d06caSMauro Carvalho Chehab 	int err;
118c93396e1STheodore Kilgore 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
1190c0d06caSMauro Carvalho Chehab 	struct cam *cam = &sd->gspca_dev.cam;
1200c0d06caSMauro Carvalho Chehab 	u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
1210c0d06caSMauro Carvalho Chehab 
1220c0d06caSMauro Carvalho Chehab 	if (priv & VV6410_SUBSAMPLE) {
12337d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_CONF, "Enabling subsampling\n");
1240c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
1250c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
1260c0d06caSMauro Carvalho Chehab 
1270c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
1280c0d06caSMauro Carvalho Chehab 	} else {
1290c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
1300c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
1310c0d06caSMauro Carvalho Chehab 		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00);
1320c0d06caSMauro Carvalho Chehab 
1330c0d06caSMauro Carvalho Chehab 	}
1340c0d06caSMauro Carvalho Chehab 
1350c0d06caSMauro Carvalho Chehab 	/* Turn on LED */
1360c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
1370c0d06caSMauro Carvalho Chehab 	if (err < 0)
1380c0d06caSMauro Carvalho Chehab 		return err;
1390c0d06caSMauro Carvalho Chehab 
1400c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
1410c0d06caSMauro Carvalho Chehab 	if (err < 0)
1420c0d06caSMauro Carvalho Chehab 		return err;
1430c0d06caSMauro Carvalho Chehab 
14437d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n");
1450c0d06caSMauro Carvalho Chehab 
1460c0d06caSMauro Carvalho Chehab 	return 0;
1470c0d06caSMauro Carvalho Chehab }
1480c0d06caSMauro Carvalho Chehab 
vv6410_stop(struct sd * sd)1490c0d06caSMauro Carvalho Chehab static int vv6410_stop(struct sd *sd)
1500c0d06caSMauro Carvalho Chehab {
151c93396e1STheodore Kilgore 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
1520c0d06caSMauro Carvalho Chehab 	int err;
1530c0d06caSMauro Carvalho Chehab 
1540c0d06caSMauro Carvalho Chehab 	/* Turn off LED */
1550c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
1560c0d06caSMauro Carvalho Chehab 	if (err < 0)
1570c0d06caSMauro Carvalho Chehab 		return err;
1580c0d06caSMauro Carvalho Chehab 
1590c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
1600c0d06caSMauro Carvalho Chehab 	if (err < 0)
1610c0d06caSMauro Carvalho Chehab 		return err;
1620c0d06caSMauro Carvalho Chehab 
16337d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n");
1640c0d06caSMauro Carvalho Chehab 
165ce4452e6SDan Carpenter 	return 0;
1660c0d06caSMauro Carvalho Chehab }
1670c0d06caSMauro Carvalho Chehab 
vv6410_dump(struct sd * sd)1680c0d06caSMauro Carvalho Chehab static int vv6410_dump(struct sd *sd)
1690c0d06caSMauro Carvalho Chehab {
1700c0d06caSMauro Carvalho Chehab 	u8 i;
1710c0d06caSMauro Carvalho Chehab 	int err = 0;
1720c0d06caSMauro Carvalho Chehab 
1730c0d06caSMauro Carvalho Chehab 	pr_info("Dumping all vv6410 sensor registers\n");
1740c0d06caSMauro Carvalho Chehab 	for (i = 0; i < 0xff && !err; i++) {
1750c0d06caSMauro Carvalho Chehab 		u16 data;
1760c0d06caSMauro Carvalho Chehab 		err = stv06xx_read_sensor(sd, i, &data);
1770c0d06caSMauro Carvalho Chehab 		pr_info("Register 0x%x contained 0x%x\n", i, data);
1780c0d06caSMauro Carvalho Chehab 	}
1790c0d06caSMauro Carvalho Chehab 	return (err < 0) ? err : 0;
1800c0d06caSMauro Carvalho Chehab }
1810c0d06caSMauro Carvalho Chehab 
vv6410_set_hflip(struct gspca_dev * gspca_dev,__s32 val)1820c0d06caSMauro Carvalho Chehab static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
1830c0d06caSMauro Carvalho Chehab {
1840c0d06caSMauro Carvalho Chehab 	int err;
1850c0d06caSMauro Carvalho Chehab 	u16 i2c_data;
1860c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
1870c0d06caSMauro Carvalho Chehab 
1880c0d06caSMauro Carvalho Chehab 	err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
1890c0d06caSMauro Carvalho Chehab 	if (err < 0)
1900c0d06caSMauro Carvalho Chehab 		return err;
1910c0d06caSMauro Carvalho Chehab 
1920c0d06caSMauro Carvalho Chehab 	if (val)
1930c0d06caSMauro Carvalho Chehab 		i2c_data |= VV6410_HFLIP;
1940c0d06caSMauro Carvalho Chehab 	else
1950c0d06caSMauro Carvalho Chehab 		i2c_data &= ~VV6410_HFLIP;
1960c0d06caSMauro Carvalho Chehab 
19737d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set horizontal flip to %d\n", val);
1980c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
1990c0d06caSMauro Carvalho Chehab 
2000c0d06caSMauro Carvalho Chehab 	return (err < 0) ? err : 0;
2010c0d06caSMauro Carvalho Chehab }
2020c0d06caSMauro Carvalho Chehab 
vv6410_set_vflip(struct gspca_dev * gspca_dev,__s32 val)2030c0d06caSMauro Carvalho Chehab static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
2040c0d06caSMauro Carvalho Chehab {
2050c0d06caSMauro Carvalho Chehab 	int err;
2060c0d06caSMauro Carvalho Chehab 	u16 i2c_data;
2070c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
2080c0d06caSMauro Carvalho Chehab 
2090c0d06caSMauro Carvalho Chehab 	err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
2100c0d06caSMauro Carvalho Chehab 	if (err < 0)
2110c0d06caSMauro Carvalho Chehab 		return err;
2120c0d06caSMauro Carvalho Chehab 
2130c0d06caSMauro Carvalho Chehab 	if (val)
2140c0d06caSMauro Carvalho Chehab 		i2c_data |= VV6410_VFLIP;
2150c0d06caSMauro Carvalho Chehab 	else
2160c0d06caSMauro Carvalho Chehab 		i2c_data &= ~VV6410_VFLIP;
2170c0d06caSMauro Carvalho Chehab 
21837d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set vertical flip to %d\n", val);
2190c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
2200c0d06caSMauro Carvalho Chehab 
2210c0d06caSMauro Carvalho Chehab 	return (err < 0) ? err : 0;
2220c0d06caSMauro Carvalho Chehab }
2230c0d06caSMauro Carvalho Chehab 
vv6410_set_analog_gain(struct gspca_dev * gspca_dev,__s32 val)2240c0d06caSMauro Carvalho Chehab static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
2250c0d06caSMauro Carvalho Chehab {
2260c0d06caSMauro Carvalho Chehab 	int err;
2270c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
2280c0d06caSMauro Carvalho Chehab 
22937d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set analog gain to %d\n", val);
2300c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
2310c0d06caSMauro Carvalho Chehab 
2320c0d06caSMauro Carvalho Chehab 	return (err < 0) ? err : 0;
2330c0d06caSMauro Carvalho Chehab }
2340c0d06caSMauro Carvalho Chehab 
vv6410_set_exposure(struct gspca_dev * gspca_dev,__s32 val)2350c0d06caSMauro Carvalho Chehab static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
2360c0d06caSMauro Carvalho Chehab {
2370c0d06caSMauro Carvalho Chehab 	int err;
2380c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
2390c0d06caSMauro Carvalho Chehab 	unsigned int fine, coarse;
2400c0d06caSMauro Carvalho Chehab 
2410c0d06caSMauro Carvalho Chehab 	val = (val * val >> 14) + val / 4;
2420c0d06caSMauro Carvalho Chehab 
2430c0d06caSMauro Carvalho Chehab 	fine = val % VV6410_CIF_LINELENGTH;
2440c0d06caSMauro Carvalho Chehab 	coarse = min(512, val / VV6410_CIF_LINELENGTH);
2450c0d06caSMauro Carvalho Chehab 
24637d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Set coarse exposure to %d, fine exposure to %d\n",
2470c0d06caSMauro Carvalho Chehab 		  coarse, fine);
2480c0d06caSMauro Carvalho Chehab 
2490c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8);
2500c0d06caSMauro Carvalho Chehab 	if (err < 0)
2510c0d06caSMauro Carvalho Chehab 		goto out;
2520c0d06caSMauro Carvalho Chehab 
2530c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff);
2540c0d06caSMauro Carvalho Chehab 	if (err < 0)
2550c0d06caSMauro Carvalho Chehab 		goto out;
2560c0d06caSMauro Carvalho Chehab 
2570c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8);
2580c0d06caSMauro Carvalho Chehab 	if (err < 0)
2590c0d06caSMauro Carvalho Chehab 		goto out;
2600c0d06caSMauro Carvalho Chehab 
2610c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff);
2620c0d06caSMauro Carvalho Chehab 
2630c0d06caSMauro Carvalho Chehab out:
2640c0d06caSMauro Carvalho Chehab 	return err;
2650c0d06caSMauro Carvalho Chehab }
266