18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Syntek STK1135 subdriver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2013 Ondrej Zary
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on Syntekdriver (stk11xx) by Nicolas VIVIEN:
88c2ecf20Sopenharmony_ci *   http://syntekdriver.sourceforge.net
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define MODULE_NAME "stk1135"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "gspca.h"
168c2ecf20Sopenharmony_ci#include "stk1135.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ondrej Zary");
198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Syntek STK1135 USB Camera Driver");
208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* specific webcam descriptor */
248c2ecf20Sopenharmony_cistruct sd {
258c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev;	/* !! must be the first item */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	u8 pkt_seq;
288c2ecf20Sopenharmony_ci	u8 sensor_page;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	bool flip_status;
318c2ecf20Sopenharmony_ci	u8 flip_debounce;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	struct v4l2_ctrl *hflip;
348c2ecf20Sopenharmony_ci	struct v4l2_ctrl *vflip;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format stk1135_modes[] = {
388c2ecf20Sopenharmony_ci	/* default mode (this driver supports variable resolution) */
398c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
408c2ecf20Sopenharmony_ci		.bytesperline = 640,
418c2ecf20Sopenharmony_ci		.sizeimage = 640 * 480,
428c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB},
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* -- read a register -- */
468c2ecf20Sopenharmony_cistatic u8 reg_r(struct gspca_dev *gspca_dev, u16 index)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
498c2ecf20Sopenharmony_ci	int ret;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
528c2ecf20Sopenharmony_ci		return 0;
538c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
548c2ecf20Sopenharmony_ci			0x00,
558c2ecf20Sopenharmony_ci			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
568c2ecf20Sopenharmony_ci			0x00,
578c2ecf20Sopenharmony_ci			index,
588c2ecf20Sopenharmony_ci			gspca_dev->usb_buf, 1,
598c2ecf20Sopenharmony_ci			500);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBI, "reg_r 0x%x=0x%02x\n",
628c2ecf20Sopenharmony_ci		  index, gspca_dev->usb_buf[0]);
638c2ecf20Sopenharmony_ci	if (ret < 0) {
648c2ecf20Sopenharmony_ci		pr_err("reg_r 0x%x err %d\n", index, ret);
658c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
668c2ecf20Sopenharmony_ci		return 0;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return gspca_dev->usb_buf[0];
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* -- write a register -- */
738c2ecf20Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, u16 index, u8 val)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int ret;
768c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
798c2ecf20Sopenharmony_ci		return;
808c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
818c2ecf20Sopenharmony_ci			0x01,
828c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
838c2ecf20Sopenharmony_ci			val,
848c2ecf20Sopenharmony_ci			index,
858c2ecf20Sopenharmony_ci			NULL,
868c2ecf20Sopenharmony_ci			0,
878c2ecf20Sopenharmony_ci			500);
888c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "reg_w 0x%x:=0x%02x\n", index, val);
898c2ecf20Sopenharmony_ci	if (ret < 0) {
908c2ecf20Sopenharmony_ci		pr_err("reg_w 0x%x err %d\n", index, ret);
918c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void reg_w_mask(struct gspca_dev *gspca_dev, u16 index, u8 val, u8 mask)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	val = (reg_r(gspca_dev, index) & ~mask) | (val & mask);
988c2ecf20Sopenharmony_ci	reg_w(gspca_dev, index, val);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* this function is called at probe time */
1028c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
1038c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	gspca_dev->cam.cam_mode = stk1135_modes;
1068c2ecf20Sopenharmony_ci	gspca_dev->cam.nmodes = ARRAY_SIZE(stk1135_modes);
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int stk1135_serial_wait_ready(struct gspca_dev *gspca_dev)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int i = 0;
1138c2ecf20Sopenharmony_ci	u8 val;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	do {
1168c2ecf20Sopenharmony_ci		val = reg_r(gspca_dev, STK1135_REG_SICTL + 1);
1178c2ecf20Sopenharmony_ci		if (i++ > 500) { /* maximum retry count */
1188c2ecf20Sopenharmony_ci			pr_err("serial bus timeout: status=0x%02x\n", val);
1198c2ecf20Sopenharmony_ci			return -1;
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci	/* repeat if BUSY or WRITE/READ not finished */
1228c2ecf20Sopenharmony_ci	} while ((val & 0x10) || !(val & 0x05));
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic u8 sensor_read_8(struct gspca_dev *gspca_dev, u8 addr)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SBUSR, addr);
1308c2ecf20Sopenharmony_ci	/* begin read */
1318c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SICTL, 0x20);
1328c2ecf20Sopenharmony_ci	/* wait until finished */
1338c2ecf20Sopenharmony_ci	if (stk1135_serial_wait_ready(gspca_dev)) {
1348c2ecf20Sopenharmony_ci		pr_err("Sensor read failed\n");
1358c2ecf20Sopenharmony_ci		return 0;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return reg_r(gspca_dev, STK1135_REG_SBUSR + 1);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic u16 sensor_read_16(struct gspca_dev *gspca_dev, u8 addr)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	return (sensor_read_8(gspca_dev, addr) << 8) |
1448c2ecf20Sopenharmony_ci		sensor_read_8(gspca_dev, 0xf1);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic void sensor_write_8(struct gspca_dev *gspca_dev, u8 addr, u8 data)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	/* load address and data registers */
1508c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SBUSW, addr);
1518c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SBUSW + 1, data);
1528c2ecf20Sopenharmony_ci	/* begin write */
1538c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SICTL, 0x01);
1548c2ecf20Sopenharmony_ci	/* wait until finished */
1558c2ecf20Sopenharmony_ci	if (stk1135_serial_wait_ready(gspca_dev)) {
1568c2ecf20Sopenharmony_ci		pr_err("Sensor write failed\n");
1578c2ecf20Sopenharmony_ci		return;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void sensor_write_16(struct gspca_dev *gspca_dev, u8 addr, u16 data)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	sensor_write_8(gspca_dev, addr, data >> 8);
1648c2ecf20Sopenharmony_ci	sensor_write_8(gspca_dev, 0xf1, data & 0xff);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void sensor_set_page(struct gspca_dev *gspca_dev, u8 page)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (page != sd->sensor_page) {
1728c2ecf20Sopenharmony_ci		sensor_write_16(gspca_dev, 0xf0, page);
1738c2ecf20Sopenharmony_ci		sd->sensor_page = page;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic u16 sensor_read(struct gspca_dev *gspca_dev, u16 reg)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	sensor_set_page(gspca_dev, reg >> 8);
1808c2ecf20Sopenharmony_ci	return sensor_read_16(gspca_dev, reg & 0xff);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void sensor_write(struct gspca_dev *gspca_dev, u16 reg, u16 val)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	sensor_set_page(gspca_dev, reg >> 8);
1868c2ecf20Sopenharmony_ci	sensor_write_16(gspca_dev, reg & 0xff, val);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void sensor_write_mask(struct gspca_dev *gspca_dev,
1908c2ecf20Sopenharmony_ci			u16 reg, u16 val, u16 mask)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	val = (sensor_read(gspca_dev, reg) & ~mask) | (val & mask);
1938c2ecf20Sopenharmony_ci	sensor_write(gspca_dev, reg, val);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistruct sensor_val {
1978c2ecf20Sopenharmony_ci	u16 reg;
1988c2ecf20Sopenharmony_ci	u16 val;
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* configure MT9M112 sensor */
2028c2ecf20Sopenharmony_cistatic void stk1135_configure_mt9m112(struct gspca_dev *gspca_dev)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	static const struct sensor_val cfg[] = {
2058c2ecf20Sopenharmony_ci		/* restart&reset, chip enable, reserved */
2068c2ecf20Sopenharmony_ci		{ 0x00d, 0x000b }, { 0x00d, 0x0008 }, { 0x035, 0x0022 },
2078c2ecf20Sopenharmony_ci		/* mode ctl: AWB on, AE both, clip aper corr, defect corr, AE */
2088c2ecf20Sopenharmony_ci		{ 0x106, 0x700e },
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		{ 0x2dd, 0x18e0 }, /* B-R thresholds, */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		/* AWB */
2138c2ecf20Sopenharmony_ci		{ 0x21f, 0x0180 }, /* Cb and Cr limits */
2148c2ecf20Sopenharmony_ci		{ 0x220, 0xc814 }, { 0x221, 0x8080 }, /* lum limits, RGB gain */
2158c2ecf20Sopenharmony_ci		{ 0x222, 0xa078 }, { 0x223, 0xa078 }, /* R, B limit */
2168c2ecf20Sopenharmony_ci		{ 0x224, 0x5f20 }, { 0x228, 0xea02 }, /* mtx adj lim, adv ctl */
2178c2ecf20Sopenharmony_ci		{ 0x229, 0x867a }, /* wide gates */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		/* Color correction */
2208c2ecf20Sopenharmony_ci		/* imager gains base, delta, delta signs */
2218c2ecf20Sopenharmony_ci		{ 0x25e, 0x594c }, { 0x25f, 0x4d51 }, { 0x260, 0x0002 },
2228c2ecf20Sopenharmony_ci		/* AWB adv ctl 2, gain offs */
2238c2ecf20Sopenharmony_ci		{ 0x2ef, 0x0008 }, { 0x2f2, 0x0000 },
2248c2ecf20Sopenharmony_ci		/* base matrix signs, scale K1-5, K6-9 */
2258c2ecf20Sopenharmony_ci		{ 0x202, 0x00ee }, { 0x203, 0x3923 }, { 0x204, 0x0724 },
2268c2ecf20Sopenharmony_ci		/* base matrix coef */
2278c2ecf20Sopenharmony_ci		{ 0x209, 0x00cd }, { 0x20a, 0x0093 }, { 0x20b, 0x0004 },/*K1-3*/
2288c2ecf20Sopenharmony_ci		{ 0x20c, 0x005c }, { 0x20d, 0x00d9 }, { 0x20e, 0x0053 },/*K4-6*/
2298c2ecf20Sopenharmony_ci		{ 0x20f, 0x0008 }, { 0x210, 0x0091 }, { 0x211, 0x00cf },/*K7-9*/
2308c2ecf20Sopenharmony_ci		{ 0x215, 0x0000 }, /* delta mtx signs */
2318c2ecf20Sopenharmony_ci		/* delta matrix coef */
2328c2ecf20Sopenharmony_ci		{ 0x216, 0x0000 }, { 0x217, 0x0000 }, { 0x218, 0x0000 },/*D1-3*/
2338c2ecf20Sopenharmony_ci		{ 0x219, 0x0000 }, { 0x21a, 0x0000 }, { 0x21b, 0x0000 },/*D4-6*/
2348c2ecf20Sopenharmony_ci		{ 0x21c, 0x0000 }, { 0x21d, 0x0000 }, { 0x21e, 0x0000 },/*D7-9*/
2358c2ecf20Sopenharmony_ci		/* enable & disable manual WB to apply color corr. settings */
2368c2ecf20Sopenharmony_ci		{ 0x106, 0xf00e }, { 0x106, 0x700e },
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		/* Lens shading correction */
2398c2ecf20Sopenharmony_ci		{ 0x180, 0x0007 }, /* control */
2408c2ecf20Sopenharmony_ci		/* vertical knee 0, 2+1, 4+3 */
2418c2ecf20Sopenharmony_ci		{ 0x181, 0xde13 }, { 0x182, 0xebe2 }, { 0x183, 0x00f6 }, /* R */
2428c2ecf20Sopenharmony_ci		{ 0x184, 0xe114 }, { 0x185, 0xeadd }, { 0x186, 0xfdf6 }, /* G */
2438c2ecf20Sopenharmony_ci		{ 0x187, 0xe511 }, { 0x188, 0xede6 }, { 0x189, 0xfbf7 }, /* B */
2448c2ecf20Sopenharmony_ci		/* horizontal knee 0, 2+1, 4+3, 5 */
2458c2ecf20Sopenharmony_ci		{ 0x18a, 0xd613 }, { 0x18b, 0xedec }, /* R .. */
2468c2ecf20Sopenharmony_ci		{ 0x18c, 0xf9f2 }, { 0x18d, 0x0000 }, /* .. R */
2478c2ecf20Sopenharmony_ci		{ 0x18e, 0xd815 }, { 0x18f, 0xe9ea }, /* G .. */
2488c2ecf20Sopenharmony_ci		{ 0x190, 0xf9f1 }, { 0x191, 0x0002 }, /* .. G */
2498c2ecf20Sopenharmony_ci		{ 0x192, 0xde10 }, { 0x193, 0xefef }, /* B .. */
2508c2ecf20Sopenharmony_ci		{ 0x194, 0xfbf4 }, { 0x195, 0x0002 }, /* .. B */
2518c2ecf20Sopenharmony_ci		/* vertical knee 6+5, 8+7 */
2528c2ecf20Sopenharmony_ci		{ 0x1b6, 0x0e06 }, { 0x1b7, 0x2713 }, /* R */
2538c2ecf20Sopenharmony_ci		{ 0x1b8, 0x1106 }, { 0x1b9, 0x2713 }, /* G */
2548c2ecf20Sopenharmony_ci		{ 0x1ba, 0x0c03 }, { 0x1bb, 0x2a0f }, /* B */
2558c2ecf20Sopenharmony_ci		/* horizontal knee 7+6, 9+8, 10 */
2568c2ecf20Sopenharmony_ci		{ 0x1bc, 0x1208 }, { 0x1bd, 0x1a16 }, { 0x1be, 0x0022 }, /* R */
2578c2ecf20Sopenharmony_ci		{ 0x1bf, 0x150a }, { 0x1c0, 0x1c1a }, { 0x1c1, 0x002d }, /* G */
2588c2ecf20Sopenharmony_ci		{ 0x1c2, 0x1109 }, { 0x1c3, 0x1414 }, { 0x1c4, 0x002a }, /* B */
2598c2ecf20Sopenharmony_ci		{ 0x106, 0x740e }, /* enable lens shading correction */
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		/* Gamma correction - context A */
2628c2ecf20Sopenharmony_ci		{ 0x153, 0x0b03 }, { 0x154, 0x4722 }, { 0x155, 0xac82 },
2638c2ecf20Sopenharmony_ci		{ 0x156, 0xdac7 }, { 0x157, 0xf5e9 }, { 0x158, 0xff00 },
2648c2ecf20Sopenharmony_ci		/* Gamma correction - context B */
2658c2ecf20Sopenharmony_ci		{ 0x1dc, 0x0b03 }, { 0x1dd, 0x4722 }, { 0x1de, 0xac82 },
2668c2ecf20Sopenharmony_ci		{ 0x1df, 0xdac7 }, { 0x1e0, 0xf5e9 }, { 0x1e1, 0xff00 },
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		/* output format: RGB, invert output pixclock, output bayer */
2698c2ecf20Sopenharmony_ci		{ 0x13a, 0x4300 }, { 0x19b, 0x4300 }, /* for context A, B */
2708c2ecf20Sopenharmony_ci		{ 0x108, 0x0180 }, /* format control - enable bayer row flip */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		{ 0x22f, 0xd100 }, { 0x29c, 0xd100 }, /* AE A, B */
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		/* default prg conf, prg ctl - by 0x2d2, prg advance - PA1 */
2758c2ecf20Sopenharmony_ci		{ 0x2d2, 0x0000 }, { 0x2cc, 0x0004 }, { 0x2cb, 0x0001 },
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		{ 0x22e, 0x0c3c }, { 0x267, 0x1010 }, /* AE tgt ctl, gain lim */
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* PLL */
2808c2ecf20Sopenharmony_ci		{ 0x065, 0xa000 }, /* clk ctl - enable PLL (clear bit 14) */
2818c2ecf20Sopenharmony_ci		{ 0x066, 0x2003 }, { 0x067, 0x0501 }, /* PLL M=128, N=3, P=1 */
2828c2ecf20Sopenharmony_ci		{ 0x065, 0x2000 }, /* disable PLL bypass (clear bit 15) */
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		{ 0x005, 0x01b8 }, { 0x007, 0x00d8 }, /* horiz blanking B, A */
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		/* AE line size, shutter delay limit */
2878c2ecf20Sopenharmony_ci		{ 0x239, 0x06c0 }, { 0x23b, 0x040e }, /* for context A */
2888c2ecf20Sopenharmony_ci		{ 0x23a, 0x06c0 }, { 0x23c, 0x0564 }, /* for context B */
2898c2ecf20Sopenharmony_ci		/* shutter width basis 60Hz, 50Hz */
2908c2ecf20Sopenharmony_ci		{ 0x257, 0x0208 }, { 0x258, 0x0271 }, /* for context A */
2918c2ecf20Sopenharmony_ci		{ 0x259, 0x0209 }, { 0x25a, 0x0271 }, /* for context B */
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		{ 0x25c, 0x120d }, { 0x25d, 0x1712 }, /* flicker 60Hz, 50Hz */
2948c2ecf20Sopenharmony_ci		{ 0x264, 0x5e1c }, /* reserved */
2958c2ecf20Sopenharmony_ci		/* flicker, AE gain limits, gain zone limits */
2968c2ecf20Sopenharmony_ci		{ 0x25b, 0x0003 }, { 0x236, 0x7810 }, { 0x237, 0x8304 },
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		{ 0x008, 0x0021 }, /* vert blanking A */
2998c2ecf20Sopenharmony_ci	};
3008c2ecf20Sopenharmony_ci	int i;
3018c2ecf20Sopenharmony_ci	u16 width, height;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cfg); i++)
3048c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, cfg[i].reg, cfg[i].val);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* set output size */
3078c2ecf20Sopenharmony_ci	width = gspca_dev->pixfmt.width;
3088c2ecf20Sopenharmony_ci	height = gspca_dev->pixfmt.height;
3098c2ecf20Sopenharmony_ci	if (width <= 640 && height <= 512) { /* context A (half readout speed)*/
3108c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x1a7, width);
3118c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x1aa, height);
3128c2ecf20Sopenharmony_ci		/* set read mode context A */
3138c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x0c8, 0x0000);
3148c2ecf20Sopenharmony_ci		/* set resize, read mode, vblank, hblank context A */
3158c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x2c8, 0x0000);
3168c2ecf20Sopenharmony_ci	} else { /* context B (full readout speed) */
3178c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x1a1, width);
3188c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x1a4, height);
3198c2ecf20Sopenharmony_ci		/* set read mode context B */
3208c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x0c8, 0x0008);
3218c2ecf20Sopenharmony_ci		/* set resize, read mode, vblank, hblank context B */
3228c2ecf20Sopenharmony_ci		sensor_write(gspca_dev, 0x2c8, 0x040b);
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void stk1135_configure_clock(struct gspca_dev *gspca_dev)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	/* configure SCLKOUT */
3298c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TMGEN, 0x12);
3308c2ecf20Sopenharmony_ci	/* set 1 clock per pixel */
3318c2ecf20Sopenharmony_ci	/* and positive edge clocked pulse high when pixel counter = 0 */
3328c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TCP1 + 0, 0x41);
3338c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TCP1 + 1, 0x00);
3348c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TCP1 + 2, 0x00);
3358c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TCP1 + 3, 0x00);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* enable CLKOUT for sensor */
3388c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SENSO + 0, 0x10);
3398c2ecf20Sopenharmony_ci	/* disable STOP clock */
3408c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x00);
3418c2ecf20Sopenharmony_ci	/* set lower 8 bits of PLL feedback divider */
3428c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SENSO + 3, 0x07);
3438c2ecf20Sopenharmony_ci	/* set other PLL parameters */
3448c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_PLLFD, 0x06);
3458c2ecf20Sopenharmony_ci	/* enable timing generator */
3468c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TMGEN, 0x80);
3478c2ecf20Sopenharmony_ci	/* enable PLL */
3488c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SENSO + 2, 0x04);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* set serial interface clock divider (30MHz/0x1f*16+2) = 60240 kHz) */
3518c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SICTL + 2, 0x1f);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* wait a while for sensor to catch up */
3548c2ecf20Sopenharmony_ci	udelay(1000);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void stk1135_camera_disable(struct gspca_dev *gspca_dev)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	/* set capture end Y position to 0 */
3608c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CIEPO + 2, 0x00);
3618c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CIEPO + 3, 0x00);
3628c2ecf20Sopenharmony_ci	/* disable capture */
3638c2ecf20Sopenharmony_ci	reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x00, 0x80);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* enable sensor standby and diasble chip enable */
3668c2ecf20Sopenharmony_ci	sensor_write_mask(gspca_dev, 0x00d, 0x0004, 0x000c);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* disable PLL */
3698c2ecf20Sopenharmony_ci	reg_w_mask(gspca_dev, STK1135_REG_SENSO + 2, 0x00, 0x01);
3708c2ecf20Sopenharmony_ci	/* disable timing generator */
3718c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_TMGEN, 0x00);
3728c2ecf20Sopenharmony_ci	/* enable STOP clock */
3738c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x20);
3748c2ecf20Sopenharmony_ci	/* disable CLKOUT for sensor */
3758c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SENSO, 0x00);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* disable sensor (GPIO5) and enable GPIO0,3,6 (?) - sensor standby? */
3788c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_GCTRL, 0x49);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
3828c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	u16 sensor_id;
3858c2ecf20Sopenharmony_ci	char *sensor_name;
3868c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* set GPIO3,4,5,6 direction to output */
3898c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_GCTRL + 2, 0x78);
3908c2ecf20Sopenharmony_ci	/* enable sensor (GPIO5) */
3918c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5));
3928c2ecf20Sopenharmony_ci	/* disable ROM interface */
3938c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_GCTRL + 3, 0x80);
3948c2ecf20Sopenharmony_ci	/* enable interrupts from GPIO8 (flip sensor) and GPIO9 (???) */
3958c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_ICTRL + 1, 0x00);
3968c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_ICTRL + 3, 0x03);
3978c2ecf20Sopenharmony_ci	/* enable remote wakeup from GPIO9 (???) */
3988c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_RMCTL + 1, 0x00);
3998c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_RMCTL + 3, 0x02);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* reset serial interface */
4028c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SICTL, 0x80);
4038c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SICTL, 0x00);
4048c2ecf20Sopenharmony_ci	/* set sensor address */
4058c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SICTL + 3, 0xba);
4068c2ecf20Sopenharmony_ci	/* disable alt 2-wire serial interface */
4078c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_ASIC + 3, 0x00);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	stk1135_configure_clock(gspca_dev);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* read sensor ID */
4128c2ecf20Sopenharmony_ci	sd->sensor_page = 0xff;
4138c2ecf20Sopenharmony_ci	sensor_id = sensor_read(gspca_dev, 0x000);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	switch (sensor_id) {
4168c2ecf20Sopenharmony_ci	case 0x148c:
4178c2ecf20Sopenharmony_ci		sensor_name = "MT9M112";
4188c2ecf20Sopenharmony_ci		break;
4198c2ecf20Sopenharmony_ci	default:
4208c2ecf20Sopenharmony_ci		sensor_name = "unknown";
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	pr_info("Detected sensor type %s (0x%x)\n", sensor_name, sensor_id);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	stk1135_camera_disable(gspca_dev);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci/* -- start the camera -- */
4308c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4338c2ecf20Sopenharmony_ci	u16 width, height;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* enable sensor (GPIO5) */
4368c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5));
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	stk1135_configure_clock(gspca_dev);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* set capture start position X = 0, Y = 0 */
4418c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CISPO + 0, 0x00);
4428c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CISPO + 1, 0x00);
4438c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CISPO + 2, 0x00);
4448c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CISPO + 3, 0x00);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* set capture end position */
4478c2ecf20Sopenharmony_ci	width = gspca_dev->pixfmt.width;
4488c2ecf20Sopenharmony_ci	height = gspca_dev->pixfmt.height;
4498c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CIEPO + 0, width & 0xff);
4508c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CIEPO + 1, width >> 8);
4518c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CIEPO + 2, height & 0xff);
4528c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_CIEPO + 3, height >> 8);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* set 8-bit mode */
4558c2ecf20Sopenharmony_ci	reg_w(gspca_dev, STK1135_REG_SCTRL, 0x20);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	stk1135_configure_mt9m112(gspca_dev);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* enable capture */
4608c2ecf20Sopenharmony_ci	reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x80, 0x80);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err >= 0)
4638c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "camera started alt: 0x%02x\n",
4648c2ecf20Sopenharmony_ci			  gspca_dev->alt);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	sd->pkt_seq = 0;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	usb_set_interface(dev, gspca_dev->iface, 0);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	stk1135_camera_disable(gspca_dev);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "camera stopped\n");
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
4838c2ecf20Sopenharmony_ci			u8 *data,			/* isoc packet */
4848c2ecf20Sopenharmony_ci			int len)			/* iso packet length */
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4878c2ecf20Sopenharmony_ci	int skip = sizeof(struct stk1135_pkt_header);
4888c2ecf20Sopenharmony_ci	bool flip;
4898c2ecf20Sopenharmony_ci	enum gspca_packet_type pkt_type = INTER_PACKET;
4908c2ecf20Sopenharmony_ci	struct stk1135_pkt_header *hdr = (void *)data;
4918c2ecf20Sopenharmony_ci	u8 seq;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (len < 4) {
4948c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_PACK, "received short packet (less than 4 bytes)\n");
4958c2ecf20Sopenharmony_ci		return;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	/* GPIO 8 is flip sensor (1 = normal position, 0 = flipped to back) */
4998c2ecf20Sopenharmony_ci	flip = !(le16_to_cpu(hdr->gpio) & (1 << 8));
5008c2ecf20Sopenharmony_ci	/* it's a switch, needs software debounce */
5018c2ecf20Sopenharmony_ci	if (sd->flip_status != flip)
5028c2ecf20Sopenharmony_ci		sd->flip_debounce++;
5038c2ecf20Sopenharmony_ci	else
5048c2ecf20Sopenharmony_ci		sd->flip_debounce = 0;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	/* check sequence number (not present in new frame packets) */
5078c2ecf20Sopenharmony_ci	if (!(hdr->flags & STK1135_HDR_FRAME_START)) {
5088c2ecf20Sopenharmony_ci		seq = hdr->seq & STK1135_HDR_SEQ_MASK;
5098c2ecf20Sopenharmony_ci		if (seq != sd->pkt_seq) {
5108c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "received out-of-sequence packet\n");
5118c2ecf20Sopenharmony_ci			/* resync sequence and discard packet */
5128c2ecf20Sopenharmony_ci			sd->pkt_seq = seq;
5138c2ecf20Sopenharmony_ci			gspca_dev->last_packet_type = DISCARD_PACKET;
5148c2ecf20Sopenharmony_ci			return;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	sd->pkt_seq++;
5188c2ecf20Sopenharmony_ci	if (sd->pkt_seq > STK1135_HDR_SEQ_MASK)
5198c2ecf20Sopenharmony_ci		sd->pkt_seq = 0;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (len == sizeof(struct stk1135_pkt_header))
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (hdr->flags & STK1135_HDR_FRAME_START) { /* new frame */
5258c2ecf20Sopenharmony_ci		skip = 8;	/* the header is longer */
5268c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
5278c2ecf20Sopenharmony_ci		pkt_type = FIRST_PACKET;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, pkt_type, data + skip, len - skip);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void sethflip(struct gspca_dev *gspca_dev, s32 val)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (sd->flip_status)
5378c2ecf20Sopenharmony_ci		val = !val;
5388c2ecf20Sopenharmony_ci	sensor_write_mask(gspca_dev, 0x020, val ? 0x0002 : 0x0000 , 0x0002);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic void setvflip(struct gspca_dev *gspca_dev, s32 val)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (sd->flip_status)
5468c2ecf20Sopenharmony_ci		val = !val;
5478c2ecf20Sopenharmony_ci	sensor_write_mask(gspca_dev, 0x020, val ? 0x0001 : 0x0000 , 0x0001);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void stk1135_dq_callback(struct gspca_dev *gspca_dev)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (sd->flip_debounce > 100) {
5558c2ecf20Sopenharmony_ci		sd->flip_status = !sd->flip_status;
5568c2ecf20Sopenharmony_ci		sethflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip));
5578c2ecf20Sopenharmony_ci		setvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->vflip));
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
5648c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	gspca_dev->usb_err = 0;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming)
5698c2ecf20Sopenharmony_ci		return 0;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	switch (ctrl->id) {
5728c2ecf20Sopenharmony_ci	case V4L2_CID_HFLIP:
5738c2ecf20Sopenharmony_ci		sethflip(gspca_dev, ctrl->val);
5748c2ecf20Sopenharmony_ci		break;
5758c2ecf20Sopenharmony_ci	case V4L2_CID_VFLIP:
5768c2ecf20Sopenharmony_ci		setvflip(gspca_dev, ctrl->val);
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
5848c2ecf20Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
5858c2ecf20Sopenharmony_ci};
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5908c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
5938c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 2);
5948c2ecf20Sopenharmony_ci	sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
5958c2ecf20Sopenharmony_ci			V4L2_CID_HFLIP, 0, 1, 1, 0);
5968c2ecf20Sopenharmony_ci	sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
5978c2ecf20Sopenharmony_ci			V4L2_CID_VFLIP, 0, 1, 1, 0);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (hdl->error) {
6008c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
6018c2ecf20Sopenharmony_ci		return hdl->error;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic void stk1135_try_fmt(struct gspca_dev *gspca_dev, struct v4l2_format *fmt)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	fmt->fmt.pix.width = clamp(fmt->fmt.pix.width, 32U, 1280U);
6098c2ecf20Sopenharmony_ci	fmt->fmt.pix.height = clamp(fmt->fmt.pix.height, 32U, 1024U);
6108c2ecf20Sopenharmony_ci	/* round up to even numbers */
6118c2ecf20Sopenharmony_ci	fmt->fmt.pix.width += (fmt->fmt.pix.width & 1);
6128c2ecf20Sopenharmony_ci	fmt->fmt.pix.height += (fmt->fmt.pix.height & 1);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
6158c2ecf20Sopenharmony_ci	fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic int stk1135_enum_framesizes(struct gspca_dev *gspca_dev,
6198c2ecf20Sopenharmony_ci			struct v4l2_frmsizeenum *fsize)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_SBGGR8)
6228c2ecf20Sopenharmony_ci		return -EINVAL;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
6258c2ecf20Sopenharmony_ci	fsize->stepwise.min_width = 32;
6268c2ecf20Sopenharmony_ci	fsize->stepwise.min_height = 32;
6278c2ecf20Sopenharmony_ci	fsize->stepwise.max_width = 1280;
6288c2ecf20Sopenharmony_ci	fsize->stepwise.max_height = 1024;
6298c2ecf20Sopenharmony_ci	fsize->stepwise.step_width = 2;
6308c2ecf20Sopenharmony_ci	fsize->stepwise.step_height = 2;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	return 0;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/* sub-driver description */
6368c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
6378c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
6388c2ecf20Sopenharmony_ci	.config = sd_config,
6398c2ecf20Sopenharmony_ci	.init = sd_init,
6408c2ecf20Sopenharmony_ci	.init_controls = sd_init_controls,
6418c2ecf20Sopenharmony_ci	.start = sd_start,
6428c2ecf20Sopenharmony_ci	.stopN = sd_stopN,
6438c2ecf20Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
6448c2ecf20Sopenharmony_ci	.dq_callback = stk1135_dq_callback,
6458c2ecf20Sopenharmony_ci	.try_fmt = stk1135_try_fmt,
6468c2ecf20Sopenharmony_ci	.enum_framesizes = stk1135_enum_framesizes,
6478c2ecf20Sopenharmony_ci};
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci/* -- module initialisation -- */
6508c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
6518c2ecf20Sopenharmony_ci	{USB_DEVICE(0x174f, 0x6a31)},	/* ASUS laptop, MT9M112 sensor */
6528c2ecf20Sopenharmony_ci	{}
6538c2ecf20Sopenharmony_ci};
6548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci/* -- device connect -- */
6578c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
6588c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
6618c2ecf20Sopenharmony_ci				THIS_MODULE);
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
6658c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
6668c2ecf20Sopenharmony_ci	.id_table = device_table,
6678c2ecf20Sopenharmony_ci	.probe = sd_probe,
6688c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
6698c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
6708c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
6718c2ecf20Sopenharmony_ci	.resume = gspca_resume,
6728c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
6738c2ecf20Sopenharmony_ci#endif
6748c2ecf20Sopenharmony_ci};
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
677