xref: /openbmc/linux/drivers/media/i2c/adv7180.c (revision abb7c7c2)
1661521a8SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * adv7180.c Analog Devices ADV7180 video decoder driver
4cb7a01acSMauro Carvalho Chehab  * Copyright (c) 2009 Intel Corporation
5cccb83f7SVladimir Barinov  * Copyright (C) 2013 Cogent Embedded, Inc.
6cccb83f7SVladimir Barinov  * Copyright (C) 2013 Renesas Solutions Corp.
7cb7a01acSMauro Carvalho Chehab  */
8cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
9cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
10cb7a01acSMauro Carvalho Chehab #include <linux/errno.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/interrupt.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
15250121d3SBen Dooks #include <linux/of.h>
1665d9e14aSSteve Longerbeam #include <linux/gpio/consumer.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
18937feeedSHans Verkuil #include <media/v4l2-ioctl.h>
19937feeedSHans Verkuil #include <media/v4l2-event.h>
20cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
21cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h>
23c18818e9SLars-Peter Clausen #include <linux/delay.h>
24cb7a01acSMauro Carvalho Chehab 
25f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
26f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
27f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
28f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
29f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_J				0x4
30f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_M				0x5
31f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL60				0x6
32f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_443				0x7
33f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_BG				0x8
34f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_N				0x9
35f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M				0xa
36f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M_PED				0xb
37f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N				0xc
38f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N_PED			0xd
39f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM				0xe
40f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM_PED			0xf
41f5dde49bSLars-Peter Clausen 
423999e5d0SLars-Peter Clausen #define ADV7180_REG_INPUT_CONTROL			0x0000
43cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
44cb7a01acSMauro Carvalho Chehab 
45c5ef8f8cSLars-Peter Clausen #define ADV7182_REG_INPUT_VIDSEL			0x0002
46c5ef8f8cSLars-Peter Clausen 
47ce5d6290SSteve Longerbeam #define ADV7180_REG_OUTPUT_CONTROL			0x0003
483999e5d0SLars-Peter Clausen #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
49cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
50cb7a01acSMauro Carvalho Chehab 
51ce5d6290SSteve Longerbeam #define ADV7180_REG_AUTODETECT_ENABLE			0x0007
52cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_DEFAULT			0x7f
53cb7a01acSMauro Carvalho Chehab /* Contrast */
543999e5d0SLars-Peter Clausen #define ADV7180_REG_CON		0x0008	/*Unsigned */
55cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MIN		0
56cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_DEF		128
57cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MAX		255
58cb7a01acSMauro Carvalho Chehab /* Brightness*/
593999e5d0SLars-Peter Clausen #define ADV7180_REG_BRI		0x000a	/*Signed */
60cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MIN		-128
61cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_DEF		0
62cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MAX		127
63cb7a01acSMauro Carvalho Chehab /* Hue */
643999e5d0SLars-Peter Clausen #define ADV7180_REG_HUE		0x000b	/*Signed, inverted */
65cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MIN		-127
66cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_DEF		0
67cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MAX		128
68cb7a01acSMauro Carvalho Chehab 
693999e5d0SLars-Peter Clausen #define ADV7180_REG_CTRL		0x000e
70029d6177SLars-Peter Clausen #define ADV7180_CTRL_IRQ_SPACE		0x20
71cb7a01acSMauro Carvalho Chehab 
72029d6177SLars-Peter Clausen #define ADV7180_REG_PWR_MAN		0x0f
73cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_ON		0x04
74cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_OFF		0x24
75cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_RES		0x80
76cb7a01acSMauro Carvalho Chehab 
773999e5d0SLars-Peter Clausen #define ADV7180_REG_STATUS1		0x0010
78cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_IN_LOCK		0x01
79cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_MASK	0x70
80cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
81cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
82cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_M	0x20
83cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_60	0x30
84cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_B_G	0x40
85cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM	0x50
86cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
87cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
88cb7a01acSMauro Carvalho Chehab 
893999e5d0SLars-Peter Clausen #define ADV7180_REG_IDENT 0x0011
90cb7a01acSMauro Carvalho Chehab #define ADV7180_ID_7180 0x18
91cb7a01acSMauro Carvalho Chehab 
92ce5d6290SSteve Longerbeam #define ADV7180_REG_STATUS3		0x0013
93ce5d6290SSteve Longerbeam #define ADV7180_REG_ANALOG_CLAMP_CTL	0x0014
94ce5d6290SSteve Longerbeam #define ADV7180_REG_SHAP_FILTER_CTL_1	0x0017
95ce5d6290SSteve Longerbeam #define ADV7180_REG_CTRL_2		0x001d
96ce5d6290SSteve Longerbeam #define ADV7180_REG_VSYNC_FIELD_CTL_1	0x0031
97ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_1	0x003d
98ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_2	0x003e
99ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_3	0x003f
100ce5d6290SSteve Longerbeam #define ADV7180_REG_LOCK_CNT		0x0051
101ce5d6290SSteve Longerbeam #define ADV7180_REG_CVBS_TRIM		0x0052
102ce5d6290SSteve Longerbeam #define ADV7180_REG_CLAMP_ADJ		0x005a
103ce5d6290SSteve Longerbeam #define ADV7180_REG_RES_CIR		0x005f
104ce5d6290SSteve Longerbeam #define ADV7180_REG_DIFF_MODE		0x0060
105ce5d6290SSteve Longerbeam 
10652e37f0aSSteve Longerbeam #define ADV7180_REG_ICONF1		0x2040
107cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW	0x01
108cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY	0x10
109cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
110cb7a01acSMauro Carvalho Chehab /* Saturation */
1113999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
1123999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
113cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN		0
114cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF		128
115cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX		255
116cb7a01acSMauro Carvalho Chehab 
117cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK	0x01
118cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK	0x02
11952e37f0aSSteve Longerbeam #define ADV7180_REG_ISR1	0x2042
12052e37f0aSSteve Longerbeam #define ADV7180_REG_ICR1	0x2043
12152e37f0aSSteve Longerbeam #define ADV7180_REG_IMR1	0x2044
12252e37f0aSSteve Longerbeam #define ADV7180_REG_IMR2	0x2048
123cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE	0x08
12452e37f0aSSteve Longerbeam #define ADV7180_REG_ISR3	0x204A
12552e37f0aSSteve Longerbeam #define ADV7180_REG_ICR3	0x204B
12652e37f0aSSteve Longerbeam #define ADV7180_REG_IMR3	0x204C
12752e37f0aSSteve Longerbeam #define ADV7180_REG_IMR4	0x2050
128cb7a01acSMauro Carvalho Chehab 
1293999e5d0SLars-Peter Clausen #define ADV7180_REG_NTSC_V_BIT_END	0x00E6
130cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
131cb7a01acSMauro Carvalho Chehab 
132851a54efSLars-Peter Clausen #define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
133b37135e3SLars-Peter Clausen #define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
134b37135e3SLars-Peter Clausen 
135ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL1		0x4080
136ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL5		0x4084
13708b717c2SLars-Peter Clausen #define ADV7180_REG_FLCONTROL		0x40e0
13808b717c2SLars-Peter Clausen #define ADV7180_FLCONTROL_FL_ENABLE 0x1
13908b717c2SLars-Peter Clausen 
140ce5d6290SSteve Longerbeam #define ADV7180_REG_RST_CLAMP	0x809c
141ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ1	0x80b6
142ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ2	0x80c0
143ce5d6290SSteve Longerbeam 
144b37135e3SLars-Peter Clausen #define ADV7180_CSI_REG_PWRDN	0x00
145b37135e3SLars-Peter Clausen #define ADV7180_CSI_PWRDN	0x80
146b37135e3SLars-Peter Clausen 
147f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN1 0x00
148f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN2 0x01
149f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN3 0x02
150f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN4 0x03
151f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN5 0x04
152f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN6 0x05
153f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
154f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
155f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
156f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
157f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
158f5dde49bSLars-Peter Clausen 
159c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN1 0x00
160c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN2 0x01
161c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN3 0x02
162c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN4 0x03
163c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN5 0x04
164c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN6 0x05
165c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN7 0x06
166c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN8 0x07
167c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
168c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
169c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
170c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
171c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
172c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
173c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
174c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
175c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
176c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
177c5ef8f8cSLars-Peter Clausen 
178b37135e3SLars-Peter Clausen #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
179851a54efSLars-Peter Clausen #define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
180b37135e3SLars-Peter Clausen 
18108b717c2SLars-Peter Clausen #define V4L2_CID_ADV_FAST_SWITCH	(V4L2_CID_USER_ADV7180_BASE + 0x00)
18208b717c2SLars-Peter Clausen 
1839483a3f8STim Harvey /* Initial number of frames to skip to avoid possible garbage */
1849483a3f8STim Harvey #define ADV7180_NUM_OF_SKIP_FRAMES       2
1859483a3f8STim Harvey 
186f5dde49bSLars-Peter Clausen struct adv7180_state;
187f5dde49bSLars-Peter Clausen 
188f5dde49bSLars-Peter Clausen #define ADV7180_FLAG_RESET_POWERED	BIT(0)
189bf7dcb80SLars-Peter Clausen #define ADV7180_FLAG_V2			BIT(1)
190b37135e3SLars-Peter Clausen #define ADV7180_FLAG_MIPI_CSI2		BIT(2)
191851a54efSLars-Peter Clausen #define ADV7180_FLAG_I2P		BIT(3)
192f5dde49bSLars-Peter Clausen 
193f5dde49bSLars-Peter Clausen struct adv7180_chip_info {
194f5dde49bSLars-Peter Clausen 	unsigned int flags;
195f5dde49bSLars-Peter Clausen 	unsigned int valid_input_mask;
196f5dde49bSLars-Peter Clausen 	int (*set_std)(struct adv7180_state *st, unsigned int std);
197f5dde49bSLars-Peter Clausen 	int (*select_input)(struct adv7180_state *st, unsigned int input);
198f5dde49bSLars-Peter Clausen 	int (*init)(struct adv7180_state *state);
199f5dde49bSLars-Peter Clausen };
200f5dde49bSLars-Peter Clausen 
201cb7a01acSMauro Carvalho Chehab struct adv7180_state {
202cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;
203cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev	sd;
204d5d51a82SLars-Peter Clausen 	struct media_pad	pad;
205cb7a01acSMauro Carvalho Chehab 	struct mutex		mutex; /* mutual excl. when accessing chip */
206cb7a01acSMauro Carvalho Chehab 	int			irq;
20765d9e14aSSteve Longerbeam 	struct gpio_desc	*pwdn_gpio;
208*abb7c7c2SFrieder Schrempf 	struct gpio_desc	*rst_gpio;
209cb7a01acSMauro Carvalho Chehab 	v4l2_std_id		curr_norm;
210e246c333SLars-Peter Clausen 	bool			powered;
211937feeedSHans Verkuil 	bool			streaming;
212cb7a01acSMauro Carvalho Chehab 	u8			input;
2133999e5d0SLars-Peter Clausen 
2143999e5d0SLars-Peter Clausen 	struct i2c_client	*client;
2153999e5d0SLars-Peter Clausen 	unsigned int		register_page;
216b37135e3SLars-Peter Clausen 	struct i2c_client	*csi_client;
217851a54efSLars-Peter Clausen 	struct i2c_client	*vpp_client;
218f5dde49bSLars-Peter Clausen 	const struct adv7180_chip_info *chip_info;
219851a54efSLars-Peter Clausen 	enum v4l2_field		field;
220cb7a01acSMauro Carvalho Chehab };
221cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
222cb7a01acSMauro Carvalho Chehab 					    struct adv7180_state,	\
223cb7a01acSMauro Carvalho Chehab 					    ctrl_hdl)->sd)
224cb7a01acSMauro Carvalho Chehab 
2253999e5d0SLars-Peter Clausen static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
2263999e5d0SLars-Peter Clausen {
2273999e5d0SLars-Peter Clausen 	if (state->register_page != page) {
2283999e5d0SLars-Peter Clausen 		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
2293999e5d0SLars-Peter Clausen 			page);
2303999e5d0SLars-Peter Clausen 		state->register_page = page;
2313999e5d0SLars-Peter Clausen 	}
2323999e5d0SLars-Peter Clausen 
2333999e5d0SLars-Peter Clausen 	return 0;
2343999e5d0SLars-Peter Clausen }
2353999e5d0SLars-Peter Clausen 
2363999e5d0SLars-Peter Clausen static int adv7180_write(struct adv7180_state *state, unsigned int reg,
2373999e5d0SLars-Peter Clausen 	unsigned int value)
2383999e5d0SLars-Peter Clausen {
2393999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2403999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2413999e5d0SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
2423999e5d0SLars-Peter Clausen }
2433999e5d0SLars-Peter Clausen 
2443999e5d0SLars-Peter Clausen static int adv7180_read(struct adv7180_state *state, unsigned int reg)
2453999e5d0SLars-Peter Clausen {
2463999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2473999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2483999e5d0SLars-Peter Clausen 	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
2493999e5d0SLars-Peter Clausen }
2503999e5d0SLars-Peter Clausen 
251b37135e3SLars-Peter Clausen static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
252b37135e3SLars-Peter Clausen 	unsigned int value)
253b37135e3SLars-Peter Clausen {
254b37135e3SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
255b37135e3SLars-Peter Clausen }
256b37135e3SLars-Peter Clausen 
257f5dde49bSLars-Peter Clausen static int adv7180_set_video_standard(struct adv7180_state *state,
258f5dde49bSLars-Peter Clausen 	unsigned int std)
259f5dde49bSLars-Peter Clausen {
260f5dde49bSLars-Peter Clausen 	return state->chip_info->set_std(state, std);
261f5dde49bSLars-Peter Clausen }
2623999e5d0SLars-Peter Clausen 
263851a54efSLars-Peter Clausen static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
264851a54efSLars-Peter Clausen 	unsigned int value)
265851a54efSLars-Peter Clausen {
266851a54efSLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
267851a54efSLars-Peter Clausen }
268851a54efSLars-Peter Clausen 
269cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
270cb7a01acSMauro Carvalho Chehab {
271b294a192SVladimir Barinov 	/* in case V4L2_IN_ST_NO_SIGNAL */
272b294a192SVladimir Barinov 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
273b294a192SVladimir Barinov 		return V4L2_STD_UNKNOWN;
274b294a192SVladimir Barinov 
275cb7a01acSMauro Carvalho Chehab 	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
276cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
277cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
278cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
279cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
280cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_M:
281cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
282cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_60:
283cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_60;
284cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_B_G:
285cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
286cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM:
287cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
288cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_COMB:
289cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
290cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM_525:
291cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
292cb7a01acSMauro Carvalho Chehab 	default:
293cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
294cb7a01acSMauro Carvalho Chehab 	}
295cb7a01acSMauro Carvalho Chehab }
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std)
298cb7a01acSMauro Carvalho Chehab {
299cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
300f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL60;
301cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_NTSC_443)
302f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_443;
303cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_N)
304f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_N;
305cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_M)
306f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_M;
307cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_Nc)
308f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_COMB_N;
309cb7a01acSMauro Carvalho Chehab 
310cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_PAL)
311f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_BG;
312cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC)
313f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_M;
314cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_SECAM)
315f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_SECAM;
316cb7a01acSMauro Carvalho Chehab 
317cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
318cb7a01acSMauro Carvalho Chehab }
319cb7a01acSMauro Carvalho Chehab 
320cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1)
321cb7a01acSMauro Carvalho Chehab {
322cb7a01acSMauro Carvalho Chehab 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
323cb7a01acSMauro Carvalho Chehab 		return V4L2_IN_ST_NO_SIGNAL;
324cb7a01acSMauro Carvalho Chehab 
325cb7a01acSMauro Carvalho Chehab 	return 0;
326cb7a01acSMauro Carvalho Chehab }
327cb7a01acSMauro Carvalho Chehab 
3283999e5d0SLars-Peter Clausen static int __adv7180_status(struct adv7180_state *state, u32 *status,
329cb7a01acSMauro Carvalho Chehab 			    v4l2_std_id *std)
330cb7a01acSMauro Carvalho Chehab {
3313999e5d0SLars-Peter Clausen 	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
332cb7a01acSMauro Carvalho Chehab 
333cb7a01acSMauro Carvalho Chehab 	if (status1 < 0)
334cb7a01acSMauro Carvalho Chehab 		return status1;
335cb7a01acSMauro Carvalho Chehab 
336cb7a01acSMauro Carvalho Chehab 	if (status)
337cb7a01acSMauro Carvalho Chehab 		*status = adv7180_status_to_v4l2(status1);
338cb7a01acSMauro Carvalho Chehab 	if (std)
339cb7a01acSMauro Carvalho Chehab 		*std = adv7180_std_to_v4l2(status1);
340cb7a01acSMauro Carvalho Chehab 
341cb7a01acSMauro Carvalho Chehab 	return 0;
342cb7a01acSMauro Carvalho Chehab }
343cb7a01acSMauro Carvalho Chehab 
344cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
345cb7a01acSMauro Carvalho Chehab {
346cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7180_state, sd);
347cb7a01acSMauro Carvalho Chehab }
348cb7a01acSMauro Carvalho Chehab 
349cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
350cb7a01acSMauro Carvalho Chehab {
351cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
352cb7a01acSMauro Carvalho Chehab 	int err = mutex_lock_interruptible(&state->mutex);
353cb7a01acSMauro Carvalho Chehab 	if (err)
354cb7a01acSMauro Carvalho Chehab 		return err;
355cb7a01acSMauro Carvalho Chehab 
356937feeedSHans Verkuil 	if (state->streaming) {
357937feeedSHans Verkuil 		err = -EBUSY;
358937feeedSHans Verkuil 		goto unlock;
359937feeedSHans Verkuil 	}
360cb7a01acSMauro Carvalho Chehab 
361937feeedSHans Verkuil 	err = adv7180_set_video_standard(state,
362937feeedSHans Verkuil 			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
363937feeedSHans Verkuil 	if (err)
364937feeedSHans Verkuil 		goto unlock;
365937feeedSHans Verkuil 
366937feeedSHans Verkuil 	msleep(100);
367937feeedSHans Verkuil 	__adv7180_status(state, NULL, std);
368937feeedSHans Verkuil 
369937feeedSHans Verkuil 	err = v4l2_std_to_adv7180(state->curr_norm);
370937feeedSHans Verkuil 	if (err < 0)
371937feeedSHans Verkuil 		goto unlock;
372937feeedSHans Verkuil 
373937feeedSHans Verkuil 	err = adv7180_set_video_standard(state, err);
374937feeedSHans Verkuil 
375937feeedSHans Verkuil unlock:
376cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
377cb7a01acSMauro Carvalho Chehab 	return err;
378cb7a01acSMauro Carvalho Chehab }
379cb7a01acSMauro Carvalho Chehab 
380cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
381cb7a01acSMauro Carvalho Chehab 			     u32 output, u32 config)
382cb7a01acSMauro Carvalho Chehab {
383cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
384cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
385cb7a01acSMauro Carvalho Chehab 
386cb7a01acSMauro Carvalho Chehab 	if (ret)
387cb7a01acSMauro Carvalho Chehab 		return ret;
388cb7a01acSMauro Carvalho Chehab 
389f5dde49bSLars-Peter Clausen 	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
390f5dde49bSLars-Peter Clausen 		ret = -EINVAL;
391cb7a01acSMauro Carvalho Chehab 		goto out;
392f5dde49bSLars-Peter Clausen 	}
393cb7a01acSMauro Carvalho Chehab 
394f5dde49bSLars-Peter Clausen 	ret = state->chip_info->select_input(state, input);
395cb7a01acSMauro Carvalho Chehab 
396f5dde49bSLars-Peter Clausen 	if (ret == 0)
397cb7a01acSMauro Carvalho Chehab 		state->input = input;
398cb7a01acSMauro Carvalho Chehab out:
399cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
400cb7a01acSMauro Carvalho Chehab 	return ret;
401cb7a01acSMauro Carvalho Chehab }
402cb7a01acSMauro Carvalho Chehab 
403cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
404cb7a01acSMauro Carvalho Chehab {
405cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
406cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
407cb7a01acSMauro Carvalho Chehab 	if (ret)
408cb7a01acSMauro Carvalho Chehab 		return ret;
409cb7a01acSMauro Carvalho Chehab 
4103999e5d0SLars-Peter Clausen 	ret = __adv7180_status(state, status, NULL);
411cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
412cb7a01acSMauro Carvalho Chehab 	return ret;
413cb7a01acSMauro Carvalho Chehab }
414cb7a01acSMauro Carvalho Chehab 
4153e35e33cSLars-Peter Clausen static int adv7180_program_std(struct adv7180_state *state)
4163e35e33cSLars-Peter Clausen {
4173e35e33cSLars-Peter Clausen 	int ret;
4183e35e33cSLars-Peter Clausen 
4193e35e33cSLars-Peter Clausen 	ret = v4l2_std_to_adv7180(state->curr_norm);
4203e35e33cSLars-Peter Clausen 	if (ret < 0)
4213e35e33cSLars-Peter Clausen 		return ret;
4223e35e33cSLars-Peter Clausen 
423f5dde49bSLars-Peter Clausen 	ret = adv7180_set_video_standard(state, ret);
4243e35e33cSLars-Peter Clausen 	if (ret < 0)
4253e35e33cSLars-Peter Clausen 		return ret;
4263e35e33cSLars-Peter Clausen 	return 0;
4273e35e33cSLars-Peter Clausen }
4283e35e33cSLars-Peter Clausen 
429cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
430cb7a01acSMauro Carvalho Chehab {
431cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
432cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
4333e35e33cSLars-Peter Clausen 
434cb7a01acSMauro Carvalho Chehab 	if (ret)
435cb7a01acSMauro Carvalho Chehab 		return ret;
436cb7a01acSMauro Carvalho Chehab 
4373e35e33cSLars-Peter Clausen 	/* Make sure we can support this std */
438cb7a01acSMauro Carvalho Chehab 	ret = v4l2_std_to_adv7180(std);
439cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
440cb7a01acSMauro Carvalho Chehab 		goto out;
441cb7a01acSMauro Carvalho Chehab 
442cb7a01acSMauro Carvalho Chehab 	state->curr_norm = std;
4433e35e33cSLars-Peter Clausen 
4443e35e33cSLars-Peter Clausen 	ret = adv7180_program_std(state);
445cb7a01acSMauro Carvalho Chehab out:
446cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
447cb7a01acSMauro Carvalho Chehab 	return ret;
448cb7a01acSMauro Carvalho Chehab }
449cb7a01acSMauro Carvalho Chehab 
450d0fadc86SNiklas Söderlund static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
451d0fadc86SNiklas Söderlund {
452d0fadc86SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
453d0fadc86SNiklas Söderlund 
454d0fadc86SNiklas Söderlund 	*norm = state->curr_norm;
455d0fadc86SNiklas Söderlund 
456d0fadc86SNiklas Söderlund 	return 0;
457d0fadc86SNiklas Söderlund }
458d0fadc86SNiklas Söderlund 
45938566d28SNiklas Söderlund static int adv7180_g_frame_interval(struct v4l2_subdev *sd,
46038566d28SNiklas Söderlund 				    struct v4l2_subdev_frame_interval *fi)
46138566d28SNiklas Söderlund {
46238566d28SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
46338566d28SNiklas Söderlund 
46438566d28SNiklas Söderlund 	if (state->curr_norm & V4L2_STD_525_60) {
46538566d28SNiklas Söderlund 		fi->interval.numerator = 1001;
46638566d28SNiklas Söderlund 		fi->interval.denominator = 30000;
46738566d28SNiklas Söderlund 	} else {
46838566d28SNiklas Söderlund 		fi->interval.numerator = 1;
46938566d28SNiklas Söderlund 		fi->interval.denominator = 25;
47038566d28SNiklas Söderlund 	}
47138566d28SNiklas Söderlund 
47238566d28SNiklas Söderlund 	return 0;
47338566d28SNiklas Söderlund }
47438566d28SNiklas Söderlund 
47565d9e14aSSteve Longerbeam static void adv7180_set_power_pin(struct adv7180_state *state, bool on)
47665d9e14aSSteve Longerbeam {
47765d9e14aSSteve Longerbeam 	if (!state->pwdn_gpio)
47865d9e14aSSteve Longerbeam 		return;
47965d9e14aSSteve Longerbeam 
48065d9e14aSSteve Longerbeam 	if (on) {
48165d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 0);
48265d9e14aSSteve Longerbeam 		usleep_range(5000, 10000);
48365d9e14aSSteve Longerbeam 	} else {
48465d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 1);
48565d9e14aSSteve Longerbeam 	}
48665d9e14aSSteve Longerbeam }
48765d9e14aSSteve Longerbeam 
488*abb7c7c2SFrieder Schrempf static void adv7180_set_reset_pin(struct adv7180_state *state, bool on)
489*abb7c7c2SFrieder Schrempf {
490*abb7c7c2SFrieder Schrempf 	if (!state->rst_gpio)
491*abb7c7c2SFrieder Schrempf 		return;
492*abb7c7c2SFrieder Schrempf 
493*abb7c7c2SFrieder Schrempf 	if (on) {
494*abb7c7c2SFrieder Schrempf 		gpiod_set_value_cansleep(state->rst_gpio, 1);
495*abb7c7c2SFrieder Schrempf 	} else {
496*abb7c7c2SFrieder Schrempf 		gpiod_set_value_cansleep(state->rst_gpio, 0);
497*abb7c7c2SFrieder Schrempf 		usleep_range(5000, 10000);
498*abb7c7c2SFrieder Schrempf 	}
499*abb7c7c2SFrieder Schrempf }
500*abb7c7c2SFrieder Schrempf 
5013999e5d0SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state, bool on)
502e246c333SLars-Peter Clausen {
503e246c333SLars-Peter Clausen 	u8 val;
504b37135e3SLars-Peter Clausen 	int ret;
505e246c333SLars-Peter Clausen 
506e246c333SLars-Peter Clausen 	if (on)
507e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_ON;
508e246c333SLars-Peter Clausen 	else
509e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_OFF;
510e246c333SLars-Peter Clausen 
511b37135e3SLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
512b37135e3SLars-Peter Clausen 	if (ret)
513b37135e3SLars-Peter Clausen 		return ret;
514b37135e3SLars-Peter Clausen 
515b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
516b37135e3SLars-Peter Clausen 		if (on) {
517b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xDE, 0x02);
518b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD2, 0xF7);
519b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD8, 0x65);
520b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xE0, 0x09);
521b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x2C, 0x00);
522851a54efSLars-Peter Clausen 			if (state->field == V4L2_FIELD_NONE)
523851a54efSLars-Peter Clausen 				adv7180_csi_write(state, 0x1D, 0x80);
524b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x00);
525b37135e3SLars-Peter Clausen 		} else {
526b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x80);
527b37135e3SLars-Peter Clausen 		}
528b37135e3SLars-Peter Clausen 	}
529b37135e3SLars-Peter Clausen 
530b37135e3SLars-Peter Clausen 	return 0;
531e246c333SLars-Peter Clausen }
532e246c333SLars-Peter Clausen 
533e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on)
534e246c333SLars-Peter Clausen {
535e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
536e246c333SLars-Peter Clausen 	int ret;
537e246c333SLars-Peter Clausen 
538e246c333SLars-Peter Clausen 	ret = mutex_lock_interruptible(&state->mutex);
539e246c333SLars-Peter Clausen 	if (ret)
540e246c333SLars-Peter Clausen 		return ret;
541e246c333SLars-Peter Clausen 
5423999e5d0SLars-Peter Clausen 	ret = adv7180_set_power(state, on);
543e246c333SLars-Peter Clausen 	if (ret == 0)
544e246c333SLars-Peter Clausen 		state->powered = on;
545e246c333SLars-Peter Clausen 
546e246c333SLars-Peter Clausen 	mutex_unlock(&state->mutex);
547e246c333SLars-Peter Clausen 	return ret;
548e246c333SLars-Peter Clausen }
549e246c333SLars-Peter Clausen 
550cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
551cb7a01acSMauro Carvalho Chehab {
552cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
553cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
554cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
555cb7a01acSMauro Carvalho Chehab 	int val;
556cb7a01acSMauro Carvalho Chehab 
557cb7a01acSMauro Carvalho Chehab 	if (ret)
558cb7a01acSMauro Carvalho Chehab 		return ret;
559cb7a01acSMauro Carvalho Chehab 	val = ctrl->val;
560cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
561cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
5623999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_BRI, val);
563cb7a01acSMauro Carvalho Chehab 		break;
564cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
565cb7a01acSMauro Carvalho Chehab 		/*Hue is inverted according to HSL chart */
5663999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
567cb7a01acSMauro Carvalho Chehab 		break;
568cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
5693999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_CON, val);
570cb7a01acSMauro Carvalho Chehab 		break;
571cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
572cb7a01acSMauro Carvalho Chehab 		/*
573cb7a01acSMauro Carvalho Chehab 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
574cb7a01acSMauro Carvalho Chehab 		 *Let's not confuse the user, everybody understands saturation
575cb7a01acSMauro Carvalho Chehab 		 */
5763999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
577cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
578cb7a01acSMauro Carvalho Chehab 			break;
5793999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
580cb7a01acSMauro Carvalho Chehab 		break;
58108b717c2SLars-Peter Clausen 	case V4L2_CID_ADV_FAST_SWITCH:
58208b717c2SLars-Peter Clausen 		if (ctrl->val) {
58308b717c2SLars-Peter Clausen 			/* ADI required write */
58408b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0x44);
58508b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL,
58608b717c2SLars-Peter Clausen 				ADV7180_FLCONTROL_FL_ENABLE);
58708b717c2SLars-Peter Clausen 		} else {
58808b717c2SLars-Peter Clausen 			/* ADI required write */
58908b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0xc4);
59008b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
59108b717c2SLars-Peter Clausen 		}
59208b717c2SLars-Peter Clausen 		break;
593cb7a01acSMauro Carvalho Chehab 	default:
594cb7a01acSMauro Carvalho Chehab 		ret = -EINVAL;
595cb7a01acSMauro Carvalho Chehab 	}
596cb7a01acSMauro Carvalho Chehab 
597cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
598cb7a01acSMauro Carvalho Chehab 	return ret;
599cb7a01acSMauro Carvalho Chehab }
600cb7a01acSMauro Carvalho Chehab 
601cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
602cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7180_s_ctrl,
603cb7a01acSMauro Carvalho Chehab };
604cb7a01acSMauro Carvalho Chehab 
60508b717c2SLars-Peter Clausen static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
60608b717c2SLars-Peter Clausen 	.ops = &adv7180_ctrl_ops,
60708b717c2SLars-Peter Clausen 	.id = V4L2_CID_ADV_FAST_SWITCH,
60808b717c2SLars-Peter Clausen 	.name = "Fast Switching",
60908b717c2SLars-Peter Clausen 	.type = V4L2_CTRL_TYPE_BOOLEAN,
61008b717c2SLars-Peter Clausen 	.min = 0,
61108b717c2SLars-Peter Clausen 	.max = 1,
61208b717c2SLars-Peter Clausen 	.step = 1,
61308b717c2SLars-Peter Clausen };
61408b717c2SLars-Peter Clausen 
615cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state)
616cb7a01acSMauro Carvalho Chehab {
617cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
618cb7a01acSMauro Carvalho Chehab 
619cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
620cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
621cb7a01acSMauro Carvalho Chehab 			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
622cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
623cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
624cb7a01acSMauro Carvalho Chehab 			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
625cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
626cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
627cb7a01acSMauro Carvalho Chehab 			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
628cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
629cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
630cb7a01acSMauro Carvalho Chehab 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
63108b717c2SLars-Peter Clausen 	v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
63208b717c2SLars-Peter Clausen 
633cb7a01acSMauro Carvalho Chehab 	state->sd.ctrl_handler = &state->ctrl_hdl;
634cb7a01acSMauro Carvalho Chehab 	if (state->ctrl_hdl.error) {
635cb7a01acSMauro Carvalho Chehab 		int err = state->ctrl_hdl.error;
636cb7a01acSMauro Carvalho Chehab 
637cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->ctrl_hdl);
638cb7a01acSMauro Carvalho Chehab 		return err;
639cb7a01acSMauro Carvalho Chehab 	}
640cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
641cb7a01acSMauro Carvalho Chehab 
642cb7a01acSMauro Carvalho Chehab 	return 0;
643cb7a01acSMauro Carvalho Chehab }
644cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state)
645cb7a01acSMauro Carvalho Chehab {
646cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
647cb7a01acSMauro Carvalho Chehab }
648cb7a01acSMauro Carvalho Chehab 
649d5d51a82SLars-Peter Clausen static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
6500d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
651d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_mbus_code_enum *code)
652cccb83f7SVladimir Barinov {
653d5d51a82SLars-Peter Clausen 	if (code->index != 0)
654cccb83f7SVladimir Barinov 		return -EINVAL;
655cccb83f7SVladimir Barinov 
6566de690ddSNiklas Söderlund 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
657cccb83f7SVladimir Barinov 
658cccb83f7SVladimir Barinov 	return 0;
659cccb83f7SVladimir Barinov }
660cccb83f7SVladimir Barinov 
661cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
662cccb83f7SVladimir Barinov 			    struct v4l2_mbus_framefmt *fmt)
663cccb83f7SVladimir Barinov {
664cccb83f7SVladimir Barinov 	struct adv7180_state *state = to_state(sd);
665cccb83f7SVladimir Barinov 
6666de690ddSNiklas Söderlund 	fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
667cccb83f7SVladimir Barinov 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
668cccb83f7SVladimir Barinov 	fmt->width = 720;
669cccb83f7SVladimir Barinov 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
670cccb83f7SVladimir Barinov 
6716457b626SNiklas Söderlund 	if (state->field == V4L2_FIELD_ALTERNATE)
6726457b626SNiklas Söderlund 		fmt->height /= 2;
6736457b626SNiklas Söderlund 
674cccb83f7SVladimir Barinov 	return 0;
675cccb83f7SVladimir Barinov }
676cccb83f7SVladimir Barinov 
677851a54efSLars-Peter Clausen static int adv7180_set_field_mode(struct adv7180_state *state)
678851a54efSLars-Peter Clausen {
679851a54efSLars-Peter Clausen 	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
680851a54efSLars-Peter Clausen 		return 0;
681851a54efSLars-Peter Clausen 
682851a54efSLars-Peter Clausen 	if (state->field == V4L2_FIELD_NONE) {
683851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
684851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x20);
685851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x28);
686851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x38);
687851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x30);
688851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x30);
689851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x80);
690851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x70);
691851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x50);
692851a54efSLars-Peter Clausen 		}
693851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x00);
694851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x00);
695851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x80);
696851a54efSLars-Peter Clausen 	} else {
697851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
698851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x18);
699851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x18);
700851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x30);
701851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x20);
702851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x28);
703851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x40);
704851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x58);
705851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x30);
706851a54efSLars-Peter Clausen 		}
707851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x70);
708851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x80);
709851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x00);
710851a54efSLars-Peter Clausen 	}
711851a54efSLars-Peter Clausen 
712851a54efSLars-Peter Clausen 	return 0;
713851a54efSLars-Peter Clausen }
714851a54efSLars-Peter Clausen 
715d5d51a82SLars-Peter Clausen static int adv7180_get_pad_format(struct v4l2_subdev *sd,
7160d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
717d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
718d5d51a82SLars-Peter Clausen {
719851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
720851a54efSLars-Peter Clausen 
721851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
7220d346d2aSTomi Valkeinen 		format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0);
723851a54efSLars-Peter Clausen 	} else {
724851a54efSLars-Peter Clausen 		adv7180_mbus_fmt(sd, &format->format);
725851a54efSLars-Peter Clausen 		format->format.field = state->field;
726851a54efSLars-Peter Clausen 	}
727851a54efSLars-Peter Clausen 
728851a54efSLars-Peter Clausen 	return 0;
729d5d51a82SLars-Peter Clausen }
730d5d51a82SLars-Peter Clausen 
731d5d51a82SLars-Peter Clausen static int adv7180_set_pad_format(struct v4l2_subdev *sd,
7320d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
733d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
734d5d51a82SLars-Peter Clausen {
735851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
736851a54efSLars-Peter Clausen 	struct v4l2_mbus_framefmt *framefmt;
737e0ad7a9bSNiklas Söderlund 	int ret;
738851a54efSLars-Peter Clausen 
739851a54efSLars-Peter Clausen 	switch (format->format.field) {
740851a54efSLars-Peter Clausen 	case V4L2_FIELD_NONE:
7416457b626SNiklas Söderlund 		if (state->chip_info->flags & ADV7180_FLAG_I2P)
742851a54efSLars-Peter Clausen 			break;
7431771e9fbSGustavo A. R. Silva 		fallthrough;
744851a54efSLars-Peter Clausen 	default:
7456457b626SNiklas Söderlund 		format->format.field = V4L2_FIELD_ALTERNATE;
746851a54efSLars-Peter Clausen 		break;
747851a54efSLars-Peter Clausen 	}
748851a54efSLars-Peter Clausen 
749e0ad7a9bSNiklas Söderlund 	ret = adv7180_mbus_fmt(sd,  &format->format);
750e0ad7a9bSNiklas Söderlund 
751851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
752851a54efSLars-Peter Clausen 		if (state->field != format->format.field) {
753851a54efSLars-Peter Clausen 			state->field = format->format.field;
754851a54efSLars-Peter Clausen 			adv7180_set_power(state, false);
755851a54efSLars-Peter Clausen 			adv7180_set_field_mode(state);
756851a54efSLars-Peter Clausen 			adv7180_set_power(state, true);
757851a54efSLars-Peter Clausen 		}
758851a54efSLars-Peter Clausen 	} else {
7590d346d2aSTomi Valkeinen 		framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0);
760851a54efSLars-Peter Clausen 		*framefmt = format->format;
761851a54efSLars-Peter Clausen 	}
762851a54efSLars-Peter Clausen 
763e0ad7a9bSNiklas Söderlund 	return ret;
764d5d51a82SLars-Peter Clausen }
765d5d51a82SLars-Peter Clausen 
76623c72dd9SNiklas Söderlund static int adv7180_init_cfg(struct v4l2_subdev *sd,
7670d346d2aSTomi Valkeinen 			    struct v4l2_subdev_state *sd_state)
76823c72dd9SNiklas Söderlund {
76923c72dd9SNiklas Söderlund 	struct v4l2_subdev_format fmt = {
7700d346d2aSTomi Valkeinen 		.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY
77123c72dd9SNiklas Söderlund 		: V4L2_SUBDEV_FORMAT_ACTIVE,
77223c72dd9SNiklas Söderlund 	};
77323c72dd9SNiklas Söderlund 
7740d346d2aSTomi Valkeinen 	return adv7180_set_pad_format(sd, sd_state, &fmt);
77523c72dd9SNiklas Söderlund }
77623c72dd9SNiklas Söderlund 
7770c3da525SJacopo Mondi static int adv7180_get_mbus_config(struct v4l2_subdev *sd,
7780c3da525SJacopo Mondi 				   unsigned int pad,
779cccb83f7SVladimir Barinov 				   struct v4l2_mbus_config *cfg)
780cccb83f7SVladimir Barinov {
781b37135e3SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
782b37135e3SLars-Peter Clausen 
783b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
7842d95e7edSSakari Ailus 		cfg->type = V4L2_MBUS_CSI2_DPHY;
785b37135e3SLars-Peter Clausen 		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
786b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CHANNEL_0 |
787b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
788b37135e3SLars-Peter Clausen 	} else {
789cccb83f7SVladimir Barinov 		/*
790cccb83f7SVladimir Barinov 		 * The ADV7180 sensor supports BT.601/656 output modes.
791cccb83f7SVladimir Barinov 		 * The BT.656 is default and not yet configurable by s/w.
792cccb83f7SVladimir Barinov 		 */
793cccb83f7SVladimir Barinov 		cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
794cccb83f7SVladimir Barinov 				 V4L2_MBUS_DATA_ACTIVE_HIGH;
795cccb83f7SVladimir Barinov 		cfg->type = V4L2_MBUS_BT656;
796b37135e3SLars-Peter Clausen 	}
797cccb83f7SVladimir Barinov 
798cccb83f7SVladimir Barinov 	return 0;
799cccb83f7SVladimir Barinov }
800cccb83f7SVladimir Barinov 
8019483a3f8STim Harvey static int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames)
8029483a3f8STim Harvey {
8039483a3f8STim Harvey 	*frames = ADV7180_NUM_OF_SKIP_FRAMES;
8049483a3f8STim Harvey 
8059483a3f8STim Harvey 	return 0;
8069483a3f8STim Harvey }
8079483a3f8STim Harvey 
808ecf37493SHans Verkuil static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect)
80964b3df92SNiklas Söderlund {
81064b3df92SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
81164b3df92SNiklas Söderlund 
81264b3df92SNiklas Söderlund 	if (state->curr_norm & V4L2_STD_525_60) {
813ecf37493SHans Verkuil 		aspect->numerator = 11;
814ecf37493SHans Verkuil 		aspect->denominator = 10;
81564b3df92SNiklas Söderlund 	} else {
816ecf37493SHans Verkuil 		aspect->numerator = 54;
817ecf37493SHans Verkuil 		aspect->denominator = 59;
81864b3df92SNiklas Söderlund 	}
81964b3df92SNiklas Söderlund 
82064b3df92SNiklas Söderlund 	return 0;
82164b3df92SNiklas Söderlund }
82264b3df92SNiklas Söderlund 
823bae4c757SNiklas Söderlund static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
824bae4c757SNiklas Söderlund {
825bae4c757SNiklas Söderlund 	*norm = V4L2_STD_ALL;
826bae4c757SNiklas Söderlund 	return 0;
827bae4c757SNiklas Söderlund }
828bae4c757SNiklas Söderlund 
829937feeedSHans Verkuil static int adv7180_s_stream(struct v4l2_subdev *sd, int enable)
830937feeedSHans Verkuil {
831937feeedSHans Verkuil 	struct adv7180_state *state = to_state(sd);
832937feeedSHans Verkuil 	int ret;
833937feeedSHans Verkuil 
834937feeedSHans Verkuil 	/* It's always safe to stop streaming, no need to take the lock */
835937feeedSHans Verkuil 	if (!enable) {
836937feeedSHans Verkuil 		state->streaming = enable;
837937feeedSHans Verkuil 		return 0;
838937feeedSHans Verkuil 	}
839937feeedSHans Verkuil 
840937feeedSHans Verkuil 	/* Must wait until querystd released the lock */
841937feeedSHans Verkuil 	ret = mutex_lock_interruptible(&state->mutex);
842937feeedSHans Verkuil 	if (ret)
843937feeedSHans Verkuil 		return ret;
844937feeedSHans Verkuil 	state->streaming = enable;
845937feeedSHans Verkuil 	mutex_unlock(&state->mutex);
846937feeedSHans Verkuil 	return 0;
847937feeedSHans Verkuil }
848937feeedSHans Verkuil 
849937feeedSHans Verkuil static int adv7180_subscribe_event(struct v4l2_subdev *sd,
850937feeedSHans Verkuil 				   struct v4l2_fh *fh,
851937feeedSHans Verkuil 				   struct v4l2_event_subscription *sub)
852937feeedSHans Verkuil {
853937feeedSHans Verkuil 	switch (sub->type) {
854937feeedSHans Verkuil 	case V4L2_EVENT_SOURCE_CHANGE:
855937feeedSHans Verkuil 		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
856937feeedSHans Verkuil 	case V4L2_EVENT_CTRL:
857937feeedSHans Verkuil 		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
858937feeedSHans Verkuil 	default:
859937feeedSHans Verkuil 		return -EINVAL;
860937feeedSHans Verkuil 	}
861937feeedSHans Verkuil }
862937feeedSHans Verkuil 
863cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = {
8648774bed9SLaurent Pinchart 	.s_std = adv7180_s_std,
865d0fadc86SNiklas Söderlund 	.g_std = adv7180_g_std,
86638566d28SNiklas Söderlund 	.g_frame_interval = adv7180_g_frame_interval,
867cb7a01acSMauro Carvalho Chehab 	.querystd = adv7180_querystd,
868cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7180_g_input_status,
869cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7180_s_routing,
870ecf37493SHans Verkuil 	.g_pixelaspect = adv7180_g_pixelaspect,
871bae4c757SNiklas Söderlund 	.g_tvnorms = adv7180_g_tvnorms,
872937feeedSHans Verkuil 	.s_stream = adv7180_s_stream,
873cb7a01acSMauro Carvalho Chehab };
874cb7a01acSMauro Carvalho Chehab 
875cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = {
876e246c333SLars-Peter Clausen 	.s_power = adv7180_s_power,
877937feeedSHans Verkuil 	.subscribe_event = adv7180_subscribe_event,
878937feeedSHans Verkuil 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
879cb7a01acSMauro Carvalho Chehab };
880cb7a01acSMauro Carvalho Chehab 
881d5d51a82SLars-Peter Clausen static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
88223c72dd9SNiklas Söderlund 	.init_cfg = adv7180_init_cfg,
883d5d51a82SLars-Peter Clausen 	.enum_mbus_code = adv7180_enum_mbus_code,
884d5d51a82SLars-Peter Clausen 	.set_fmt = adv7180_set_pad_format,
885d5d51a82SLars-Peter Clausen 	.get_fmt = adv7180_get_pad_format,
8860c3da525SJacopo Mondi 	.get_mbus_config = adv7180_get_mbus_config,
887d5d51a82SLars-Peter Clausen };
888d5d51a82SLars-Peter Clausen 
8899483a3f8STim Harvey static const struct v4l2_subdev_sensor_ops adv7180_sensor_ops = {
8909483a3f8STim Harvey 	.g_skip_frames = adv7180_get_skip_frames,
8919483a3f8STim Harvey };
8929483a3f8STim Harvey 
893cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = {
894cb7a01acSMauro Carvalho Chehab 	.core = &adv7180_core_ops,
895cb7a01acSMauro Carvalho Chehab 	.video = &adv7180_video_ops,
896d5d51a82SLars-Peter Clausen 	.pad = &adv7180_pad_ops,
8979483a3f8STim Harvey 	.sensor = &adv7180_sensor_ops,
898cb7a01acSMauro Carvalho Chehab };
899cb7a01acSMauro Carvalho Chehab 
9000c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid)
901cb7a01acSMauro Carvalho Chehab {
9020c25534dSLars-Peter Clausen 	struct adv7180_state *state = devid;
903cb7a01acSMauro Carvalho Chehab 	u8 isr3;
904cb7a01acSMauro Carvalho Chehab 
905cb7a01acSMauro Carvalho Chehab 	mutex_lock(&state->mutex);
9063999e5d0SLars-Peter Clausen 	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
907cb7a01acSMauro Carvalho Chehab 	/* clear */
9083999e5d0SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_ICR3, isr3);
909cb7a01acSMauro Carvalho Chehab 
910937feeedSHans Verkuil 	if (isr3 & ADV7180_IRQ3_AD_CHANGE) {
911937feeedSHans Verkuil 		static const struct v4l2_event src_ch = {
912937feeedSHans Verkuil 			.type = V4L2_EVENT_SOURCE_CHANGE,
913937feeedSHans Verkuil 			.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
914937feeedSHans Verkuil 		};
915937feeedSHans Verkuil 
916937feeedSHans Verkuil 		v4l2_subdev_notify_event(&state->sd, &src_ch);
917937feeedSHans Verkuil 	}
918cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
919cb7a01acSMauro Carvalho Chehab 
920cb7a01acSMauro Carvalho Chehab 	return IRQ_HANDLED;
921cb7a01acSMauro Carvalho Chehab }
922cb7a01acSMauro Carvalho Chehab 
923f5dde49bSLars-Peter Clausen static int adv7180_init(struct adv7180_state *state)
924f5dde49bSLars-Peter Clausen {
925f5dde49bSLars-Peter Clausen 	int ret;
926f5dde49bSLars-Peter Clausen 
927f5dde49bSLars-Peter Clausen 	/* ITU-R BT.656-4 compatible */
928f5dde49bSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
929f5dde49bSLars-Peter Clausen 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
930f5dde49bSLars-Peter Clausen 	if (ret < 0)
931f5dde49bSLars-Peter Clausen 		return ret;
932f5dde49bSLars-Peter Clausen 
933f5dde49bSLars-Peter Clausen 	/* Manually set V bit end position in NTSC mode */
934f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
935f5dde49bSLars-Peter Clausen 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
936f5dde49bSLars-Peter Clausen }
937f5dde49bSLars-Peter Clausen 
938f5dde49bSLars-Peter Clausen static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
939f5dde49bSLars-Peter Clausen {
940f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
941f5dde49bSLars-Peter Clausen 		(std << 4) | state->input);
942f5dde49bSLars-Peter Clausen }
943f5dde49bSLars-Peter Clausen 
944f5dde49bSLars-Peter Clausen static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
945f5dde49bSLars-Peter Clausen {
946f5dde49bSLars-Peter Clausen 	int ret;
947f5dde49bSLars-Peter Clausen 
948f5dde49bSLars-Peter Clausen 	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
949f5dde49bSLars-Peter Clausen 	if (ret < 0)
950f5dde49bSLars-Peter Clausen 		return ret;
951f5dde49bSLars-Peter Clausen 
952f5dde49bSLars-Peter Clausen 	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
953f5dde49bSLars-Peter Clausen 	ret |= input;
954f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
955f5dde49bSLars-Peter Clausen }
956f5dde49bSLars-Peter Clausen 
957c5ef8f8cSLars-Peter Clausen static int adv7182_init(struct adv7180_state *state)
958c5ef8f8cSLars-Peter Clausen {
959b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
960b37135e3SLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
961b37135e3SLars-Peter Clausen 			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
962b37135e3SLars-Peter Clausen 
963851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
964851a54efSLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
965851a54efSLars-Peter Clausen 			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
966851a54efSLars-Peter Clausen 
967bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2) {
968bf7dcb80SLars-Peter Clausen 		/* ADI recommended writes for improved video quality */
969bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0080, 0x51);
970bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0081, 0x51);
971bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0082, 0x68);
972bf7dcb80SLars-Peter Clausen 	}
973bf7dcb80SLars-Peter Clausen 
974c5ef8f8cSLars-Peter Clausen 	/* ADI required writes */
975b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
976ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x4e);
977ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57);
978ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0);
979b37135e3SLars-Peter Clausen 	} else {
980b37135e3SLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_V2)
981ce5d6290SSteve Longerbeam 			adv7180_write(state,
982ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
983ce5d6290SSteve Longerbeam 				      0x17);
984b37135e3SLars-Peter Clausen 		else
985ce5d6290SSteve Longerbeam 			adv7180_write(state,
986ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
987ce5d6290SSteve Longerbeam 				      0x07);
988ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x0c);
989ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0x40);
990b37135e3SLars-Peter Clausen 	}
991b37135e3SLars-Peter Clausen 
992b37135e3SLars-Peter Clausen 	adv7180_write(state, 0x0013, 0x00);
993c5ef8f8cSLars-Peter Clausen 
994c5ef8f8cSLars-Peter Clausen 	return 0;
995c5ef8f8cSLars-Peter Clausen }
996c5ef8f8cSLars-Peter Clausen 
997c5ef8f8cSLars-Peter Clausen static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
998c5ef8f8cSLars-Peter Clausen {
999c5ef8f8cSLars-Peter Clausen 	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
1000c5ef8f8cSLars-Peter Clausen }
1001c5ef8f8cSLars-Peter Clausen 
1002c5ef8f8cSLars-Peter Clausen enum adv7182_input_type {
1003c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_CVBS,
1004c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_DIFF_CVBS,
1005c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_SVIDEO,
1006c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_YPBPR,
1007c5ef8f8cSLars-Peter Clausen };
1008c5ef8f8cSLars-Peter Clausen 
1009c5ef8f8cSLars-Peter Clausen static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
1010c5ef8f8cSLars-Peter Clausen {
1011c5ef8f8cSLars-Peter Clausen 	switch (input) {
1012c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN1:
1013c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN2:
1014c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN3:
1015c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN4:
1016c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN5:
1017c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN6:
1018c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN7:
1019c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN8:
1020c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_CVBS;
1021c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
1022c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
1023c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
1024c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
1025c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_SVIDEO;
1026c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
1027c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
1028c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_YPBPR;
1029c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
1030c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
1031c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
1032c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
1033c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_DIFF_CVBS;
1034c5ef8f8cSLars-Peter Clausen 	default: /* Will never happen */
1035c5ef8f8cSLars-Peter Clausen 		return 0;
1036c5ef8f8cSLars-Peter Clausen 	}
1037c5ef8f8cSLars-Peter Clausen }
1038c5ef8f8cSLars-Peter Clausen 
1039c5ef8f8cSLars-Peter Clausen /* ADI recommended writes to registers 0x52, 0x53, 0x54 */
1040c5ef8f8cSLars-Peter Clausen static unsigned int adv7182_lbias_settings[][3] = {
1041c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
1042c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
1043c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
1044c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
1045c5ef8f8cSLars-Peter Clausen };
1046c5ef8f8cSLars-Peter Clausen 
1047bf7dcb80SLars-Peter Clausen static unsigned int adv7280_lbias_settings[][3] = {
1048bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
1049bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
1050bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
1051bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
1052bf7dcb80SLars-Peter Clausen };
1053bf7dcb80SLars-Peter Clausen 
1054c5ef8f8cSLars-Peter Clausen static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
1055c5ef8f8cSLars-Peter Clausen {
1056c5ef8f8cSLars-Peter Clausen 	enum adv7182_input_type input_type;
1057c5ef8f8cSLars-Peter Clausen 	unsigned int *lbias;
1058c5ef8f8cSLars-Peter Clausen 	unsigned int i;
1059c5ef8f8cSLars-Peter Clausen 	int ret;
1060c5ef8f8cSLars-Peter Clausen 
1061c5ef8f8cSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
1062c5ef8f8cSLars-Peter Clausen 	if (ret)
1063c5ef8f8cSLars-Peter Clausen 		return ret;
1064c5ef8f8cSLars-Peter Clausen 
1065c5ef8f8cSLars-Peter Clausen 	/* Reset clamp circuitry - ADI recommended writes */
1066ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0x00);
1067ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0xff);
1068c5ef8f8cSLars-Peter Clausen 
1069c5ef8f8cSLars-Peter Clausen 	input_type = adv7182_get_input_type(input);
1070c5ef8f8cSLars-Peter Clausen 
1071c5ef8f8cSLars-Peter Clausen 	switch (input_type) {
1072c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_CVBS:
1073c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_DIFF_CVBS:
1074c5ef8f8cSLars-Peter Clausen 		/* ADI recommends to use the SH1 filter */
1075ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x41);
1076c5ef8f8cSLars-Peter Clausen 		break;
1077c5ef8f8cSLars-Peter Clausen 	default:
1078ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x01);
1079c5ef8f8cSLars-Peter Clausen 		break;
1080c5ef8f8cSLars-Peter Clausen 	}
1081c5ef8f8cSLars-Peter Clausen 
1082bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2)
1083bf7dcb80SLars-Peter Clausen 		lbias = adv7280_lbias_settings[input_type];
1084bf7dcb80SLars-Peter Clausen 	else
1085c5ef8f8cSLars-Peter Clausen 		lbias = adv7182_lbias_settings[input_type];
1086c5ef8f8cSLars-Peter Clausen 
1087c5ef8f8cSLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
1088ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CVBS_TRIM + i, lbias[i]);
1089c5ef8f8cSLars-Peter Clausen 
1090c5ef8f8cSLars-Peter Clausen 	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
1091c5ef8f8cSLars-Peter Clausen 		/* ADI required writes to make differential CVBS work */
1092ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xa8);
1093ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0x90);
1094ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0xb0);
1095ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x08);
1096ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0xa0);
1097c5ef8f8cSLars-Peter Clausen 	} else {
1098ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xf0);
1099ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0xd0);
1100ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0x10);
1101ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x9c);
1102ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0x00);
1103c5ef8f8cSLars-Peter Clausen 	}
1104c5ef8f8cSLars-Peter Clausen 
1105c5ef8f8cSLars-Peter Clausen 	return 0;
1106c5ef8f8cSLars-Peter Clausen }
1107c5ef8f8cSLars-Peter Clausen 
1108f5dde49bSLars-Peter Clausen static const struct adv7180_chip_info adv7180_info = {
1109f5dde49bSLars-Peter Clausen 	.flags = ADV7180_FLAG_RESET_POWERED,
1110f5dde49bSLars-Peter Clausen 	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
1111f5dde49bSLars-Peter Clausen 	 * all inputs and let the card driver take care of validation
1112f5dde49bSLars-Peter Clausen 	 */
1113f5dde49bSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
1114f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN2) |
1115f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN3) |
1116f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN4) |
1117f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN5) |
1118f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN6) |
1119f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
1120f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
1121f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
1122f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1123f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
1124f5dde49bSLars-Peter Clausen 	.init = adv7180_init,
1125f5dde49bSLars-Peter Clausen 	.set_std = adv7180_set_std,
1126f5dde49bSLars-Peter Clausen 	.select_input = adv7180_select_input,
1127f5dde49bSLars-Peter Clausen };
1128f5dde49bSLars-Peter Clausen 
1129c5ef8f8cSLars-Peter Clausen static const struct adv7180_chip_info adv7182_info = {
1130c5ef8f8cSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1131c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1132c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1133c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1134c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1135c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1136c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1137c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1138c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
1139c5ef8f8cSLars-Peter Clausen 	.init = adv7182_init,
1140c5ef8f8cSLars-Peter Clausen 	.set_std = adv7182_set_std,
1141c5ef8f8cSLars-Peter Clausen 	.select_input = adv7182_select_input,
1142c5ef8f8cSLars-Peter Clausen };
1143c5ef8f8cSLars-Peter Clausen 
1144bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7280_info = {
1145851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1146bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1147bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1148bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1149bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1150bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1151bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1152bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
1153bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1154bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1155bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1156bf7dcb80SLars-Peter Clausen };
1157bf7dcb80SLars-Peter Clausen 
1158b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7280_m_info = {
1159851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1160b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1161b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1162b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1163b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1164b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1165b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1166b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1167b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1168b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1169b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1170b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1171b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1172b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1173b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
1174b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1175b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1176b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1177b37135e3SLars-Peter Clausen };
1178b37135e3SLars-Peter Clausen 
1179bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7281_info = {
1180b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1181bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1182bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1183bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1184bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1185bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1186bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1187bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1188bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1189bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1190bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1191bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1192bf7dcb80SLars-Peter Clausen };
1193bf7dcb80SLars-Peter Clausen 
1194b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_m_info = {
1195b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1196b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1197b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1198b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1199b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1200b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1201b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1202b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1203b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1204b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1205b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1206b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1207b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1208b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1209b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1210b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1211b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1212b37135e3SLars-Peter Clausen };
1213b37135e3SLars-Peter Clausen 
1214b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_ma_info = {
1215b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1216b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1217b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1218b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1219b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1220b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1221b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1222b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1223b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1224b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1225b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1226b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1227b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1228b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1229b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
1230b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1231b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1232b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
1233b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1234b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1235b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1236b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1237b37135e3SLars-Peter Clausen };
1238b37135e3SLars-Peter Clausen 
1239851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_info = {
1240851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1241851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1242851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1243851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1244851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1245851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1246851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1247851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1248851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1249851a54efSLars-Peter Clausen 	.init = adv7182_init,
1250851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1251851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1252851a54efSLars-Peter Clausen };
1253851a54efSLars-Peter Clausen 
1254851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_m_info = {
1255851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1256851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1257851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1258851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1259851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1260851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1261851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1262851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1263851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1264851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1265851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1266851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1267851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1268851a54efSLars-Peter Clausen 	.init = adv7182_init,
1269851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1270851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1271851a54efSLars-Peter Clausen };
1272851a54efSLars-Peter Clausen 
12733999e5d0SLars-Peter Clausen static int init_device(struct adv7180_state *state)
1274cb7a01acSMauro Carvalho Chehab {
1275cb7a01acSMauro Carvalho Chehab 	int ret;
1276cb7a01acSMauro Carvalho Chehab 
12773999e5d0SLars-Peter Clausen 	mutex_lock(&state->mutex);
12783999e5d0SLars-Peter Clausen 
127965d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, true);
1280*abb7c7c2SFrieder Schrempf 	adv7180_set_reset_pin(state, false);
128165d9e14aSSteve Longerbeam 
1282c18818e9SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
128316dfe72fSUlrich Hecht 	usleep_range(5000, 10000);
1284c18818e9SLars-Peter Clausen 
1285f5dde49bSLars-Peter Clausen 	ret = state->chip_info->init(state);
12863e35e33cSLars-Peter Clausen 	if (ret)
12873999e5d0SLars-Peter Clausen 		goto out_unlock;
1288cb7a01acSMauro Carvalho Chehab 
1289f5dde49bSLars-Peter Clausen 	ret = adv7180_program_std(state);
1290f5dde49bSLars-Peter Clausen 	if (ret)
12913999e5d0SLars-Peter Clausen 		goto out_unlock;
1292cb7a01acSMauro Carvalho Chehab 
1293851a54efSLars-Peter Clausen 	adv7180_set_field_mode(state);
1294851a54efSLars-Peter Clausen 
1295cb7a01acSMauro Carvalho Chehab 	/* register for interrupts */
1296cb7a01acSMauro Carvalho Chehab 	if (state->irq > 0) {
1297cb7a01acSMauro Carvalho Chehab 		/* config the Interrupt pin to be active low */
12983999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_ICONF1,
1299cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_ACTIVE_LOW |
1300cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_PSYNC_ONLY);
1301cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13023999e5d0SLars-Peter Clausen 			goto out_unlock;
1303cb7a01acSMauro Carvalho Chehab 
13043999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
1305cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13063999e5d0SLars-Peter Clausen 			goto out_unlock;
1307cb7a01acSMauro Carvalho Chehab 
13083999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
1309cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13103999e5d0SLars-Peter Clausen 			goto out_unlock;
1311cb7a01acSMauro Carvalho Chehab 
1312cb7a01acSMauro Carvalho Chehab 		/* enable AD change interrupts interrupts */
13133999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR3,
1314cb7a01acSMauro Carvalho Chehab 						ADV7180_IRQ3_AD_CHANGE);
1315cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13163999e5d0SLars-Peter Clausen 			goto out_unlock;
1317cb7a01acSMauro Carvalho Chehab 
13183999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
1319cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13203999e5d0SLars-Peter Clausen 			goto out_unlock;
1321cb7a01acSMauro Carvalho Chehab 	}
1322cb7a01acSMauro Carvalho Chehab 
13233999e5d0SLars-Peter Clausen out_unlock:
13243999e5d0SLars-Peter Clausen 	mutex_unlock(&state->mutex);
1325df065b37SAlexey Khoroshilov 
1326df065b37SAlexey Khoroshilov 	return ret;
1327cb7a01acSMauro Carvalho Chehab }
1328cb7a01acSMauro Carvalho Chehab 
13294c62e976SGreg Kroah-Hartman static int adv7180_probe(struct i2c_client *client,
1330cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1331cb7a01acSMauro Carvalho Chehab {
1332cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state;
1333cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1334cb7a01acSMauro Carvalho Chehab 	int ret;
1335cb7a01acSMauro Carvalho Chehab 
1336cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1337cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
1338cb7a01acSMauro Carvalho Chehab 		return -EIO;
1339cb7a01acSMauro Carvalho Chehab 
1340c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
13417657e064SFabio Estevam 	if (state == NULL)
13427657e064SFabio Estevam 		return -ENOMEM;
1343cb7a01acSMauro Carvalho Chehab 
13443999e5d0SLars-Peter Clausen 	state->client = client;
13456457b626SNiklas Söderlund 	state->field = V4L2_FIELD_ALTERNATE;
1346f5dde49bSLars-Peter Clausen 	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
13473999e5d0SLars-Peter Clausen 
134865d9e14aSSteve Longerbeam 	state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
134965d9e14aSSteve Longerbeam 						   GPIOD_OUT_HIGH);
135065d9e14aSSteve Longerbeam 	if (IS_ERR(state->pwdn_gpio)) {
135165d9e14aSSteve Longerbeam 		ret = PTR_ERR(state->pwdn_gpio);
135265d9e14aSSteve Longerbeam 		v4l_err(client, "request for power pin failed: %d\n", ret);
135365d9e14aSSteve Longerbeam 		return ret;
135465d9e14aSSteve Longerbeam 	}
135565d9e14aSSteve Longerbeam 
1356*abb7c7c2SFrieder Schrempf 	state->rst_gpio = devm_gpiod_get_optional(&client->dev, "reset",
1357*abb7c7c2SFrieder Schrempf 						  GPIOD_OUT_HIGH);
1358*abb7c7c2SFrieder Schrempf 	if (IS_ERR(state->rst_gpio)) {
1359*abb7c7c2SFrieder Schrempf 		ret = PTR_ERR(state->rst_gpio);
1360*abb7c7c2SFrieder Schrempf 		v4l_err(client, "request for reset pin failed: %d\n", ret);
1361*abb7c7c2SFrieder Schrempf 		return ret;
1362*abb7c7c2SFrieder Schrempf 	}
1363*abb7c7c2SFrieder Schrempf 
1364b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
136531b9754cSWolfram Sang 		state->csi_client = i2c_new_dummy_device(client->adapter,
1366b37135e3SLars-Peter Clausen 				ADV7180_DEFAULT_CSI_I2C_ADDR);
136731b9754cSWolfram Sang 		if (IS_ERR(state->csi_client))
136831b9754cSWolfram Sang 			return PTR_ERR(state->csi_client);
1369b37135e3SLars-Peter Clausen 	}
1370b37135e3SLars-Peter Clausen 
1371851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
137231b9754cSWolfram Sang 		state->vpp_client = i2c_new_dummy_device(client->adapter,
1373851a54efSLars-Peter Clausen 				ADV7180_DEFAULT_VPP_I2C_ADDR);
137431b9754cSWolfram Sang 		if (IS_ERR(state->vpp_client)) {
137531b9754cSWolfram Sang 			ret = PTR_ERR(state->vpp_client);
1376851a54efSLars-Peter Clausen 			goto err_unregister_csi_client;
1377851a54efSLars-Peter Clausen 		}
1378851a54efSLars-Peter Clausen 	}
1379851a54efSLars-Peter Clausen 
1380cb7a01acSMauro Carvalho Chehab 	state->irq = client->irq;
1381cb7a01acSMauro Carvalho Chehab 	mutex_init(&state->mutex);
1382937feeedSHans Verkuil 	state->curr_norm = V4L2_STD_NTSC;
1383f5dde49bSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
1384e246c333SLars-Peter Clausen 		state->powered = true;
1385f5dde49bSLars-Peter Clausen 	else
1386f5dde49bSLars-Peter Clausen 		state->powered = false;
1387cb7a01acSMauro Carvalho Chehab 	state->input = 0;
1388cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
1389cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
13905cc72c47SAkinobu Mita 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
1391cb7a01acSMauro Carvalho Chehab 
1392cb7a01acSMauro Carvalho Chehab 	ret = adv7180_init_controls(state);
1393cb7a01acSMauro Carvalho Chehab 	if (ret)
1394851a54efSLars-Peter Clausen 		goto err_unregister_vpp_client;
1395d5d51a82SLars-Peter Clausen 
1396d5d51a82SLars-Peter Clausen 	state->pad.flags = MEDIA_PAD_FL_SOURCE;
1397ca0fa5f0SHans Verkuil 	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
1398ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
1399cb7a01acSMauro Carvalho Chehab 	if (ret)
1400cb7a01acSMauro Carvalho Chehab 		goto err_free_ctrl;
1401fa5b7945SLars-Peter Clausen 
1402d5d51a82SLars-Peter Clausen 	ret = init_device(state);
1403d5d51a82SLars-Peter Clausen 	if (ret)
1404d5d51a82SLars-Peter Clausen 		goto err_media_entity_cleanup;
1405d5d51a82SLars-Peter Clausen 
1406fa5721d1SLars-Peter Clausen 	if (state->irq) {
1407fa5721d1SLars-Peter Clausen 		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
1408f3e991d4SLars-Peter Clausen 					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
1409f3e991d4SLars-Peter Clausen 					   KBUILD_MODNAME, state);
1410fa5721d1SLars-Peter Clausen 		if (ret)
1411d5d51a82SLars-Peter Clausen 			goto err_media_entity_cleanup;
1412fa5721d1SLars-Peter Clausen 	}
1413fa5721d1SLars-Peter Clausen 
1414fa5b7945SLars-Peter Clausen 	ret = v4l2_async_register_subdev(sd);
1415fa5b7945SLars-Peter Clausen 	if (ret)
1416fa5b7945SLars-Peter Clausen 		goto err_free_irq;
1417fa5b7945SLars-Peter Clausen 
1418b19c25f4SFabio Estevam 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1419b19c25f4SFabio Estevam 		 client->addr, client->adapter->name);
1420b19c25f4SFabio Estevam 
1421cb7a01acSMauro Carvalho Chehab 	return 0;
1422cb7a01acSMauro Carvalho Chehab 
1423fa5b7945SLars-Peter Clausen err_free_irq:
1424fa5b7945SLars-Peter Clausen 	if (state->irq > 0)
1425fa5b7945SLars-Peter Clausen 		free_irq(client->irq, state);
1426d5d51a82SLars-Peter Clausen err_media_entity_cleanup:
1427d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1428cb7a01acSMauro Carvalho Chehab err_free_ctrl:
1429cb7a01acSMauro Carvalho Chehab 	adv7180_exit_controls(state);
1430851a54efSLars-Peter Clausen err_unregister_vpp_client:
1431851a54efSLars-Peter Clausen 	i2c_unregister_device(state->vpp_client);
1432b37135e3SLars-Peter Clausen err_unregister_csi_client:
1433b37135e3SLars-Peter Clausen 	i2c_unregister_device(state->csi_client);
1434297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1435cb7a01acSMauro Carvalho Chehab 	return ret;
1436cb7a01acSMauro Carvalho Chehab }
1437cb7a01acSMauro Carvalho Chehab 
14384c62e976SGreg Kroah-Hartman static int adv7180_remove(struct i2c_client *client)
1439cb7a01acSMauro Carvalho Chehab {
1440cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1441cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1442cb7a01acSMauro Carvalho Chehab 
1443fa5b7945SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
1444fa5b7945SLars-Peter Clausen 
14450c25534dSLars-Peter Clausen 	if (state->irq > 0)
1446cb7a01acSMauro Carvalho Chehab 		free_irq(client->irq, state);
1447cb7a01acSMauro Carvalho Chehab 
1448d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1449b13f4af2SLars-Peter Clausen 	adv7180_exit_controls(state);
1450b37135e3SLars-Peter Clausen 
1451851a54efSLars-Peter Clausen 	i2c_unregister_device(state->vpp_client);
1452b37135e3SLars-Peter Clausen 	i2c_unregister_device(state->csi_client);
1453b37135e3SLars-Peter Clausen 
1454*abb7c7c2SFrieder Schrempf 	adv7180_set_reset_pin(state, true);
145565d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, false);
145665d9e14aSSteve Longerbeam 
1457297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1458b37135e3SLars-Peter Clausen 
1459cb7a01acSMauro Carvalho Chehab 	return 0;
1460cb7a01acSMauro Carvalho Chehab }
1461cb7a01acSMauro Carvalho Chehab 
1462cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = {
1463f5dde49bSLars-Peter Clausen 	{ "adv7180", (kernel_ulong_t)&adv7180_info },
1464281ddc3cSUlrich Hecht 	{ "adv7180cp", (kernel_ulong_t)&adv7180_info },
1465281ddc3cSUlrich Hecht 	{ "adv7180st", (kernel_ulong_t)&adv7180_info },
1466c5ef8f8cSLars-Peter Clausen 	{ "adv7182", (kernel_ulong_t)&adv7182_info },
1467bf7dcb80SLars-Peter Clausen 	{ "adv7280", (kernel_ulong_t)&adv7280_info },
1468b37135e3SLars-Peter Clausen 	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
1469bf7dcb80SLars-Peter Clausen 	{ "adv7281", (kernel_ulong_t)&adv7281_info },
1470b37135e3SLars-Peter Clausen 	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
1471b37135e3SLars-Peter Clausen 	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
1472851a54efSLars-Peter Clausen 	{ "adv7282", (kernel_ulong_t)&adv7282_info },
1473851a54efSLars-Peter Clausen 	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
1474cb7a01acSMauro Carvalho Chehab 	{},
1475cb7a01acSMauro Carvalho Chehab };
1476f5dde49bSLars-Peter Clausen MODULE_DEVICE_TABLE(i2c, adv7180_id);
1477cb7a01acSMauro Carvalho Chehab 
1478cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
1479cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev)
1480cb7a01acSMauro Carvalho Chehab {
148117ed3c90SKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
1482e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
1483cb7a01acSMauro Carvalho Chehab 
14843999e5d0SLars-Peter Clausen 	return adv7180_set_power(state, false);
1485cb7a01acSMauro Carvalho Chehab }
1486cb7a01acSMauro Carvalho Chehab 
1487cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev)
1488cb7a01acSMauro Carvalho Chehab {
148917ed3c90SKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
1490cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1491cb7a01acSMauro Carvalho Chehab 	int ret;
1492cb7a01acSMauro Carvalho Chehab 
14933999e5d0SLars-Peter Clausen 	ret = init_device(state);
1494cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1495cb7a01acSMauro Carvalho Chehab 		return ret;
1496c18818e9SLars-Peter Clausen 
1497c18818e9SLars-Peter Clausen 	ret = adv7180_set_power(state, state->powered);
1498c18818e9SLars-Peter Clausen 	if (ret)
1499c18818e9SLars-Peter Clausen 		return ret;
1500c18818e9SLars-Peter Clausen 
1501cb7a01acSMauro Carvalho Chehab 	return 0;
1502cb7a01acSMauro Carvalho Chehab }
1503cc1088dcSLars-Peter Clausen 
1504cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
1505cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops)
1506cc1088dcSLars-Peter Clausen 
1507cc1088dcSLars-Peter Clausen #else
1508cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL
1509cb7a01acSMauro Carvalho Chehab #endif
1510cb7a01acSMauro Carvalho Chehab 
1511250121d3SBen Dooks #ifdef CONFIG_OF
1512250121d3SBen Dooks static const struct of_device_id adv7180_of_id[] = {
1513250121d3SBen Dooks 	{ .compatible = "adi,adv7180", },
1514ce1ec5c0SUlrich Hecht 	{ .compatible = "adi,adv7180cp", },
1515ce1ec5c0SUlrich Hecht 	{ .compatible = "adi,adv7180st", },
1516bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7182", },
1517bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280", },
1518bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280-m", },
1519bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281", },
1520bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-m", },
1521bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-ma", },
1522bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282", },
1523bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282-m", },
1524250121d3SBen Dooks 	{ },
1525250121d3SBen Dooks };
1526250121d3SBen Dooks 
1527250121d3SBen Dooks MODULE_DEVICE_TABLE(of, adv7180_of_id);
1528250121d3SBen Dooks #endif
1529250121d3SBen Dooks 
1530cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = {
1531cb7a01acSMauro Carvalho Chehab 	.driver = {
1532cb7a01acSMauro Carvalho Chehab 		   .name = KBUILD_MODNAME,
1533cc1088dcSLars-Peter Clausen 		   .pm = ADV7180_PM_OPS,
1534250121d3SBen Dooks 		   .of_match_table = of_match_ptr(adv7180_of_id),
1535cb7a01acSMauro Carvalho Chehab 		   },
1536cb7a01acSMauro Carvalho Chehab 	.probe = adv7180_probe,
15374c62e976SGreg Kroah-Hartman 	.remove = adv7180_remove,
1538cb7a01acSMauro Carvalho Chehab 	.id_table = adv7180_id,
1539cb7a01acSMauro Carvalho Chehab };
1540cb7a01acSMauro Carvalho Chehab 
1541cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver);
1542cb7a01acSMauro Carvalho Chehab 
1543cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
1544cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories");
1545cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1546