162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
462306a36Sopenharmony_ci *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
562306a36Sopenharmony_ci * Copyright (c) 2002, 2003 Tuukka Toivonen
662306a36Sopenharmony_ci * Copyright (c) 2008 Erik Andrén
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * P/N 861037:      Sensor HDCS1000        ASIC STV0600
962306a36Sopenharmony_ci * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
1062306a36Sopenharmony_ci * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
1162306a36Sopenharmony_ci * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
1262306a36Sopenharmony_ci * P/N 861075-0040: Sensor HDCS1000        ASIC
1362306a36Sopenharmony_ci * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
1462306a36Sopenharmony_ci * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "stv06xx_vv6410.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct v4l2_pix_format vv6410_mode[] = {
2262306a36Sopenharmony_ci	{
2362306a36Sopenharmony_ci		356,
2462306a36Sopenharmony_ci		292,
2562306a36Sopenharmony_ci		V4L2_PIX_FMT_SGRBG8,
2662306a36Sopenharmony_ci		V4L2_FIELD_NONE,
2762306a36Sopenharmony_ci		.sizeimage = 356 * 292,
2862306a36Sopenharmony_ci		.bytesperline = 356,
2962306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
3062306a36Sopenharmony_ci		.priv = 0
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int vv6410_s_ctrl(struct v4l2_ctrl *ctrl)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct gspca_dev *gspca_dev =
3762306a36Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
3862306a36Sopenharmony_ci	int err = -EINVAL;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	switch (ctrl->id) {
4162306a36Sopenharmony_ci	case V4L2_CID_HFLIP:
4262306a36Sopenharmony_ci		if (!gspca_dev->streaming)
4362306a36Sopenharmony_ci			return 0;
4462306a36Sopenharmony_ci		err = vv6410_set_hflip(gspca_dev, ctrl->val);
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	case V4L2_CID_VFLIP:
4762306a36Sopenharmony_ci		if (!gspca_dev->streaming)
4862306a36Sopenharmony_ci			return 0;
4962306a36Sopenharmony_ci		err = vv6410_set_vflip(gspca_dev, ctrl->val);
5062306a36Sopenharmony_ci		break;
5162306a36Sopenharmony_ci	case V4L2_CID_GAIN:
5262306a36Sopenharmony_ci		err = vv6410_set_analog_gain(gspca_dev, ctrl->val);
5362306a36Sopenharmony_ci		break;
5462306a36Sopenharmony_ci	case V4L2_CID_EXPOSURE:
5562306a36Sopenharmony_ci		err = vv6410_set_exposure(gspca_dev, ctrl->val);
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	return err;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops vv6410_ctrl_ops = {
6262306a36Sopenharmony_ci	.s_ctrl = vv6410_s_ctrl,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int vv6410_probe(struct sd *sd)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	u16 data;
6862306a36Sopenharmony_ci	int err;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
7162306a36Sopenharmony_ci	if (err < 0)
7262306a36Sopenharmony_ci		return -ENODEV;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (data != 0x19)
7562306a36Sopenharmony_ci		return -ENODEV;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	pr_info("vv6410 sensor detected\n");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	sd->gspca_dev.cam.cam_mode = vv6410_mode;
8062306a36Sopenharmony_ci	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int vv6410_init_controls(struct sd *sd)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 2);
8962306a36Sopenharmony_ci	/* Disable the hardware VFLIP and HFLIP as we currently lack a
9062306a36Sopenharmony_ci	   mechanism to adjust the image offset in such a way that
9162306a36Sopenharmony_ci	   we don't need to renegotiate the announced format */
9262306a36Sopenharmony_ci	/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
9362306a36Sopenharmony_ci	/*		V4L2_CID_HFLIP, 0, 1, 1, 0); */
9462306a36Sopenharmony_ci	/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
9562306a36Sopenharmony_ci	/*		V4L2_CID_VFLIP, 0, 1, 1, 0); */
9662306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
9762306a36Sopenharmony_ci			V4L2_CID_EXPOSURE, 0, 32768, 1, 20000);
9862306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
9962306a36Sopenharmony_ci			V4L2_CID_GAIN, 0, 15, 1, 10);
10062306a36Sopenharmony_ci	return hdl->error;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int vv6410_init(struct sd *sd)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	int err = 0, i;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++)
10862306a36Sopenharmony_ci		stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
11162306a36Sopenharmony_ci					 ARRAY_SIZE(vv6410_sensor_init));
11262306a36Sopenharmony_ci	return (err < 0) ? err : 0;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int vv6410_start(struct sd *sd)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int err;
11862306a36Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
11962306a36Sopenharmony_ci	struct cam *cam = &sd->gspca_dev.cam;
12062306a36Sopenharmony_ci	u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (priv & VV6410_SUBSAMPLE) {
12362306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_CONF, "Enabling subsampling\n");
12462306a36Sopenharmony_ci		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
12562306a36Sopenharmony_ci		stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
12862306a36Sopenharmony_ci	} else {
12962306a36Sopenharmony_ci		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
13062306a36Sopenharmony_ci		stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
13162306a36Sopenharmony_ci		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Turn on LED */
13662306a36Sopenharmony_ci	err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
13762306a36Sopenharmony_ci	if (err < 0)
13862306a36Sopenharmony_ci		return err;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
14162306a36Sopenharmony_ci	if (err < 0)
14262306a36Sopenharmony_ci		return err;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n");
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int vv6410_stop(struct sd *sd)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
15262306a36Sopenharmony_ci	int err;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Turn off LED */
15562306a36Sopenharmony_ci	err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
15662306a36Sopenharmony_ci	if (err < 0)
15762306a36Sopenharmony_ci		return err;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
16062306a36Sopenharmony_ci	if (err < 0)
16162306a36Sopenharmony_ci		return err;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n");
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int vv6410_dump(struct sd *sd)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	u8 i;
17162306a36Sopenharmony_ci	int err = 0;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	pr_info("Dumping all vv6410 sensor registers\n");
17462306a36Sopenharmony_ci	for (i = 0; i < 0xff && !err; i++) {
17562306a36Sopenharmony_ci		u16 data;
17662306a36Sopenharmony_ci		err = stv06xx_read_sensor(sd, i, &data);
17762306a36Sopenharmony_ci		pr_info("Register 0x%x contained 0x%x\n", i, data);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	return (err < 0) ? err : 0;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	int err;
18562306a36Sopenharmony_ci	u16 i2c_data;
18662306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
18962306a36Sopenharmony_ci	if (err < 0)
19062306a36Sopenharmony_ci		return err;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (val)
19362306a36Sopenharmony_ci		i2c_data |= VV6410_HFLIP;
19462306a36Sopenharmony_ci	else
19562306a36Sopenharmony_ci		i2c_data &= ~VV6410_HFLIP;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set horizontal flip to %d\n", val);
19862306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return (err < 0) ? err : 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int err;
20662306a36Sopenharmony_ci	u16 i2c_data;
20762306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
21062306a36Sopenharmony_ci	if (err < 0)
21162306a36Sopenharmony_ci		return err;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (val)
21462306a36Sopenharmony_ci		i2c_data |= VV6410_VFLIP;
21562306a36Sopenharmony_ci	else
21662306a36Sopenharmony_ci		i2c_data &= ~VV6410_VFLIP;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set vertical flip to %d\n", val);
21962306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return (err < 0) ? err : 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	int err;
22762306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set analog gain to %d\n", val);
23062306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return (err < 0) ? err : 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	int err;
23862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
23962306a36Sopenharmony_ci	unsigned int fine, coarse;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	val = (val * val >> 14) + val / 4;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	fine = val % VV6410_CIF_LINELENGTH;
24462306a36Sopenharmony_ci	coarse = min(512, val / VV6410_CIF_LINELENGTH);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set coarse exposure to %d, fine exposure to %d\n",
24762306a36Sopenharmony_ci		  coarse, fine);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8);
25062306a36Sopenharmony_ci	if (err < 0)
25162306a36Sopenharmony_ci		goto out;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff);
25462306a36Sopenharmony_ci	if (err < 0)
25562306a36Sopenharmony_ci		goto out;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8);
25862306a36Sopenharmony_ci	if (err < 0)
25962306a36Sopenharmony_ci		goto out;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ciout:
26462306a36Sopenharmony_ci	return err;
26562306a36Sopenharmony_ci}
266