1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * Support for the sensor part which is integrated (I think) into the
40c0d06caSMauro Carvalho Chehab  * st6422 stv06xx alike bridge, as its integrated there are no i2c writes
50c0d06caSMauro Carvalho Chehab  * but instead direct bridge writes.
60c0d06caSMauro Carvalho Chehab  *
70c0d06caSMauro Carvalho Chehab  * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
80c0d06caSMauro Carvalho Chehab  *
90c0d06caSMauro Carvalho Chehab  * Strongly based on qc-usb-messenger, which is:
100c0d06caSMauro Carvalho Chehab  * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
110c0d06caSMauro Carvalho Chehab  *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
120c0d06caSMauro Carvalho Chehab  * Copyright (c) 2002, 2003 Tuukka Toivonen
130c0d06caSMauro Carvalho Chehab  */
140c0d06caSMauro Carvalho Chehab 
150c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
160c0d06caSMauro Carvalho Chehab 
170c0d06caSMauro Carvalho Chehab #include "stv06xx_st6422.h"
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format st6422_mode[] = {
200c0d06caSMauro Carvalho Chehab 	/* Note we actually get 124 lines of data, of which we skip the 4st
210c0d06caSMauro Carvalho Chehab 	   4 as they are garbage */
220c0d06caSMauro Carvalho Chehab 	{
230c0d06caSMauro Carvalho Chehab 		162,
240c0d06caSMauro Carvalho Chehab 		120,
250c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SGRBG8,
260c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
270c0d06caSMauro Carvalho Chehab 		.sizeimage = 162 * 120,
280c0d06caSMauro Carvalho Chehab 		.bytesperline = 162,
290c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
300c0d06caSMauro Carvalho Chehab 		.priv = 1
310c0d06caSMauro Carvalho Chehab 	},
320c0d06caSMauro Carvalho Chehab 	/* Note we actually get 248 lines of data, of which we skip the 4st
330c0d06caSMauro Carvalho Chehab 	   4 as they are garbage, and we tell the app it only gets the
340c0d06caSMauro Carvalho Chehab 	   first 240 of the 244 lines it actually gets, so that it ignores
350c0d06caSMauro Carvalho Chehab 	   the last 4. */
360c0d06caSMauro Carvalho Chehab 	{
370c0d06caSMauro Carvalho Chehab 		324,
380c0d06caSMauro Carvalho Chehab 		240,
390c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SGRBG8,
400c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
410c0d06caSMauro Carvalho Chehab 		.sizeimage = 324 * 244,
420c0d06caSMauro Carvalho Chehab 		.bytesperline = 324,
430c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
440c0d06caSMauro Carvalho Chehab 		.priv = 0
450c0d06caSMauro Carvalho Chehab 	},
460c0d06caSMauro Carvalho Chehab };
470c0d06caSMauro Carvalho Chehab 
480c0d06caSMauro Carvalho Chehab /* V4L2 controls supported by the driver */
490c0d06caSMauro Carvalho Chehab static int setbrightness(struct sd *sd, s32 val);
500c0d06caSMauro Carvalho Chehab static int setcontrast(struct sd *sd, s32 val);
510c0d06caSMauro Carvalho Chehab static int setgain(struct sd *sd, u8 gain);
520c0d06caSMauro Carvalho Chehab static int setexposure(struct sd *sd, s16 expo);
530c0d06caSMauro Carvalho Chehab 
st6422_s_ctrl(struct v4l2_ctrl * ctrl)540c0d06caSMauro Carvalho Chehab static int st6422_s_ctrl(struct v4l2_ctrl *ctrl)
550c0d06caSMauro Carvalho Chehab {
560c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev =
570c0d06caSMauro Carvalho Chehab 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
580c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
590c0d06caSMauro Carvalho Chehab 	int err = -EINVAL;
600c0d06caSMauro Carvalho Chehab 
610c0d06caSMauro Carvalho Chehab 	switch (ctrl->id) {
620c0d06caSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
630c0d06caSMauro Carvalho Chehab 		err = setbrightness(sd, ctrl->val);
640c0d06caSMauro Carvalho Chehab 		break;
650c0d06caSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
660c0d06caSMauro Carvalho Chehab 		err = setcontrast(sd, ctrl->val);
670c0d06caSMauro Carvalho Chehab 		break;
680c0d06caSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
690c0d06caSMauro Carvalho Chehab 		err = setgain(sd, ctrl->val);
700c0d06caSMauro Carvalho Chehab 		break;
710c0d06caSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
720c0d06caSMauro Carvalho Chehab 		err = setexposure(sd, ctrl->val);
730c0d06caSMauro Carvalho Chehab 		break;
740c0d06caSMauro Carvalho Chehab 	}
750c0d06caSMauro Carvalho Chehab 
760c0d06caSMauro Carvalho Chehab 	/* commit settings */
770c0d06caSMauro Carvalho Chehab 	if (err >= 0)
780c0d06caSMauro Carvalho Chehab 		err = stv06xx_write_bridge(sd, 0x143f, 0x01);
790c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.usb_err = err;
800c0d06caSMauro Carvalho Chehab 	return err;
810c0d06caSMauro Carvalho Chehab }
820c0d06caSMauro Carvalho Chehab 
830c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops st6422_ctrl_ops = {
840c0d06caSMauro Carvalho Chehab 	.s_ctrl = st6422_s_ctrl,
850c0d06caSMauro Carvalho Chehab };
860c0d06caSMauro Carvalho Chehab 
st6422_init_controls(struct sd * sd)870c0d06caSMauro Carvalho Chehab static int st6422_init_controls(struct sd *sd)
880c0d06caSMauro Carvalho Chehab {
890c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
900c0d06caSMauro Carvalho Chehab 
910c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(hdl, 4);
920c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
930c0d06caSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, 0, 31, 1, 3);
940c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
950c0d06caSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 15, 1, 11);
960c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
970c0d06caSMauro Carvalho Chehab 			V4L2_CID_EXPOSURE, 0, 1023, 1, 256);
980c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
990c0d06caSMauro Carvalho Chehab 			V4L2_CID_GAIN, 0, 255, 1, 64);
1000c0d06caSMauro Carvalho Chehab 
1010c0d06caSMauro Carvalho Chehab 	return hdl->error;
1020c0d06caSMauro Carvalho Chehab }
1030c0d06caSMauro Carvalho Chehab 
st6422_probe(struct sd * sd)1040c0d06caSMauro Carvalho Chehab static int st6422_probe(struct sd *sd)
1050c0d06caSMauro Carvalho Chehab {
1060c0d06caSMauro Carvalho Chehab 	if (sd->bridge != BRIDGE_ST6422)
1070c0d06caSMauro Carvalho Chehab 		return -ENODEV;
1080c0d06caSMauro Carvalho Chehab 
1090c0d06caSMauro Carvalho Chehab 	pr_info("st6422 sensor detected\n");
1100c0d06caSMauro Carvalho Chehab 
1110c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.cam_mode = st6422_mode;
1120c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode);
1130c0d06caSMauro Carvalho Chehab 	return 0;
1140c0d06caSMauro Carvalho Chehab }
1150c0d06caSMauro Carvalho Chehab 
st6422_init(struct sd * sd)1160c0d06caSMauro Carvalho Chehab static int st6422_init(struct sd *sd)
1170c0d06caSMauro Carvalho Chehab {
1180c0d06caSMauro Carvalho Chehab 	int err = 0, i;
1190c0d06caSMauro Carvalho Chehab 
12059251a8bSColin Ian King 	static const u16 st6422_bridge_init[][2] = {
1210c0d06caSMauro Carvalho Chehab 		{ STV_ISO_ENABLE, 0x00 }, /* disable capture */
1220c0d06caSMauro Carvalho Chehab 		{ 0x1436, 0x00 },
1230c0d06caSMauro Carvalho Chehab 		{ 0x1432, 0x03 },	/* 0x00-0x1F brightness */
1240c0d06caSMauro Carvalho Chehab 		{ 0x143a, 0xf9 },	/* 0x00-0x0F contrast */
1250c0d06caSMauro Carvalho Chehab 		{ 0x0509, 0x38 },	/* R */
1260c0d06caSMauro Carvalho Chehab 		{ 0x050a, 0x38 },	/* G */
1270c0d06caSMauro Carvalho Chehab 		{ 0x050b, 0x38 },	/* B */
1280c0d06caSMauro Carvalho Chehab 		{ 0x050c, 0x2a },
1290c0d06caSMauro Carvalho Chehab 		{ 0x050d, 0x01 },
1300c0d06caSMauro Carvalho Chehab 
1310c0d06caSMauro Carvalho Chehab 
1320c0d06caSMauro Carvalho Chehab 		{ 0x1431, 0x00 },	/* 0x00-0x07 ??? */
1330c0d06caSMauro Carvalho Chehab 		{ 0x1433, 0x34 },	/* 160x120, 0x00-0x01 night filter */
1340c0d06caSMauro Carvalho Chehab 		{ 0x1438, 0x18 },	/* 640x480 */
1350c0d06caSMauro Carvalho Chehab /* 18 bayes */
1360c0d06caSMauro Carvalho Chehab /* 10 compressed? */
1370c0d06caSMauro Carvalho Chehab 
1380c0d06caSMauro Carvalho Chehab 		{ 0x1439, 0x00 },
1390c0d06caSMauro Carvalho Chehab /* anti-noise?  0xa2 gives a perfect image */
1400c0d06caSMauro Carvalho Chehab 
1410c0d06caSMauro Carvalho Chehab 		{ 0x143b, 0x05 },
1420c0d06caSMauro Carvalho Chehab 		{ 0x143c, 0x00 },	/* 0x00-0x01 - ??? */
1430c0d06caSMauro Carvalho Chehab 
1440c0d06caSMauro Carvalho Chehab 
1450c0d06caSMauro Carvalho Chehab /* shutter time 0x0000-0x03FF */
1460c0d06caSMauro Carvalho Chehab /* low value  give good picures on moving objects (but requires much light) */
1470c0d06caSMauro Carvalho Chehab /* high value gives good picures in darkness (but tends to be overexposed) */
1480c0d06caSMauro Carvalho Chehab 		{ 0x143e, 0x01 },
1490c0d06caSMauro Carvalho Chehab 		{ 0x143d, 0x00 },
1500c0d06caSMauro Carvalho Chehab 
1510c0d06caSMauro Carvalho Chehab 		{ 0x1442, 0xe2 },
1520c0d06caSMauro Carvalho Chehab /* write: 1x1x xxxx */
1530c0d06caSMauro Carvalho Chehab /* read:  1x1x xxxx */
1540c0d06caSMauro Carvalho Chehab /*        bit 5 == button pressed and hold if 0 */
1550c0d06caSMauro Carvalho Chehab /* write 0xe2,0xea */
1560c0d06caSMauro Carvalho Chehab 
1570c0d06caSMauro Carvalho Chehab /* 0x144a */
1580c0d06caSMauro Carvalho Chehab /* 0x00 init */
1590c0d06caSMauro Carvalho Chehab /* bit 7 == button has been pressed, but not handled */
1600c0d06caSMauro Carvalho Chehab 
1610c0d06caSMauro Carvalho Chehab /* interrupt */
1620c0d06caSMauro Carvalho Chehab /* if(urb->iso_frame_desc[i].status == 0x80) { */
1630c0d06caSMauro Carvalho Chehab /* if(urb->iso_frame_desc[i].status == 0x88) { */
1640c0d06caSMauro Carvalho Chehab 
1650c0d06caSMauro Carvalho Chehab 		{ 0x1500, 0xd0 },
1660c0d06caSMauro Carvalho Chehab 		{ 0x1500, 0xd0 },
1670c0d06caSMauro Carvalho Chehab 		{ 0x1500, 0x50 },	/* 0x00 - 0xFF  0x80 == compr ? */
1680c0d06caSMauro Carvalho Chehab 
1690c0d06caSMauro Carvalho Chehab 		{ 0x1501, 0xaf },
1700c0d06caSMauro Carvalho Chehab /* high val-> light area gets darker */
1710c0d06caSMauro Carvalho Chehab /* low val -> light area gets lighter */
1720c0d06caSMauro Carvalho Chehab 		{ 0x1502, 0xc2 },
1730c0d06caSMauro Carvalho Chehab /* high val-> light area gets darker */
1740c0d06caSMauro Carvalho Chehab /* low val -> light area gets lighter */
1750c0d06caSMauro Carvalho Chehab 		{ 0x1503, 0x45 },
1760c0d06caSMauro Carvalho Chehab /* high val-> light area gets darker */
1770c0d06caSMauro Carvalho Chehab /* low val -> light area gets lighter */
1780c0d06caSMauro Carvalho Chehab 		{ 0x1505, 0x02 },
1790c0d06caSMauro Carvalho Chehab /* 2  : 324x248  80352 bytes */
1800c0d06caSMauro Carvalho Chehab /* 7  : 248x162  40176 bytes */
1810c0d06caSMauro Carvalho Chehab /* c+f: 162*124  20088 bytes */
1820c0d06caSMauro Carvalho Chehab 
1830c0d06caSMauro Carvalho Chehab 		{ 0x150e, 0x8e },
1840c0d06caSMauro Carvalho Chehab 		{ 0x150f, 0x37 },
1850c0d06caSMauro Carvalho Chehab 		{ 0x15c0, 0x00 },
1860c0d06caSMauro Carvalho Chehab 		{ 0x15c3, 0x08 },	/* 0x04/0x14 ... test pictures ??? */
1870c0d06caSMauro Carvalho Chehab 
1880c0d06caSMauro Carvalho Chehab 
1890c0d06caSMauro Carvalho Chehab 		{ 0x143f, 0x01 },	/* commit settings */
1900c0d06caSMauro Carvalho Chehab 
1910c0d06caSMauro Carvalho Chehab 	};
1920c0d06caSMauro Carvalho Chehab 
1930c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) {
1940c0d06caSMauro Carvalho Chehab 		err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0],
1950c0d06caSMauro Carvalho Chehab 					       st6422_bridge_init[i][1]);
1960c0d06caSMauro Carvalho Chehab 	}
1970c0d06caSMauro Carvalho Chehab 
1980c0d06caSMauro Carvalho Chehab 	return err;
1990c0d06caSMauro Carvalho Chehab }
2000c0d06caSMauro Carvalho Chehab 
setbrightness(struct sd * sd,s32 val)2010c0d06caSMauro Carvalho Chehab static int setbrightness(struct sd *sd, s32 val)
2020c0d06caSMauro Carvalho Chehab {
2030c0d06caSMauro Carvalho Chehab 	/* val goes from 0 -> 31 */
2040c0d06caSMauro Carvalho Chehab 	return stv06xx_write_bridge(sd, 0x1432, val);
2050c0d06caSMauro Carvalho Chehab }
2060c0d06caSMauro Carvalho Chehab 
setcontrast(struct sd * sd,s32 val)2070c0d06caSMauro Carvalho Chehab static int setcontrast(struct sd *sd, s32 val)
2080c0d06caSMauro Carvalho Chehab {
2090c0d06caSMauro Carvalho Chehab 	/* Val goes from 0 -> 15 */
2100c0d06caSMauro Carvalho Chehab 	return stv06xx_write_bridge(sd, 0x143a, val | 0xf0);
2110c0d06caSMauro Carvalho Chehab }
2120c0d06caSMauro Carvalho Chehab 
setgain(struct sd * sd,u8 gain)2130c0d06caSMauro Carvalho Chehab static int setgain(struct sd *sd, u8 gain)
2140c0d06caSMauro Carvalho Chehab {
2150c0d06caSMauro Carvalho Chehab 	int err;
2160c0d06caSMauro Carvalho Chehab 
2170c0d06caSMauro Carvalho Chehab 	/* Set red, green, blue, gain */
2180c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, 0x0509, gain);
2190c0d06caSMauro Carvalho Chehab 	if (err < 0)
2200c0d06caSMauro Carvalho Chehab 		return err;
2210c0d06caSMauro Carvalho Chehab 
2220c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, 0x050a, gain);
2230c0d06caSMauro Carvalho Chehab 	if (err < 0)
2240c0d06caSMauro Carvalho Chehab 		return err;
2250c0d06caSMauro Carvalho Chehab 
2260c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, 0x050b, gain);
2270c0d06caSMauro Carvalho Chehab 	if (err < 0)
2280c0d06caSMauro Carvalho Chehab 		return err;
2290c0d06caSMauro Carvalho Chehab 
2300c0d06caSMauro Carvalho Chehab 	/* 2 mystery writes */
2310c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, 0x050c, 0x2a);
2320c0d06caSMauro Carvalho Chehab 	if (err < 0)
2330c0d06caSMauro Carvalho Chehab 		return err;
2340c0d06caSMauro Carvalho Chehab 
2350c0d06caSMauro Carvalho Chehab 	return stv06xx_write_bridge(sd, 0x050d, 0x01);
2360c0d06caSMauro Carvalho Chehab }
2370c0d06caSMauro Carvalho Chehab 
setexposure(struct sd * sd,s16 expo)2380c0d06caSMauro Carvalho Chehab static int setexposure(struct sd *sd, s16 expo)
2390c0d06caSMauro Carvalho Chehab {
2400c0d06caSMauro Carvalho Chehab 	int err;
2410c0d06caSMauro Carvalho Chehab 
2420c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff);
2430c0d06caSMauro Carvalho Chehab 	if (err < 0)
2440c0d06caSMauro Carvalho Chehab 		return err;
2450c0d06caSMauro Carvalho Chehab 
2460c0d06caSMauro Carvalho Chehab 	return stv06xx_write_bridge(sd, 0x143e, expo >> 8);
2470c0d06caSMauro Carvalho Chehab }
2480c0d06caSMauro Carvalho Chehab 
st6422_start(struct sd * sd)2490c0d06caSMauro Carvalho Chehab static int st6422_start(struct sd *sd)
2500c0d06caSMauro Carvalho Chehab {
2510c0d06caSMauro Carvalho Chehab 	int err;
2520c0d06caSMauro Carvalho Chehab 	struct cam *cam = &sd->gspca_dev.cam;
2530c0d06caSMauro Carvalho Chehab 
2540c0d06caSMauro Carvalho Chehab 	if (cam->cam_mode[sd->gspca_dev.curr_mode].priv)
2550c0d06caSMauro Carvalho Chehab 		err = stv06xx_write_bridge(sd, 0x1505, 0x0f);
2560c0d06caSMauro Carvalho Chehab 	else
2570c0d06caSMauro Carvalho Chehab 		err = stv06xx_write_bridge(sd, 0x1505, 0x02);
2580c0d06caSMauro Carvalho Chehab 	if (err < 0)
2590c0d06caSMauro Carvalho Chehab 		return err;
2600c0d06caSMauro Carvalho Chehab 
2610c0d06caSMauro Carvalho Chehab 	/* commit settings */
2620c0d06caSMauro Carvalho Chehab 	err = stv06xx_write_bridge(sd, 0x143f, 0x01);
2630c0d06caSMauro Carvalho Chehab 	return (err < 0) ? err : 0;
2640c0d06caSMauro Carvalho Chehab }
2650c0d06caSMauro Carvalho Chehab 
st6422_stop(struct sd * sd)2660c0d06caSMauro Carvalho Chehab static int st6422_stop(struct sd *sd)
2670c0d06caSMauro Carvalho Chehab {
268c93396e1STheodore Kilgore 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
269c93396e1STheodore Kilgore 
27037d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n");
2710c0d06caSMauro Carvalho Chehab 
2720c0d06caSMauro Carvalho Chehab 	return 0;
2730c0d06caSMauro Carvalho Chehab }
274