18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * saa7110 - Philips SAA7110(A) video decoder driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
88c2ecf20Sopenharmony_ci * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
98c2ecf20Sopenharmony_ci *    - some corrections for Pinnacle Systems Inc. DC10plus card.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
128c2ecf20Sopenharmony_ci *    - moved over to linux>=2.4.x i2c protocol (1/1/2003)
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/wait.h>
218c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
228c2ecf20Sopenharmony_ci#include <linux/i2c.h>
238c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
258c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pauline Middelink");
298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int debug;
338c2ecf20Sopenharmony_cimodule_param(debug, int, 0);
348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-1)");
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define SAA7110_MAX_INPUT	9	/* 6 CVBS, 3 SVHS */
378c2ecf20Sopenharmony_ci#define SAA7110_MAX_OUTPUT	1	/* 1 YUV */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define SAA7110_NR_REG		0x35
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct saa7110 {
428c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
438c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
448c2ecf20Sopenharmony_ci	u8 reg[SAA7110_NR_REG];
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	v4l2_std_id norm;
478c2ecf20Sopenharmony_ci	int input;
488c2ecf20Sopenharmony_ci	int enable;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	wait_queue_head_t wq;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	return container_of(sd, struct saa7110, sd);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	return &container_of(ctrl->handler, struct saa7110, hdl)->sd;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
648c2ecf20Sopenharmony_ci/* I2C support functions						   */
658c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
708c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	decoder->reg[reg] = value;
738c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(client, reg, value);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
798c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
808c2ecf20Sopenharmony_ci	int ret = -1;
818c2ecf20Sopenharmony_ci	u8 reg = *data;		/* first register to write to */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Sanity check */
848c2ecf20Sopenharmony_ci	if (reg + (len - 1) > SAA7110_NR_REG)
858c2ecf20Sopenharmony_ci		return ret;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* the saa7110 has an autoincrement function, use it if
888c2ecf20Sopenharmony_ci	 * the adapter understands raw I2C */
898c2ecf20Sopenharmony_ci	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
908c2ecf20Sopenharmony_ci		ret = i2c_master_send(client, data, len);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		/* Cache the written data */
938c2ecf20Sopenharmony_ci		memcpy(decoder->reg + reg, data + 1, len - 1);
948c2ecf20Sopenharmony_ci	} else {
958c2ecf20Sopenharmony_ci		for (++data, --len; len; len--) {
968c2ecf20Sopenharmony_ci			ret = saa7110_write(sd, reg++, *data++);
978c2ecf20Sopenharmony_ci			if (ret < 0)
988c2ecf20Sopenharmony_ci				break;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return ret;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic inline int saa7110_read(struct v4l2_subdev *sd)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return i2c_smbus_read_byte(client);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
1138c2ecf20Sopenharmony_ci/* SAA7110 functions							   */
1148c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#define FRESP_06H_COMPST 0x03	/*0x13*/
1178c2ecf20Sopenharmony_ci#define FRESP_06H_SVIDEO 0x83	/*0xC0*/
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int saa7110_selmux(struct v4l2_subdev *sd, int chan)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	static const unsigned char modes[9][8] = {
1238c2ecf20Sopenharmony_ci		/* mode 0 */
1248c2ecf20Sopenharmony_ci		{FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03,
1258c2ecf20Sopenharmony_ci			      0x44, 0x75, 0x16},
1268c2ecf20Sopenharmony_ci		/* mode 1 */
1278c2ecf20Sopenharmony_ci		{FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03,
1288c2ecf20Sopenharmony_ci			      0x44, 0x75, 0x16},
1298c2ecf20Sopenharmony_ci		/* mode 2 */
1308c2ecf20Sopenharmony_ci		{FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03,
1318c2ecf20Sopenharmony_ci			      0x60, 0xB5, 0x05},
1328c2ecf20Sopenharmony_ci		/* mode 3 */
1338c2ecf20Sopenharmony_ci		{FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03,
1348c2ecf20Sopenharmony_ci			      0x60, 0xB5, 0x05},
1358c2ecf20Sopenharmony_ci		/* mode 4 */
1368c2ecf20Sopenharmony_ci		{FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83,
1378c2ecf20Sopenharmony_ci			      0x60, 0xB5, 0x03},
1388c2ecf20Sopenharmony_ci		/* mode 5 */
1398c2ecf20Sopenharmony_ci		{FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83,
1408c2ecf20Sopenharmony_ci			      0x60, 0xB5, 0x03},
1418c2ecf20Sopenharmony_ci		/* mode 6 */
1428c2ecf20Sopenharmony_ci		{FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3,
1438c2ecf20Sopenharmony_ci			      0x44, 0x75, 0x12},
1448c2ecf20Sopenharmony_ci		/* mode 7 */
1458c2ecf20Sopenharmony_ci		{FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13,
1468c2ecf20Sopenharmony_ci			      0x60, 0xB5, 0x14},
1478c2ecf20Sopenharmony_ci		/* mode 8 */
1488c2ecf20Sopenharmony_ci		{FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23,
1498c2ecf20Sopenharmony_ci			      0x44, 0x75, 0x21}
1508c2ecf20Sopenharmony_ci	};
1518c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
1528c2ecf20Sopenharmony_ci	const unsigned char *ptr = modes[chan];
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x06, ptr[0]);	/* Luminance control    */
1558c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x20, ptr[1]);	/* Analog Control #1    */
1568c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x21, ptr[2]);	/* Analog Control #2    */
1578c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x22, ptr[3]);	/* Mixer Control #1     */
1588c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x2C, ptr[4]);	/* Mixer Control #2     */
1598c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x30, ptr[5]);	/* ADCs gain control    */
1608c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x31, ptr[6]);	/* Mixer Control #3     */
1618c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x21, ptr[7]);	/* Analog Control #2    */
1628c2ecf20Sopenharmony_ci	decoder->input = chan;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic const unsigned char initseq[1 + SAA7110_NR_REG] = {
1688c2ecf20Sopenharmony_ci	0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00,
1698c2ecf20Sopenharmony_ci	/* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90,
1708c2ecf20Sopenharmony_ci	/* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
1718c2ecf20Sopenharmony_ci	/* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1728c2ecf20Sopenharmony_ci	/* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
1738c2ecf20Sopenharmony_ci	/* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C,
1748c2ecf20Sopenharmony_ci	/* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic v4l2_std_id determine_norm(struct v4l2_subdev *sd)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
1808c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
1818c2ecf20Sopenharmony_ci	int status;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* mode changed, start automatic detection */
1848c2ecf20Sopenharmony_ci	saa7110_write_block(sd, initseq, sizeof(initseq));
1858c2ecf20Sopenharmony_ci	saa7110_selmux(sd, decoder->input);
1868c2ecf20Sopenharmony_ci	prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
1878c2ecf20Sopenharmony_ci	schedule_timeout(msecs_to_jiffies(250));
1888c2ecf20Sopenharmony_ci	finish_wait(&decoder->wq, &wait);
1898c2ecf20Sopenharmony_ci	status = saa7110_read(sd);
1908c2ecf20Sopenharmony_ci	if (status & 0x40) {
1918c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status);
1928c2ecf20Sopenharmony_ci		return V4L2_STD_UNKNOWN;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci	if ((status & 3) == 0) {
1958c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x06, 0x83);
1968c2ecf20Sopenharmony_ci		if (status & 0x20) {
1978c2ecf20Sopenharmony_ci			v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status);
1988c2ecf20Sopenharmony_ci			/*saa7110_write(sd,0x2E,0x81);*/
1998c2ecf20Sopenharmony_ci			return V4L2_STD_NTSC;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status);
2028c2ecf20Sopenharmony_ci		/*saa7110_write(sd,0x2E,0x9A);*/
2038c2ecf20Sopenharmony_ci		return V4L2_STD_PAL;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci	/*saa7110_write(sd,0x06,0x03);*/
2068c2ecf20Sopenharmony_ci	if (status & 0x20) {	/* 60Hz */
2078c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status);
2088c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0D, 0x86);
2098c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0F, 0x50);
2108c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x11, 0x2C);
2118c2ecf20Sopenharmony_ci		/*saa7110_write(sd,0x2E,0x81);*/
2128c2ecf20Sopenharmony_ci		return V4L2_STD_NTSC;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* 50Hz -> PAL/SECAM */
2168c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x0D, 0x86);
2178c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x0F, 0x10);
2188c2ecf20Sopenharmony_ci	saa7110_write(sd, 0x11, 0x59);
2198c2ecf20Sopenharmony_ci	/*saa7110_write(sd,0x2E,0x9A);*/
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
2228c2ecf20Sopenharmony_ci	schedule_timeout(msecs_to_jiffies(250));
2238c2ecf20Sopenharmony_ci	finish_wait(&decoder->wq, &wait);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	status = saa7110_read(sd);
2268c2ecf20Sopenharmony_ci	if ((status & 0x03) == 0x01) {
2278c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status);
2288c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0D, 0x87);
2298c2ecf20Sopenharmony_ci		return V4L2_STD_SECAM;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status);
2328c2ecf20Sopenharmony_ci	return V4L2_STD_PAL;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
2388c2ecf20Sopenharmony_ci	int res = V4L2_IN_ST_NO_SIGNAL;
2398c2ecf20Sopenharmony_ci	int status = saa7110_read(sd);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n",
2428c2ecf20Sopenharmony_ci		       status, (unsigned long long)decoder->norm);
2438c2ecf20Sopenharmony_ci	if (!(status & 0x40))
2448c2ecf20Sopenharmony_ci		res = 0;
2458c2ecf20Sopenharmony_ci	if (!(status & 0x03))
2468c2ecf20Sopenharmony_ci		res |= V4L2_IN_ST_NO_COLOR;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	*pstatus = res;
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	*std &= determine_norm(sd);
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (decoder->norm != std) {
2638c2ecf20Sopenharmony_ci		decoder->norm = std;
2648c2ecf20Sopenharmony_ci		/*saa7110_write(sd, 0x06, 0x03);*/
2658c2ecf20Sopenharmony_ci		if (std & V4L2_STD_NTSC) {
2668c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x0D, 0x86);
2678c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x0F, 0x50);
2688c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x11, 0x2C);
2698c2ecf20Sopenharmony_ci			/*saa7110_write(sd, 0x2E, 0x81);*/
2708c2ecf20Sopenharmony_ci			v4l2_dbg(1, debug, sd, "switched to NTSC\n");
2718c2ecf20Sopenharmony_ci		} else if (std & V4L2_STD_PAL) {
2728c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x0D, 0x86);
2738c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x0F, 0x10);
2748c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x11, 0x59);
2758c2ecf20Sopenharmony_ci			/*saa7110_write(sd, 0x2E, 0x9A);*/
2768c2ecf20Sopenharmony_ci			v4l2_dbg(1, debug, sd, "switched to PAL\n");
2778c2ecf20Sopenharmony_ci		} else if (std & V4L2_STD_SECAM) {
2788c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x0D, 0x87);
2798c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x0F, 0x10);
2808c2ecf20Sopenharmony_ci			saa7110_write(sd, 0x11, 0x59);
2818c2ecf20Sopenharmony_ci			/*saa7110_write(sd, 0x2E, 0x9A);*/
2828c2ecf20Sopenharmony_ci			v4l2_dbg(1, debug, sd, "switched to SECAM\n");
2838c2ecf20Sopenharmony_ci		} else {
2848c2ecf20Sopenharmony_ci			return -EINVAL;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int saa7110_s_routing(struct v4l2_subdev *sd,
2918c2ecf20Sopenharmony_ci			     u32 input, u32 output, u32 config)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (input >= SAA7110_MAX_INPUT) {
2968c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "input=%d not available\n", input);
2978c2ecf20Sopenharmony_ci		return -EINVAL;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	if (decoder->input != input) {
3008c2ecf20Sopenharmony_ci		saa7110_selmux(sd, input);
3018c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "switched to input=%d\n", input);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int saa7110_s_stream(struct v4l2_subdev *sd, int enable)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (decoder->enable != enable) {
3118c2ecf20Sopenharmony_ci		decoder->enable = enable;
3128c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80);
3138c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off");
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int saa7110_s_ctrl(struct v4l2_ctrl *ctrl)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = to_sd(ctrl);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	switch (ctrl->id) {
3238c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
3248c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x19, ctrl->val);
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
3278c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x13, ctrl->val);
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
3308c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x12, ctrl->val);
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
3338c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x07, ctrl->val);
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	default:
3368c2ecf20Sopenharmony_ci		return -EINVAL;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops saa7110_ctrl_ops = {
3448c2ecf20Sopenharmony_ci	.s_ctrl = saa7110_s_ctrl,
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops saa7110_video_ops = {
3488c2ecf20Sopenharmony_ci	.s_std = saa7110_s_std,
3498c2ecf20Sopenharmony_ci	.s_routing = saa7110_s_routing,
3508c2ecf20Sopenharmony_ci	.s_stream = saa7110_s_stream,
3518c2ecf20Sopenharmony_ci	.querystd = saa7110_querystd,
3528c2ecf20Sopenharmony_ci	.g_input_status = saa7110_g_input_status,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops saa7110_ops = {
3568c2ecf20Sopenharmony_ci	.video = &saa7110_video_ops,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int saa7110_probe(struct i2c_client *client,
3628c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct saa7110 *decoder;
3658c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
3668c2ecf20Sopenharmony_ci	int rv;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Check if the adapter supports the needed features */
3698c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter,
3708c2ecf20Sopenharmony_ci		I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
3718c2ecf20Sopenharmony_ci		return -ENODEV;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	v4l_info(client, "chip found @ 0x%x (%s)\n",
3748c2ecf20Sopenharmony_ci			client->addr << 1, client->adapter->name);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
3778c2ecf20Sopenharmony_ci	if (!decoder)
3788c2ecf20Sopenharmony_ci		return -ENOMEM;
3798c2ecf20Sopenharmony_ci	sd = &decoder->sd;
3808c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &saa7110_ops);
3818c2ecf20Sopenharmony_ci	decoder->norm = V4L2_STD_PAL;
3828c2ecf20Sopenharmony_ci	decoder->input = 0;
3838c2ecf20Sopenharmony_ci	decoder->enable = 1;
3848c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&decoder->hdl, 2);
3858c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
3868c2ecf20Sopenharmony_ci		V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
3878c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
3888c2ecf20Sopenharmony_ci		V4L2_CID_CONTRAST, 0, 127, 1, 64);
3898c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
3908c2ecf20Sopenharmony_ci		V4L2_CID_SATURATION, 0, 127, 1, 64);
3918c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
3928c2ecf20Sopenharmony_ci		V4L2_CID_HUE, -128, 127, 1, 0);
3938c2ecf20Sopenharmony_ci	sd->ctrl_handler = &decoder->hdl;
3948c2ecf20Sopenharmony_ci	if (decoder->hdl.error) {
3958c2ecf20Sopenharmony_ci		int err = decoder->hdl.error;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(&decoder->hdl);
3988c2ecf20Sopenharmony_ci		return err;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_setup(&decoder->hdl);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	init_waitqueue_head(&decoder->wq);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	rv = saa7110_write_block(sd, initseq, sizeof(initseq));
4058c2ecf20Sopenharmony_ci	if (rv < 0) {
4068c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "init status %d\n", rv);
4078c2ecf20Sopenharmony_ci	} else {
4088c2ecf20Sopenharmony_ci		int ver, status;
4098c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x21, 0x10);
4108c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0e, 0x18);
4118c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0D, 0x04);
4128c2ecf20Sopenharmony_ci		ver = saa7110_read(sd);
4138c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0D, 0x06);
4148c2ecf20Sopenharmony_ci		/*mdelay(150);*/
4158c2ecf20Sopenharmony_ci		status = saa7110_read(sd);
4168c2ecf20Sopenharmony_ci		v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n",
4178c2ecf20Sopenharmony_ci			       ver, status);
4188c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0D, 0x86);
4198c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x0F, 0x10);
4208c2ecf20Sopenharmony_ci		saa7110_write(sd, 0x11, 0x59);
4218c2ecf20Sopenharmony_ci		/*saa7110_write(sd, 0x2E, 0x9A);*/
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/*saa7110_selmux(sd,0);*/
4258c2ecf20Sopenharmony_ci	/*determine_norm(sd);*/
4268c2ecf20Sopenharmony_ci	/* setup and implicit mode 0 select has been performed */
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int saa7110_remove(struct i2c_client *client)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
4348c2ecf20Sopenharmony_ci	struct saa7110 *decoder = to_saa7110(sd);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
4378c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&decoder->hdl);
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic const struct i2c_device_id saa7110_id[] = {
4448c2ecf20Sopenharmony_ci	{ "saa7110", 0 },
4458c2ecf20Sopenharmony_ci	{ }
4468c2ecf20Sopenharmony_ci};
4478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, saa7110_id);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic struct i2c_driver saa7110_driver = {
4508c2ecf20Sopenharmony_ci	.driver = {
4518c2ecf20Sopenharmony_ci		.name	= "saa7110",
4528c2ecf20Sopenharmony_ci	},
4538c2ecf20Sopenharmony_ci	.probe		= saa7110_probe,
4548c2ecf20Sopenharmony_ci	.remove		= saa7110_remove,
4558c2ecf20Sopenharmony_ci	.id_table	= saa7110_id,
4568c2ecf20Sopenharmony_ci};
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cimodule_i2c_driver(saa7110_driver);
459