xref: /openbmc/linux/drivers/media/i2c/adv7180.c (revision 851a54ef)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * adv7180.c Analog Devices ADV7180 video decoder driver
3cb7a01acSMauro Carvalho Chehab  * Copyright (c) 2009 Intel Corporation
4cccb83f7SVladimir Barinov  * Copyright (C) 2013 Cogent Embedded, Inc.
5cccb83f7SVladimir Barinov  * Copyright (C) 2013 Renesas Solutions Corp.
6cb7a01acSMauro Carvalho Chehab  *
7cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
8cb7a01acSMauro Carvalho Chehab  * it under the terms of the GNU General Public License version 2 as
9cb7a01acSMauro Carvalho Chehab  * published by the Free Software Foundation.
10cb7a01acSMauro Carvalho Chehab  *
11cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
12cb7a01acSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14cb7a01acSMauro Carvalho Chehab  * GNU General Public License for more details.
15cb7a01acSMauro Carvalho Chehab  *
16cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
17cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
18cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19cb7a01acSMauro Carvalho Chehab  */
20cb7a01acSMauro Carvalho Chehab 
21cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/errno.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
25cb7a01acSMauro Carvalho Chehab #include <linux/interrupt.h>
26cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
27cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
29cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
32cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h>
33c18818e9SLars-Peter Clausen #include <linux/delay.h>
34cb7a01acSMauro Carvalho Chehab 
35f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
36f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
37f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
38f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
39f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_J				0x4
40f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_M				0x5
41f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL60				0x6
42f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_443				0x7
43f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_BG				0x8
44f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_N				0x9
45f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M				0xa
46f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M_PED				0xb
47f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N				0xc
48f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N_PED			0xd
49f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM				0xe
50f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM_PED			0xf
51f5dde49bSLars-Peter Clausen 
523999e5d0SLars-Peter Clausen #define ADV7180_REG_INPUT_CONTROL			0x0000
53cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
54cb7a01acSMauro Carvalho Chehab 
55c5ef8f8cSLars-Peter Clausen #define ADV7182_REG_INPUT_VIDSEL			0x0002
56c5ef8f8cSLars-Peter Clausen 
573999e5d0SLars-Peter Clausen #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
58cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
59cb7a01acSMauro Carvalho Chehab 
60029d6177SLars-Peter Clausen #define ADV7180_REG_AUTODETECT_ENABLE			0x07
61cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_DEFAULT			0x7f
62cb7a01acSMauro Carvalho Chehab /* Contrast */
633999e5d0SLars-Peter Clausen #define ADV7180_REG_CON		0x0008	/*Unsigned */
64cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MIN		0
65cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_DEF		128
66cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MAX		255
67cb7a01acSMauro Carvalho Chehab /* Brightness*/
683999e5d0SLars-Peter Clausen #define ADV7180_REG_BRI		0x000a	/*Signed */
69cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MIN		-128
70cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_DEF		0
71cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MAX		127
72cb7a01acSMauro Carvalho Chehab /* Hue */
733999e5d0SLars-Peter Clausen #define ADV7180_REG_HUE		0x000b	/*Signed, inverted */
74cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MIN		-127
75cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_DEF		0
76cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MAX		128
77cb7a01acSMauro Carvalho Chehab 
783999e5d0SLars-Peter Clausen #define ADV7180_REG_CTRL		0x000e
79029d6177SLars-Peter Clausen #define ADV7180_CTRL_IRQ_SPACE		0x20
80cb7a01acSMauro Carvalho Chehab 
81029d6177SLars-Peter Clausen #define ADV7180_REG_PWR_MAN		0x0f
82cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_ON		0x04
83cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_OFF		0x24
84cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_RES		0x80
85cb7a01acSMauro Carvalho Chehab 
863999e5d0SLars-Peter Clausen #define ADV7180_REG_STATUS1		0x0010
87cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_IN_LOCK		0x01
88cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_MASK	0x70
89cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
90cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
91cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_M	0x20
92cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_60	0x30
93cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_B_G	0x40
94cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM	0x50
95cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
96cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
97cb7a01acSMauro Carvalho Chehab 
983999e5d0SLars-Peter Clausen #define ADV7180_REG_IDENT 0x0011
99cb7a01acSMauro Carvalho Chehab #define ADV7180_ID_7180 0x18
100cb7a01acSMauro Carvalho Chehab 
1013999e5d0SLars-Peter Clausen #define ADV7180_REG_ICONF1		0x0040
102cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW	0x01
103cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY	0x10
104cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
105cb7a01acSMauro Carvalho Chehab /* Saturation */
1063999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
1073999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
108cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN		0
109cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF		128
110cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX		255
111cb7a01acSMauro Carvalho Chehab 
112cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK	0x01
113cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK	0x02
1143999e5d0SLars-Peter Clausen #define ADV7180_REG_ISR1	0x0042
1153999e5d0SLars-Peter Clausen #define ADV7180_REG_ICR1	0x0043
1163999e5d0SLars-Peter Clausen #define ADV7180_REG_IMR1	0x0044
1173999e5d0SLars-Peter Clausen #define ADV7180_REG_IMR2	0x0048
118cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE	0x08
1193999e5d0SLars-Peter Clausen #define ADV7180_REG_ISR3	0x004A
1203999e5d0SLars-Peter Clausen #define ADV7180_REG_ICR3	0x004B
1213999e5d0SLars-Peter Clausen #define ADV7180_REG_IMR3	0x004C
122029d6177SLars-Peter Clausen #define ADV7180_REG_IMR4	0x50
123cb7a01acSMauro Carvalho Chehab 
1243999e5d0SLars-Peter Clausen #define ADV7180_REG_NTSC_V_BIT_END	0x00E6
125cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
126cb7a01acSMauro Carvalho Chehab 
127851a54efSLars-Peter Clausen #define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
128b37135e3SLars-Peter Clausen #define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
129b37135e3SLars-Peter Clausen 
130b37135e3SLars-Peter Clausen #define ADV7180_CSI_REG_PWRDN	0x00
131b37135e3SLars-Peter Clausen #define ADV7180_CSI_PWRDN	0x80
132b37135e3SLars-Peter Clausen 
133f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN1 0x00
134f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN2 0x01
135f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN3 0x02
136f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN4 0x03
137f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN5 0x04
138f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN6 0x05
139f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
140f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
141f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
142f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
143f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
144f5dde49bSLars-Peter Clausen 
145c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN1 0x00
146c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN2 0x01
147c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN3 0x02
148c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN4 0x03
149c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN5 0x04
150c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN6 0x05
151c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN7 0x06
152c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN8 0x07
153c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
154c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
155c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
156c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
157c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
158c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
159c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
160c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
161c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
162c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
163c5ef8f8cSLars-Peter Clausen 
164b37135e3SLars-Peter Clausen #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
165851a54efSLars-Peter Clausen #define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
166b37135e3SLars-Peter Clausen 
167f5dde49bSLars-Peter Clausen struct adv7180_state;
168f5dde49bSLars-Peter Clausen 
169f5dde49bSLars-Peter Clausen #define ADV7180_FLAG_RESET_POWERED	BIT(0)
170bf7dcb80SLars-Peter Clausen #define ADV7180_FLAG_V2			BIT(1)
171b37135e3SLars-Peter Clausen #define ADV7180_FLAG_MIPI_CSI2		BIT(2)
172851a54efSLars-Peter Clausen #define ADV7180_FLAG_I2P		BIT(3)
173f5dde49bSLars-Peter Clausen 
174f5dde49bSLars-Peter Clausen struct adv7180_chip_info {
175f5dde49bSLars-Peter Clausen 	unsigned int flags;
176f5dde49bSLars-Peter Clausen 	unsigned int valid_input_mask;
177f5dde49bSLars-Peter Clausen 	int (*set_std)(struct adv7180_state *st, unsigned int std);
178f5dde49bSLars-Peter Clausen 	int (*select_input)(struct adv7180_state *st, unsigned int input);
179f5dde49bSLars-Peter Clausen 	int (*init)(struct adv7180_state *state);
180f5dde49bSLars-Peter Clausen };
181f5dde49bSLars-Peter Clausen 
182cb7a01acSMauro Carvalho Chehab struct adv7180_state {
183cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;
184cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev	sd;
185d5d51a82SLars-Peter Clausen 	struct media_pad	pad;
186cb7a01acSMauro Carvalho Chehab 	struct mutex		mutex; /* mutual excl. when accessing chip */
187cb7a01acSMauro Carvalho Chehab 	int			irq;
188cb7a01acSMauro Carvalho Chehab 	v4l2_std_id		curr_norm;
189cb7a01acSMauro Carvalho Chehab 	bool			autodetect;
190e246c333SLars-Peter Clausen 	bool			powered;
191cb7a01acSMauro Carvalho Chehab 	u8			input;
1923999e5d0SLars-Peter Clausen 
1933999e5d0SLars-Peter Clausen 	struct i2c_client	*client;
1943999e5d0SLars-Peter Clausen 	unsigned int		register_page;
195b37135e3SLars-Peter Clausen 	struct i2c_client	*csi_client;
196851a54efSLars-Peter Clausen 	struct i2c_client	*vpp_client;
197f5dde49bSLars-Peter Clausen 	const struct adv7180_chip_info *chip_info;
198851a54efSLars-Peter Clausen 	enum v4l2_field		field;
199cb7a01acSMauro Carvalho Chehab };
200cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
201cb7a01acSMauro Carvalho Chehab 					    struct adv7180_state,	\
202cb7a01acSMauro Carvalho Chehab 					    ctrl_hdl)->sd)
203cb7a01acSMauro Carvalho Chehab 
2043999e5d0SLars-Peter Clausen static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
2053999e5d0SLars-Peter Clausen {
2063999e5d0SLars-Peter Clausen 	if (state->register_page != page) {
2073999e5d0SLars-Peter Clausen 		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
2083999e5d0SLars-Peter Clausen 			page);
2093999e5d0SLars-Peter Clausen 		state->register_page = page;
2103999e5d0SLars-Peter Clausen 	}
2113999e5d0SLars-Peter Clausen 
2123999e5d0SLars-Peter Clausen 	return 0;
2133999e5d0SLars-Peter Clausen }
2143999e5d0SLars-Peter Clausen 
2153999e5d0SLars-Peter Clausen static int adv7180_write(struct adv7180_state *state, unsigned int reg,
2163999e5d0SLars-Peter Clausen 	unsigned int value)
2173999e5d0SLars-Peter Clausen {
2183999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2193999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2203999e5d0SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
2213999e5d0SLars-Peter Clausen }
2223999e5d0SLars-Peter Clausen 
2233999e5d0SLars-Peter Clausen static int adv7180_read(struct adv7180_state *state, unsigned int reg)
2243999e5d0SLars-Peter Clausen {
2253999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2263999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2273999e5d0SLars-Peter Clausen 	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
2283999e5d0SLars-Peter Clausen }
2293999e5d0SLars-Peter Clausen 
230b37135e3SLars-Peter Clausen static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
231b37135e3SLars-Peter Clausen 	unsigned int value)
232b37135e3SLars-Peter Clausen {
233b37135e3SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
234b37135e3SLars-Peter Clausen }
235b37135e3SLars-Peter Clausen 
236f5dde49bSLars-Peter Clausen static int adv7180_set_video_standard(struct adv7180_state *state,
237f5dde49bSLars-Peter Clausen 	unsigned int std)
238f5dde49bSLars-Peter Clausen {
239f5dde49bSLars-Peter Clausen 	return state->chip_info->set_std(state, std);
240f5dde49bSLars-Peter Clausen }
2413999e5d0SLars-Peter Clausen 
242851a54efSLars-Peter Clausen static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
243851a54efSLars-Peter Clausen 	unsigned int value)
244851a54efSLars-Peter Clausen {
245851a54efSLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
246851a54efSLars-Peter Clausen }
247851a54efSLars-Peter Clausen 
248cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
249cb7a01acSMauro Carvalho Chehab {
250b294a192SVladimir Barinov 	/* in case V4L2_IN_ST_NO_SIGNAL */
251b294a192SVladimir Barinov 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
252b294a192SVladimir Barinov 		return V4L2_STD_UNKNOWN;
253b294a192SVladimir Barinov 
254cb7a01acSMauro Carvalho Chehab 	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
255cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
256cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
257cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
258cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
259cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_M:
260cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
261cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_60:
262cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_60;
263cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_B_G:
264cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
265cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM:
266cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
267cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_COMB:
268cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
269cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM_525:
270cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
271cb7a01acSMauro Carvalho Chehab 	default:
272cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
273cb7a01acSMauro Carvalho Chehab 	}
274cb7a01acSMauro Carvalho Chehab }
275cb7a01acSMauro Carvalho Chehab 
276cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std)
277cb7a01acSMauro Carvalho Chehab {
278cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
279f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL60;
280cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_NTSC_443)
281f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_443;
282cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_N)
283f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_N;
284cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_M)
285f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_M;
286cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_Nc)
287f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_COMB_N;
288cb7a01acSMauro Carvalho Chehab 
289cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_PAL)
290f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_BG;
291cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC)
292f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_M;
293cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_SECAM)
294f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_SECAM;
295cb7a01acSMauro Carvalho Chehab 
296cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
297cb7a01acSMauro Carvalho Chehab }
298cb7a01acSMauro Carvalho Chehab 
299cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1)
300cb7a01acSMauro Carvalho Chehab {
301cb7a01acSMauro Carvalho Chehab 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
302cb7a01acSMauro Carvalho Chehab 		return V4L2_IN_ST_NO_SIGNAL;
303cb7a01acSMauro Carvalho Chehab 
304cb7a01acSMauro Carvalho Chehab 	return 0;
305cb7a01acSMauro Carvalho Chehab }
306cb7a01acSMauro Carvalho Chehab 
3073999e5d0SLars-Peter Clausen static int __adv7180_status(struct adv7180_state *state, u32 *status,
308cb7a01acSMauro Carvalho Chehab 			    v4l2_std_id *std)
309cb7a01acSMauro Carvalho Chehab {
3103999e5d0SLars-Peter Clausen 	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
311cb7a01acSMauro Carvalho Chehab 
312cb7a01acSMauro Carvalho Chehab 	if (status1 < 0)
313cb7a01acSMauro Carvalho Chehab 		return status1;
314cb7a01acSMauro Carvalho Chehab 
315cb7a01acSMauro Carvalho Chehab 	if (status)
316cb7a01acSMauro Carvalho Chehab 		*status = adv7180_status_to_v4l2(status1);
317cb7a01acSMauro Carvalho Chehab 	if (std)
318cb7a01acSMauro Carvalho Chehab 		*std = adv7180_std_to_v4l2(status1);
319cb7a01acSMauro Carvalho Chehab 
320cb7a01acSMauro Carvalho Chehab 	return 0;
321cb7a01acSMauro Carvalho Chehab }
322cb7a01acSMauro Carvalho Chehab 
323cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
324cb7a01acSMauro Carvalho Chehab {
325cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7180_state, sd);
326cb7a01acSMauro Carvalho Chehab }
327cb7a01acSMauro Carvalho Chehab 
328cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
329cb7a01acSMauro Carvalho Chehab {
330cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
331cb7a01acSMauro Carvalho Chehab 	int err = mutex_lock_interruptible(&state->mutex);
332cb7a01acSMauro Carvalho Chehab 	if (err)
333cb7a01acSMauro Carvalho Chehab 		return err;
334cb7a01acSMauro Carvalho Chehab 
335cb7a01acSMauro Carvalho Chehab 	/* when we are interrupt driven we know the state */
336cb7a01acSMauro Carvalho Chehab 	if (!state->autodetect || state->irq > 0)
337cb7a01acSMauro Carvalho Chehab 		*std = state->curr_norm;
338cb7a01acSMauro Carvalho Chehab 	else
3393999e5d0SLars-Peter Clausen 		err = __adv7180_status(state, NULL, std);
340cb7a01acSMauro Carvalho Chehab 
341cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
342cb7a01acSMauro Carvalho Chehab 	return err;
343cb7a01acSMauro Carvalho Chehab }
344cb7a01acSMauro Carvalho Chehab 
345cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
346cb7a01acSMauro Carvalho Chehab 			     u32 output, u32 config)
347cb7a01acSMauro Carvalho Chehab {
348cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
349cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
350cb7a01acSMauro Carvalho Chehab 
351cb7a01acSMauro Carvalho Chehab 	if (ret)
352cb7a01acSMauro Carvalho Chehab 		return ret;
353cb7a01acSMauro Carvalho Chehab 
354f5dde49bSLars-Peter Clausen 	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
355f5dde49bSLars-Peter Clausen 		ret = -EINVAL;
356cb7a01acSMauro Carvalho Chehab 		goto out;
357f5dde49bSLars-Peter Clausen 	}
358cb7a01acSMauro Carvalho Chehab 
359f5dde49bSLars-Peter Clausen 	ret = state->chip_info->select_input(state, input);
360cb7a01acSMauro Carvalho Chehab 
361f5dde49bSLars-Peter Clausen 	if (ret == 0)
362cb7a01acSMauro Carvalho Chehab 		state->input = input;
363cb7a01acSMauro Carvalho Chehab out:
364cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
365cb7a01acSMauro Carvalho Chehab 	return ret;
366cb7a01acSMauro Carvalho Chehab }
367cb7a01acSMauro Carvalho Chehab 
368cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
369cb7a01acSMauro Carvalho Chehab {
370cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
371cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
372cb7a01acSMauro Carvalho Chehab 	if (ret)
373cb7a01acSMauro Carvalho Chehab 		return ret;
374cb7a01acSMauro Carvalho Chehab 
3753999e5d0SLars-Peter Clausen 	ret = __adv7180_status(state, status, NULL);
376cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
377cb7a01acSMauro Carvalho Chehab 	return ret;
378cb7a01acSMauro Carvalho Chehab }
379cb7a01acSMauro Carvalho Chehab 
3803e35e33cSLars-Peter Clausen static int adv7180_program_std(struct adv7180_state *state)
3813e35e33cSLars-Peter Clausen {
3823e35e33cSLars-Peter Clausen 	int ret;
3833e35e33cSLars-Peter Clausen 
3843e35e33cSLars-Peter Clausen 	if (state->autodetect) {
385f5dde49bSLars-Peter Clausen 		ret = adv7180_set_video_standard(state,
386f5dde49bSLars-Peter Clausen 			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
3873e35e33cSLars-Peter Clausen 		if (ret < 0)
3883e35e33cSLars-Peter Clausen 			return ret;
3893e35e33cSLars-Peter Clausen 
3903e35e33cSLars-Peter Clausen 		__adv7180_status(state, NULL, &state->curr_norm);
3913e35e33cSLars-Peter Clausen 	} else {
3923e35e33cSLars-Peter Clausen 		ret = v4l2_std_to_adv7180(state->curr_norm);
3933e35e33cSLars-Peter Clausen 		if (ret < 0)
3943e35e33cSLars-Peter Clausen 			return ret;
3953e35e33cSLars-Peter Clausen 
396f5dde49bSLars-Peter Clausen 		ret = adv7180_set_video_standard(state, ret);
3973e35e33cSLars-Peter Clausen 		if (ret < 0)
3983e35e33cSLars-Peter Clausen 			return ret;
3993e35e33cSLars-Peter Clausen 	}
4003e35e33cSLars-Peter Clausen 
4013e35e33cSLars-Peter Clausen 	return 0;
4023e35e33cSLars-Peter Clausen }
4033e35e33cSLars-Peter Clausen 
404cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
405cb7a01acSMauro Carvalho Chehab {
406cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
407cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
4083e35e33cSLars-Peter Clausen 
409cb7a01acSMauro Carvalho Chehab 	if (ret)
410cb7a01acSMauro Carvalho Chehab 		return ret;
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab 	/* all standards -> autodetect */
413cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_ALL) {
414cb7a01acSMauro Carvalho Chehab 		state->autodetect = true;
415cb7a01acSMauro Carvalho Chehab 	} else {
4163e35e33cSLars-Peter Clausen 		/* Make sure we can support this std */
417cb7a01acSMauro Carvalho Chehab 		ret = v4l2_std_to_adv7180(std);
418cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
419cb7a01acSMauro Carvalho Chehab 			goto out;
420cb7a01acSMauro Carvalho Chehab 
421cb7a01acSMauro Carvalho Chehab 		state->curr_norm = std;
422cb7a01acSMauro Carvalho Chehab 		state->autodetect = false;
423cb7a01acSMauro Carvalho Chehab 	}
4243e35e33cSLars-Peter Clausen 
4253e35e33cSLars-Peter Clausen 	ret = adv7180_program_std(state);
426cb7a01acSMauro Carvalho Chehab out:
427cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
428cb7a01acSMauro Carvalho Chehab 	return ret;
429cb7a01acSMauro Carvalho Chehab }
430cb7a01acSMauro Carvalho Chehab 
4313999e5d0SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state, bool on)
432e246c333SLars-Peter Clausen {
433e246c333SLars-Peter Clausen 	u8 val;
434b37135e3SLars-Peter Clausen 	int ret;
435e246c333SLars-Peter Clausen 
436e246c333SLars-Peter Clausen 	if (on)
437e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_ON;
438e246c333SLars-Peter Clausen 	else
439e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_OFF;
440e246c333SLars-Peter Clausen 
441b37135e3SLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
442b37135e3SLars-Peter Clausen 	if (ret)
443b37135e3SLars-Peter Clausen 		return ret;
444b37135e3SLars-Peter Clausen 
445b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
446b37135e3SLars-Peter Clausen 		if (on) {
447b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xDE, 0x02);
448b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD2, 0xF7);
449b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD8, 0x65);
450b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xE0, 0x09);
451b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x2C, 0x00);
452851a54efSLars-Peter Clausen 			if (state->field == V4L2_FIELD_NONE)
453851a54efSLars-Peter Clausen 				adv7180_csi_write(state, 0x1D, 0x80);
454b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x00);
455b37135e3SLars-Peter Clausen 		} else {
456b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x80);
457b37135e3SLars-Peter Clausen 		}
458b37135e3SLars-Peter Clausen 	}
459b37135e3SLars-Peter Clausen 
460b37135e3SLars-Peter Clausen 	return 0;
461e246c333SLars-Peter Clausen }
462e246c333SLars-Peter Clausen 
463e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on)
464e246c333SLars-Peter Clausen {
465e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
466e246c333SLars-Peter Clausen 	int ret;
467e246c333SLars-Peter Clausen 
468e246c333SLars-Peter Clausen 	ret = mutex_lock_interruptible(&state->mutex);
469e246c333SLars-Peter Clausen 	if (ret)
470e246c333SLars-Peter Clausen 		return ret;
471e246c333SLars-Peter Clausen 
4723999e5d0SLars-Peter Clausen 	ret = adv7180_set_power(state, on);
473e246c333SLars-Peter Clausen 	if (ret == 0)
474e246c333SLars-Peter Clausen 		state->powered = on;
475e246c333SLars-Peter Clausen 
476e246c333SLars-Peter Clausen 	mutex_unlock(&state->mutex);
477e246c333SLars-Peter Clausen 	return ret;
478e246c333SLars-Peter Clausen }
479e246c333SLars-Peter Clausen 
480cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
481cb7a01acSMauro Carvalho Chehab {
482cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
483cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
484cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
485cb7a01acSMauro Carvalho Chehab 	int val;
486cb7a01acSMauro Carvalho Chehab 
487cb7a01acSMauro Carvalho Chehab 	if (ret)
488cb7a01acSMauro Carvalho Chehab 		return ret;
489cb7a01acSMauro Carvalho Chehab 	val = ctrl->val;
490cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
491cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
4923999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_BRI, val);
493cb7a01acSMauro Carvalho Chehab 		break;
494cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
495cb7a01acSMauro Carvalho Chehab 		/*Hue is inverted according to HSL chart */
4963999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
497cb7a01acSMauro Carvalho Chehab 		break;
498cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
4993999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_CON, val);
500cb7a01acSMauro Carvalho Chehab 		break;
501cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
502cb7a01acSMauro Carvalho Chehab 		/*
503cb7a01acSMauro Carvalho Chehab 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
504cb7a01acSMauro Carvalho Chehab 		 *Let's not confuse the user, everybody understands saturation
505cb7a01acSMauro Carvalho Chehab 		 */
5063999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
507cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
508cb7a01acSMauro Carvalho Chehab 			break;
5093999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
510cb7a01acSMauro Carvalho Chehab 		break;
511cb7a01acSMauro Carvalho Chehab 	default:
512cb7a01acSMauro Carvalho Chehab 		ret = -EINVAL;
513cb7a01acSMauro Carvalho Chehab 	}
514cb7a01acSMauro Carvalho Chehab 
515cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
516cb7a01acSMauro Carvalho Chehab 	return ret;
517cb7a01acSMauro Carvalho Chehab }
518cb7a01acSMauro Carvalho Chehab 
519cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
520cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7180_s_ctrl,
521cb7a01acSMauro Carvalho Chehab };
522cb7a01acSMauro Carvalho Chehab 
523cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state)
524cb7a01acSMauro Carvalho Chehab {
525cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
526cb7a01acSMauro Carvalho Chehab 
527cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
528cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
529cb7a01acSMauro Carvalho Chehab 			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
530cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
531cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
532cb7a01acSMauro Carvalho Chehab 			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
533cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
534cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
535cb7a01acSMauro Carvalho Chehab 			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
536cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
537cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
538cb7a01acSMauro Carvalho Chehab 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
539cb7a01acSMauro Carvalho Chehab 	state->sd.ctrl_handler = &state->ctrl_hdl;
540cb7a01acSMauro Carvalho Chehab 	if (state->ctrl_hdl.error) {
541cb7a01acSMauro Carvalho Chehab 		int err = state->ctrl_hdl.error;
542cb7a01acSMauro Carvalho Chehab 
543cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->ctrl_hdl);
544cb7a01acSMauro Carvalho Chehab 		return err;
545cb7a01acSMauro Carvalho Chehab 	}
546cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
547cb7a01acSMauro Carvalho Chehab 
548cb7a01acSMauro Carvalho Chehab 	return 0;
549cb7a01acSMauro Carvalho Chehab }
550cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state)
551cb7a01acSMauro Carvalho Chehab {
552cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
553cb7a01acSMauro Carvalho Chehab }
554cb7a01acSMauro Carvalho Chehab 
555d5d51a82SLars-Peter Clausen static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
556d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_fh *fh,
557d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_mbus_code_enum *code)
558cccb83f7SVladimir Barinov {
559d5d51a82SLars-Peter Clausen 	if (code->index != 0)
560cccb83f7SVladimir Barinov 		return -EINVAL;
561cccb83f7SVladimir Barinov 
562d5d51a82SLars-Peter Clausen 	code->code = MEDIA_BUS_FMT_YUYV8_2X8;
563cccb83f7SVladimir Barinov 
564cccb83f7SVladimir Barinov 	return 0;
565cccb83f7SVladimir Barinov }
566cccb83f7SVladimir Barinov 
567cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
568cccb83f7SVladimir Barinov 			    struct v4l2_mbus_framefmt *fmt)
569cccb83f7SVladimir Barinov {
570cccb83f7SVladimir Barinov 	struct adv7180_state *state = to_state(sd);
571cccb83f7SVladimir Barinov 
572f5fe58fdSBoris BREZILLON 	fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
573cccb83f7SVladimir Barinov 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
574cccb83f7SVladimir Barinov 	fmt->width = 720;
575cccb83f7SVladimir Barinov 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
576cccb83f7SVladimir Barinov 
577cccb83f7SVladimir Barinov 	return 0;
578cccb83f7SVladimir Barinov }
579cccb83f7SVladimir Barinov 
580851a54efSLars-Peter Clausen static int adv7180_set_field_mode(struct adv7180_state *state)
581851a54efSLars-Peter Clausen {
582851a54efSLars-Peter Clausen 	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
583851a54efSLars-Peter Clausen 		return 0;
584851a54efSLars-Peter Clausen 
585851a54efSLars-Peter Clausen 	if (state->field == V4L2_FIELD_NONE) {
586851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
587851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x20);
588851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x28);
589851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x38);
590851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x30);
591851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x30);
592851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x80);
593851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x70);
594851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x50);
595851a54efSLars-Peter Clausen 		}
596851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x00);
597851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x00);
598851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x80);
599851a54efSLars-Peter Clausen 	} else {
600851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
601851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x18);
602851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x18);
603851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x30);
604851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x20);
605851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x28);
606851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x40);
607851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x58);
608851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x30);
609851a54efSLars-Peter Clausen 		}
610851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x70);
611851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x80);
612851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x00);
613851a54efSLars-Peter Clausen 	}
614851a54efSLars-Peter Clausen 
615851a54efSLars-Peter Clausen 	return 0;
616851a54efSLars-Peter Clausen }
617851a54efSLars-Peter Clausen 
618d5d51a82SLars-Peter Clausen static int adv7180_get_pad_format(struct v4l2_subdev *sd,
619d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_fh *fh,
620d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
621d5d51a82SLars-Peter Clausen {
622851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
623851a54efSLars-Peter Clausen 
624851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
625851a54efSLars-Peter Clausen 		format->format = *v4l2_subdev_get_try_format(fh, 0);
626851a54efSLars-Peter Clausen 	} else {
627851a54efSLars-Peter Clausen 		adv7180_mbus_fmt(sd, &format->format);
628851a54efSLars-Peter Clausen 		format->format.field = state->field;
629851a54efSLars-Peter Clausen 	}
630851a54efSLars-Peter Clausen 
631851a54efSLars-Peter Clausen 	return 0;
632d5d51a82SLars-Peter Clausen }
633d5d51a82SLars-Peter Clausen 
634d5d51a82SLars-Peter Clausen static int adv7180_set_pad_format(struct v4l2_subdev *sd,
635d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_fh *fh,
636d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
637d5d51a82SLars-Peter Clausen {
638851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
639851a54efSLars-Peter Clausen 	struct v4l2_mbus_framefmt *framefmt;
640851a54efSLars-Peter Clausen 
641851a54efSLars-Peter Clausen 	switch (format->format.field) {
642851a54efSLars-Peter Clausen 	case V4L2_FIELD_NONE:
643851a54efSLars-Peter Clausen 		if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
644851a54efSLars-Peter Clausen 			format->format.field = V4L2_FIELD_INTERLACED;
645851a54efSLars-Peter Clausen 		break;
646851a54efSLars-Peter Clausen 	default:
647851a54efSLars-Peter Clausen 		format->format.field = V4L2_FIELD_INTERLACED;
648851a54efSLars-Peter Clausen 		break;
649851a54efSLars-Peter Clausen 	}
650851a54efSLars-Peter Clausen 
651851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
652851a54efSLars-Peter Clausen 		framefmt = &format->format;
653851a54efSLars-Peter Clausen 		if (state->field != format->format.field) {
654851a54efSLars-Peter Clausen 			state->field = format->format.field;
655851a54efSLars-Peter Clausen 			adv7180_set_power(state, false);
656851a54efSLars-Peter Clausen 			adv7180_set_field_mode(state);
657851a54efSLars-Peter Clausen 			adv7180_set_power(state, true);
658851a54efSLars-Peter Clausen 		}
659851a54efSLars-Peter Clausen 	} else {
660851a54efSLars-Peter Clausen 		framefmt = v4l2_subdev_get_try_format(fh, 0);
661851a54efSLars-Peter Clausen 		*framefmt = format->format;
662851a54efSLars-Peter Clausen 	}
663851a54efSLars-Peter Clausen 
664851a54efSLars-Peter Clausen 	return adv7180_mbus_fmt(sd, framefmt);
665d5d51a82SLars-Peter Clausen }
666d5d51a82SLars-Peter Clausen 
667cccb83f7SVladimir Barinov static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
668cccb83f7SVladimir Barinov 				 struct v4l2_mbus_config *cfg)
669cccb83f7SVladimir Barinov {
670b37135e3SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
671b37135e3SLars-Peter Clausen 
672b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
673b37135e3SLars-Peter Clausen 		cfg->type = V4L2_MBUS_CSI2;
674b37135e3SLars-Peter Clausen 		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
675b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CHANNEL_0 |
676b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
677b37135e3SLars-Peter Clausen 	} else {
678cccb83f7SVladimir Barinov 		/*
679cccb83f7SVladimir Barinov 		 * The ADV7180 sensor supports BT.601/656 output modes.
680cccb83f7SVladimir Barinov 		 * The BT.656 is default and not yet configurable by s/w.
681cccb83f7SVladimir Barinov 		 */
682cccb83f7SVladimir Barinov 		cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
683cccb83f7SVladimir Barinov 				 V4L2_MBUS_DATA_ACTIVE_HIGH;
684cccb83f7SVladimir Barinov 		cfg->type = V4L2_MBUS_BT656;
685b37135e3SLars-Peter Clausen 	}
686cccb83f7SVladimir Barinov 
687cccb83f7SVladimir Barinov 	return 0;
688cccb83f7SVladimir Barinov }
689cccb83f7SVladimir Barinov 
690cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = {
6918774bed9SLaurent Pinchart 	.s_std = adv7180_s_std,
692cb7a01acSMauro Carvalho Chehab 	.querystd = adv7180_querystd,
693cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7180_g_input_status,
694cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7180_s_routing,
695cccb83f7SVladimir Barinov 	.g_mbus_config = adv7180_g_mbus_config,
696cb7a01acSMauro Carvalho Chehab };
697cb7a01acSMauro Carvalho Chehab 
698f5dde49bSLars-Peter Clausen 
699cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = {
700e246c333SLars-Peter Clausen 	.s_power = adv7180_s_power,
701cb7a01acSMauro Carvalho Chehab };
702cb7a01acSMauro Carvalho Chehab 
703d5d51a82SLars-Peter Clausen static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
704d5d51a82SLars-Peter Clausen 	.enum_mbus_code = adv7180_enum_mbus_code,
705d5d51a82SLars-Peter Clausen 	.set_fmt = adv7180_set_pad_format,
706d5d51a82SLars-Peter Clausen 	.get_fmt = adv7180_get_pad_format,
707d5d51a82SLars-Peter Clausen };
708d5d51a82SLars-Peter Clausen 
709cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = {
710cb7a01acSMauro Carvalho Chehab 	.core = &adv7180_core_ops,
711cb7a01acSMauro Carvalho Chehab 	.video = &adv7180_video_ops,
712d5d51a82SLars-Peter Clausen 	.pad = &adv7180_pad_ops,
713cb7a01acSMauro Carvalho Chehab };
714cb7a01acSMauro Carvalho Chehab 
7150c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid)
716cb7a01acSMauro Carvalho Chehab {
7170c25534dSLars-Peter Clausen 	struct adv7180_state *state = devid;
718cb7a01acSMauro Carvalho Chehab 	u8 isr3;
719cb7a01acSMauro Carvalho Chehab 
720cb7a01acSMauro Carvalho Chehab 	mutex_lock(&state->mutex);
7213999e5d0SLars-Peter Clausen 	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
722cb7a01acSMauro Carvalho Chehab 	/* clear */
7233999e5d0SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_ICR3, isr3);
724cb7a01acSMauro Carvalho Chehab 
725cb7a01acSMauro Carvalho Chehab 	if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
7263999e5d0SLars-Peter Clausen 		__adv7180_status(state, NULL, &state->curr_norm);
727cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
728cb7a01acSMauro Carvalho Chehab 
729cb7a01acSMauro Carvalho Chehab 	return IRQ_HANDLED;
730cb7a01acSMauro Carvalho Chehab }
731cb7a01acSMauro Carvalho Chehab 
732f5dde49bSLars-Peter Clausen static int adv7180_init(struct adv7180_state *state)
733f5dde49bSLars-Peter Clausen {
734f5dde49bSLars-Peter Clausen 	int ret;
735f5dde49bSLars-Peter Clausen 
736f5dde49bSLars-Peter Clausen 	/* ITU-R BT.656-4 compatible */
737f5dde49bSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
738f5dde49bSLars-Peter Clausen 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
739f5dde49bSLars-Peter Clausen 	if (ret < 0)
740f5dde49bSLars-Peter Clausen 		return ret;
741f5dde49bSLars-Peter Clausen 
742f5dde49bSLars-Peter Clausen 	/* Manually set V bit end position in NTSC mode */
743f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
744f5dde49bSLars-Peter Clausen 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
745f5dde49bSLars-Peter Clausen }
746f5dde49bSLars-Peter Clausen 
747f5dde49bSLars-Peter Clausen static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
748f5dde49bSLars-Peter Clausen {
749f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
750f5dde49bSLars-Peter Clausen 		(std << 4) | state->input);
751f5dde49bSLars-Peter Clausen }
752f5dde49bSLars-Peter Clausen 
753f5dde49bSLars-Peter Clausen static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
754f5dde49bSLars-Peter Clausen {
755f5dde49bSLars-Peter Clausen 	int ret;
756f5dde49bSLars-Peter Clausen 
757f5dde49bSLars-Peter Clausen 	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
758f5dde49bSLars-Peter Clausen 	if (ret < 0)
759f5dde49bSLars-Peter Clausen 		return ret;
760f5dde49bSLars-Peter Clausen 
761f5dde49bSLars-Peter Clausen 	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
762f5dde49bSLars-Peter Clausen 	ret |= input;
763f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
764f5dde49bSLars-Peter Clausen }
765f5dde49bSLars-Peter Clausen 
766c5ef8f8cSLars-Peter Clausen static int adv7182_init(struct adv7180_state *state)
767c5ef8f8cSLars-Peter Clausen {
768b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
769b37135e3SLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
770b37135e3SLars-Peter Clausen 			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
771b37135e3SLars-Peter Clausen 
772851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
773851a54efSLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
774851a54efSLars-Peter Clausen 			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
775851a54efSLars-Peter Clausen 
776bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2) {
777bf7dcb80SLars-Peter Clausen 		/* ADI recommended writes for improved video quality */
778bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0080, 0x51);
779bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0081, 0x51);
780bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0082, 0x68);
781bf7dcb80SLars-Peter Clausen 	}
782bf7dcb80SLars-Peter Clausen 
783c5ef8f8cSLars-Peter Clausen 	/* ADI required writes */
784b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
785b37135e3SLars-Peter Clausen 		adv7180_write(state, 0x0003, 0x4e);
786b37135e3SLars-Peter Clausen 		adv7180_write(state, 0x0004, 0x57);
787b37135e3SLars-Peter Clausen 		adv7180_write(state, 0x001d, 0xc0);
788b37135e3SLars-Peter Clausen 	} else {
789b37135e3SLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_V2)
790b37135e3SLars-Peter Clausen 			adv7180_write(state, 0x0004, 0x17);
791b37135e3SLars-Peter Clausen 		else
792b37135e3SLars-Peter Clausen 			adv7180_write(state, 0x0004, 0x07);
793c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x0003, 0x0c);
794c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x001d, 0x40);
795b37135e3SLars-Peter Clausen 	}
796b37135e3SLars-Peter Clausen 
797b37135e3SLars-Peter Clausen 	adv7180_write(state, 0x0013, 0x00);
798c5ef8f8cSLars-Peter Clausen 
799c5ef8f8cSLars-Peter Clausen 	return 0;
800c5ef8f8cSLars-Peter Clausen }
801c5ef8f8cSLars-Peter Clausen 
802c5ef8f8cSLars-Peter Clausen static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
803c5ef8f8cSLars-Peter Clausen {
804c5ef8f8cSLars-Peter Clausen 	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
805c5ef8f8cSLars-Peter Clausen }
806c5ef8f8cSLars-Peter Clausen 
807c5ef8f8cSLars-Peter Clausen enum adv7182_input_type {
808c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_CVBS,
809c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_DIFF_CVBS,
810c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_SVIDEO,
811c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_YPBPR,
812c5ef8f8cSLars-Peter Clausen };
813c5ef8f8cSLars-Peter Clausen 
814c5ef8f8cSLars-Peter Clausen static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
815c5ef8f8cSLars-Peter Clausen {
816c5ef8f8cSLars-Peter Clausen 	switch (input) {
817c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN1:
818c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN2:
819c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN3:
820c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN4:
821c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN5:
822c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN6:
823c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN7:
824c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN8:
825c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_CVBS;
826c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
827c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
828c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
829c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
830c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_SVIDEO;
831c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
832c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
833c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_YPBPR;
834c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
835c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
836c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
837c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
838c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_DIFF_CVBS;
839c5ef8f8cSLars-Peter Clausen 	default: /* Will never happen */
840c5ef8f8cSLars-Peter Clausen 		return 0;
841c5ef8f8cSLars-Peter Clausen 	}
842c5ef8f8cSLars-Peter Clausen }
843c5ef8f8cSLars-Peter Clausen 
844c5ef8f8cSLars-Peter Clausen /* ADI recommended writes to registers 0x52, 0x53, 0x54 */
845c5ef8f8cSLars-Peter Clausen static unsigned int adv7182_lbias_settings[][3] = {
846c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
847c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
848c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
849c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
850c5ef8f8cSLars-Peter Clausen };
851c5ef8f8cSLars-Peter Clausen 
852bf7dcb80SLars-Peter Clausen static unsigned int adv7280_lbias_settings[][3] = {
853bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
854bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
855bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
856bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
857bf7dcb80SLars-Peter Clausen };
858bf7dcb80SLars-Peter Clausen 
859c5ef8f8cSLars-Peter Clausen static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
860c5ef8f8cSLars-Peter Clausen {
861c5ef8f8cSLars-Peter Clausen 	enum adv7182_input_type input_type;
862c5ef8f8cSLars-Peter Clausen 	unsigned int *lbias;
863c5ef8f8cSLars-Peter Clausen 	unsigned int i;
864c5ef8f8cSLars-Peter Clausen 	int ret;
865c5ef8f8cSLars-Peter Clausen 
866c5ef8f8cSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
867c5ef8f8cSLars-Peter Clausen 	if (ret)
868c5ef8f8cSLars-Peter Clausen 		return ret;
869c5ef8f8cSLars-Peter Clausen 
870c5ef8f8cSLars-Peter Clausen 	/* Reset clamp circuitry - ADI recommended writes */
871c5ef8f8cSLars-Peter Clausen 	adv7180_write(state, 0x809c, 0x00);
872c5ef8f8cSLars-Peter Clausen 	adv7180_write(state, 0x809c, 0xff);
873c5ef8f8cSLars-Peter Clausen 
874c5ef8f8cSLars-Peter Clausen 	input_type = adv7182_get_input_type(input);
875c5ef8f8cSLars-Peter Clausen 
876c5ef8f8cSLars-Peter Clausen 	switch (input_type) {
877c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_CVBS:
878c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_DIFF_CVBS:
879c5ef8f8cSLars-Peter Clausen 		/* ADI recommends to use the SH1 filter */
880c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x0017, 0x41);
881c5ef8f8cSLars-Peter Clausen 		break;
882c5ef8f8cSLars-Peter Clausen 	default:
883c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x0017, 0x01);
884c5ef8f8cSLars-Peter Clausen 		break;
885c5ef8f8cSLars-Peter Clausen 	}
886c5ef8f8cSLars-Peter Clausen 
887bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2)
888bf7dcb80SLars-Peter Clausen 		lbias = adv7280_lbias_settings[input_type];
889bf7dcb80SLars-Peter Clausen 	else
890c5ef8f8cSLars-Peter Clausen 		lbias = adv7182_lbias_settings[input_type];
891c5ef8f8cSLars-Peter Clausen 
892c5ef8f8cSLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
893c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x0052 + i, lbias[i]);
894c5ef8f8cSLars-Peter Clausen 
895c5ef8f8cSLars-Peter Clausen 	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
896c5ef8f8cSLars-Peter Clausen 		/* ADI required writes to make differential CVBS work */
897c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x005f, 0xa8);
898c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x005a, 0x90);
899c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x0060, 0xb0);
900c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x80b6, 0x08);
901c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x80c0, 0xa0);
902c5ef8f8cSLars-Peter Clausen 	} else {
903c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x005f, 0xf0);
904c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x005a, 0xd0);
905c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x0060, 0x10);
906c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x80b6, 0x9c);
907c5ef8f8cSLars-Peter Clausen 		adv7180_write(state, 0x80c0, 0x00);
908c5ef8f8cSLars-Peter Clausen 	}
909c5ef8f8cSLars-Peter Clausen 
910c5ef8f8cSLars-Peter Clausen 	return 0;
911c5ef8f8cSLars-Peter Clausen }
912c5ef8f8cSLars-Peter Clausen 
913f5dde49bSLars-Peter Clausen static const struct adv7180_chip_info adv7180_info = {
914f5dde49bSLars-Peter Clausen 	.flags = ADV7180_FLAG_RESET_POWERED,
915f5dde49bSLars-Peter Clausen 	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
916f5dde49bSLars-Peter Clausen 	 * all inputs and let the card driver take care of validation
917f5dde49bSLars-Peter Clausen 	 */
918f5dde49bSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
919f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN2) |
920f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN3) |
921f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN4) |
922f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN5) |
923f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN6) |
924f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
925f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
926f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
927f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
928f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
929f5dde49bSLars-Peter Clausen 	.init = adv7180_init,
930f5dde49bSLars-Peter Clausen 	.set_std = adv7180_set_std,
931f5dde49bSLars-Peter Clausen 	.select_input = adv7180_select_input,
932f5dde49bSLars-Peter Clausen };
933f5dde49bSLars-Peter Clausen 
934c5ef8f8cSLars-Peter Clausen static const struct adv7180_chip_info adv7182_info = {
935c5ef8f8cSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
936c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
937c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
938c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
939c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
940c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
941c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
942c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
943c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
944c5ef8f8cSLars-Peter Clausen 	.init = adv7182_init,
945c5ef8f8cSLars-Peter Clausen 	.set_std = adv7182_set_std,
946c5ef8f8cSLars-Peter Clausen 	.select_input = adv7182_select_input,
947c5ef8f8cSLars-Peter Clausen };
948c5ef8f8cSLars-Peter Clausen 
949bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7280_info = {
950851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
951bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
952bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
953bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
954bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
955bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
956bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
957bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
958bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
959bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
960bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
961bf7dcb80SLars-Peter Clausen };
962bf7dcb80SLars-Peter Clausen 
963b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7280_m_info = {
964851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
965b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
966b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
967b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
968b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
969b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
970b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
971b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
972b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
973b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
974b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
975b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
976b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
977b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
978b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
979b37135e3SLars-Peter Clausen 	.init = adv7182_init,
980b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
981b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
982b37135e3SLars-Peter Clausen };
983b37135e3SLars-Peter Clausen 
984bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7281_info = {
985b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
986bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
987bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
988bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
989bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
990bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
991bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
992bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
993bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
994bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
995bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
996bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
997bf7dcb80SLars-Peter Clausen };
998bf7dcb80SLars-Peter Clausen 
999b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_m_info = {
1000b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1001b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1002b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1003b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1004b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1005b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1006b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1007b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1008b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1009b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1010b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1011b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1012b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1013b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1014b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1015b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1016b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1017b37135e3SLars-Peter Clausen };
1018b37135e3SLars-Peter Clausen 
1019b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_ma_info = {
1020b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1021b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1022b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1023b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1024b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1025b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1026b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1027b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1028b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1029b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1030b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1031b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1032b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1033b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1034b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
1035b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1036b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1037b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
1038b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1039b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1040b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1041b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1042b37135e3SLars-Peter Clausen };
1043b37135e3SLars-Peter Clausen 
1044851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_info = {
1045851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1046851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1047851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1048851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1049851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1050851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1051851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1052851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1053851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1054851a54efSLars-Peter Clausen 	.init = adv7182_init,
1055851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1056851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1057851a54efSLars-Peter Clausen };
1058851a54efSLars-Peter Clausen 
1059851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_m_info = {
1060851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1061851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1062851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1063851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1064851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1065851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1066851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1067851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1068851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1069851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1070851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1071851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1072851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1073851a54efSLars-Peter Clausen 	.init = adv7182_init,
1074851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1075851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1076851a54efSLars-Peter Clausen };
1077851a54efSLars-Peter Clausen 
10783999e5d0SLars-Peter Clausen static int init_device(struct adv7180_state *state)
1079cb7a01acSMauro Carvalho Chehab {
1080cb7a01acSMauro Carvalho Chehab 	int ret;
1081cb7a01acSMauro Carvalho Chehab 
10823999e5d0SLars-Peter Clausen 	mutex_lock(&state->mutex);
10833999e5d0SLars-Peter Clausen 
1084c18818e9SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
1085c18818e9SLars-Peter Clausen 	usleep_range(2000, 10000);
1086c18818e9SLars-Peter Clausen 
1087f5dde49bSLars-Peter Clausen 	ret = state->chip_info->init(state);
10883e35e33cSLars-Peter Clausen 	if (ret)
10893999e5d0SLars-Peter Clausen 		goto out_unlock;
1090cb7a01acSMauro Carvalho Chehab 
1091f5dde49bSLars-Peter Clausen 	ret = adv7180_program_std(state);
1092f5dde49bSLars-Peter Clausen 	if (ret)
10933999e5d0SLars-Peter Clausen 		goto out_unlock;
1094cb7a01acSMauro Carvalho Chehab 
1095851a54efSLars-Peter Clausen 	adv7180_set_field_mode(state);
1096851a54efSLars-Peter Clausen 
1097cb7a01acSMauro Carvalho Chehab 	/* register for interrupts */
1098cb7a01acSMauro Carvalho Chehab 	if (state->irq > 0) {
1099cb7a01acSMauro Carvalho Chehab 		/* config the Interrupt pin to be active low */
11003999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_ICONF1,
1101cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_ACTIVE_LOW |
1102cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_PSYNC_ONLY);
1103cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
11043999e5d0SLars-Peter Clausen 			goto out_unlock;
1105cb7a01acSMauro Carvalho Chehab 
11063999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
1107cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
11083999e5d0SLars-Peter Clausen 			goto out_unlock;
1109cb7a01acSMauro Carvalho Chehab 
11103999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
1111cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
11123999e5d0SLars-Peter Clausen 			goto out_unlock;
1113cb7a01acSMauro Carvalho Chehab 
1114cb7a01acSMauro Carvalho Chehab 		/* enable AD change interrupts interrupts */
11153999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR3,
1116cb7a01acSMauro Carvalho Chehab 						ADV7180_IRQ3_AD_CHANGE);
1117cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
11183999e5d0SLars-Peter Clausen 			goto out_unlock;
1119cb7a01acSMauro Carvalho Chehab 
11203999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
1121cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
11223999e5d0SLars-Peter Clausen 			goto out_unlock;
1123cb7a01acSMauro Carvalho Chehab 	}
1124cb7a01acSMauro Carvalho Chehab 
11253999e5d0SLars-Peter Clausen out_unlock:
11263999e5d0SLars-Peter Clausen 	mutex_unlock(&state->mutex);
1127df065b37SAlexey Khoroshilov 
1128df065b37SAlexey Khoroshilov 	return ret;
1129cb7a01acSMauro Carvalho Chehab }
1130cb7a01acSMauro Carvalho Chehab 
11314c62e976SGreg Kroah-Hartman static int adv7180_probe(struct i2c_client *client,
1132cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1133cb7a01acSMauro Carvalho Chehab {
1134cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state;
1135cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1136cb7a01acSMauro Carvalho Chehab 	int ret;
1137cb7a01acSMauro Carvalho Chehab 
1138cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1139cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
1140cb7a01acSMauro Carvalho Chehab 		return -EIO;
1141cb7a01acSMauro Carvalho Chehab 
1142cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1143cb7a01acSMauro Carvalho Chehab 		 client->addr, client->adapter->name);
1144cb7a01acSMauro Carvalho Chehab 
1145c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
11467657e064SFabio Estevam 	if (state == NULL)
11477657e064SFabio Estevam 		return -ENOMEM;
1148cb7a01acSMauro Carvalho Chehab 
11493999e5d0SLars-Peter Clausen 	state->client = client;
1150851a54efSLars-Peter Clausen 	state->field = V4L2_FIELD_INTERLACED;
1151f5dde49bSLars-Peter Clausen 	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
11523999e5d0SLars-Peter Clausen 
1153b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
1154b37135e3SLars-Peter Clausen 		state->csi_client = i2c_new_dummy(client->adapter,
1155b37135e3SLars-Peter Clausen 				ADV7180_DEFAULT_CSI_I2C_ADDR);
1156b37135e3SLars-Peter Clausen 		if (!state->csi_client)
1157b37135e3SLars-Peter Clausen 			return -ENOMEM;
1158b37135e3SLars-Peter Clausen 	}
1159b37135e3SLars-Peter Clausen 
1160851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
1161851a54efSLars-Peter Clausen 		state->vpp_client = i2c_new_dummy(client->adapter,
1162851a54efSLars-Peter Clausen 				ADV7180_DEFAULT_VPP_I2C_ADDR);
1163851a54efSLars-Peter Clausen 		if (!state->vpp_client) {
1164851a54efSLars-Peter Clausen 			ret = -ENOMEM;
1165851a54efSLars-Peter Clausen 			goto err_unregister_csi_client;
1166851a54efSLars-Peter Clausen 		}
1167851a54efSLars-Peter Clausen 	}
1168851a54efSLars-Peter Clausen 
1169cb7a01acSMauro Carvalho Chehab 	state->irq = client->irq;
1170cb7a01acSMauro Carvalho Chehab 	mutex_init(&state->mutex);
1171cb7a01acSMauro Carvalho Chehab 	state->autodetect = true;
1172f5dde49bSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
1173e246c333SLars-Peter Clausen 		state->powered = true;
1174f5dde49bSLars-Peter Clausen 	else
1175f5dde49bSLars-Peter Clausen 		state->powered = false;
1176cb7a01acSMauro Carvalho Chehab 	state->input = 0;
1177cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
1178cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
1179d5d51a82SLars-Peter Clausen 	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
1180cb7a01acSMauro Carvalho Chehab 
1181cb7a01acSMauro Carvalho Chehab 	ret = adv7180_init_controls(state);
1182cb7a01acSMauro Carvalho Chehab 	if (ret)
1183851a54efSLars-Peter Clausen 		goto err_unregister_vpp_client;
1184d5d51a82SLars-Peter Clausen 
1185d5d51a82SLars-Peter Clausen 	state->pad.flags = MEDIA_PAD_FL_SOURCE;
1186d5d51a82SLars-Peter Clausen 	sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
1187d5d51a82SLars-Peter Clausen 	ret = media_entity_init(&sd->entity, 1, &state->pad, 0);
1188cb7a01acSMauro Carvalho Chehab 	if (ret)
1189cb7a01acSMauro Carvalho Chehab 		goto err_free_ctrl;
1190fa5b7945SLars-Peter Clausen 
1191d5d51a82SLars-Peter Clausen 	ret = init_device(state);
1192d5d51a82SLars-Peter Clausen 	if (ret)
1193d5d51a82SLars-Peter Clausen 		goto err_media_entity_cleanup;
1194d5d51a82SLars-Peter Clausen 
1195fa5721d1SLars-Peter Clausen 	if (state->irq) {
1196fa5721d1SLars-Peter Clausen 		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
1197f3e991d4SLars-Peter Clausen 					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
1198f3e991d4SLars-Peter Clausen 					   KBUILD_MODNAME, state);
1199fa5721d1SLars-Peter Clausen 		if (ret)
1200d5d51a82SLars-Peter Clausen 			goto err_media_entity_cleanup;
1201fa5721d1SLars-Peter Clausen 	}
1202fa5721d1SLars-Peter Clausen 
1203fa5b7945SLars-Peter Clausen 	ret = v4l2_async_register_subdev(sd);
1204fa5b7945SLars-Peter Clausen 	if (ret)
1205fa5b7945SLars-Peter Clausen 		goto err_free_irq;
1206fa5b7945SLars-Peter Clausen 
1207cb7a01acSMauro Carvalho Chehab 	return 0;
1208cb7a01acSMauro Carvalho Chehab 
1209fa5b7945SLars-Peter Clausen err_free_irq:
1210fa5b7945SLars-Peter Clausen 	if (state->irq > 0)
1211fa5b7945SLars-Peter Clausen 		free_irq(client->irq, state);
1212d5d51a82SLars-Peter Clausen err_media_entity_cleanup:
1213d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1214cb7a01acSMauro Carvalho Chehab err_free_ctrl:
1215cb7a01acSMauro Carvalho Chehab 	adv7180_exit_controls(state);
1216851a54efSLars-Peter Clausen err_unregister_vpp_client:
1217851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
1218851a54efSLars-Peter Clausen 		i2c_unregister_device(state->vpp_client);
1219b37135e3SLars-Peter Clausen err_unregister_csi_client:
1220b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
1221b37135e3SLars-Peter Clausen 		i2c_unregister_device(state->csi_client);
1222297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1223cb7a01acSMauro Carvalho Chehab 	return ret;
1224cb7a01acSMauro Carvalho Chehab }
1225cb7a01acSMauro Carvalho Chehab 
12264c62e976SGreg Kroah-Hartman static int adv7180_remove(struct i2c_client *client)
1227cb7a01acSMauro Carvalho Chehab {
1228cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1229cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1230cb7a01acSMauro Carvalho Chehab 
1231fa5b7945SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
1232fa5b7945SLars-Peter Clausen 
12330c25534dSLars-Peter Clausen 	if (state->irq > 0)
1234cb7a01acSMauro Carvalho Chehab 		free_irq(client->irq, state);
1235cb7a01acSMauro Carvalho Chehab 
1236d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1237b13f4af2SLars-Peter Clausen 	adv7180_exit_controls(state);
1238b37135e3SLars-Peter Clausen 
1239851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
1240851a54efSLars-Peter Clausen 		i2c_unregister_device(state->vpp_client);
1241b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
1242b37135e3SLars-Peter Clausen 		i2c_unregister_device(state->csi_client);
1243b37135e3SLars-Peter Clausen 
1244297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1245b37135e3SLars-Peter Clausen 
1246cb7a01acSMauro Carvalho Chehab 	return 0;
1247cb7a01acSMauro Carvalho Chehab }
1248cb7a01acSMauro Carvalho Chehab 
1249cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = {
1250f5dde49bSLars-Peter Clausen 	{ "adv7180", (kernel_ulong_t)&adv7180_info },
1251c5ef8f8cSLars-Peter Clausen 	{ "adv7182", (kernel_ulong_t)&adv7182_info },
1252bf7dcb80SLars-Peter Clausen 	{ "adv7280", (kernel_ulong_t)&adv7280_info },
1253b37135e3SLars-Peter Clausen 	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
1254bf7dcb80SLars-Peter Clausen 	{ "adv7281", (kernel_ulong_t)&adv7281_info },
1255b37135e3SLars-Peter Clausen 	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
1256b37135e3SLars-Peter Clausen 	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
1257851a54efSLars-Peter Clausen 	{ "adv7282", (kernel_ulong_t)&adv7282_info },
1258851a54efSLars-Peter Clausen 	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
1259cb7a01acSMauro Carvalho Chehab 	{},
1260cb7a01acSMauro Carvalho Chehab };
1261f5dde49bSLars-Peter Clausen MODULE_DEVICE_TABLE(i2c, adv7180_id);
1262cb7a01acSMauro Carvalho Chehab 
1263cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
1264cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev)
1265cb7a01acSMauro Carvalho Chehab {
1266cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
1267e246c333SLars-Peter Clausen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1268e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
1269cb7a01acSMauro Carvalho Chehab 
12703999e5d0SLars-Peter Clausen 	return adv7180_set_power(state, false);
1271cb7a01acSMauro Carvalho Chehab }
1272cb7a01acSMauro Carvalho Chehab 
1273cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev)
1274cb7a01acSMauro Carvalho Chehab {
1275cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
1276cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1277cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1278cb7a01acSMauro Carvalho Chehab 	int ret;
1279cb7a01acSMauro Carvalho Chehab 
12803999e5d0SLars-Peter Clausen 	ret = init_device(state);
1281cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1282cb7a01acSMauro Carvalho Chehab 		return ret;
1283c18818e9SLars-Peter Clausen 
1284c18818e9SLars-Peter Clausen 	ret = adv7180_set_power(state, state->powered);
1285c18818e9SLars-Peter Clausen 	if (ret)
1286c18818e9SLars-Peter Clausen 		return ret;
1287c18818e9SLars-Peter Clausen 
1288cb7a01acSMauro Carvalho Chehab 	return 0;
1289cb7a01acSMauro Carvalho Chehab }
1290cc1088dcSLars-Peter Clausen 
1291cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
1292cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops)
1293cc1088dcSLars-Peter Clausen 
1294cc1088dcSLars-Peter Clausen #else
1295cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL
1296cb7a01acSMauro Carvalho Chehab #endif
1297cb7a01acSMauro Carvalho Chehab 
1298cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = {
1299cb7a01acSMauro Carvalho Chehab 	.driver = {
1300cb7a01acSMauro Carvalho Chehab 		   .owner = THIS_MODULE,
1301cb7a01acSMauro Carvalho Chehab 		   .name = KBUILD_MODNAME,
1302cc1088dcSLars-Peter Clausen 		   .pm = ADV7180_PM_OPS,
1303cb7a01acSMauro Carvalho Chehab 		   },
1304cb7a01acSMauro Carvalho Chehab 	.probe = adv7180_probe,
13054c62e976SGreg Kroah-Hartman 	.remove = adv7180_remove,
1306cb7a01acSMauro Carvalho Chehab 	.id_table = adv7180_id,
1307cb7a01acSMauro Carvalho Chehab };
1308cb7a01acSMauro Carvalho Chehab 
1309cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver);
1310cb7a01acSMauro Carvalho Chehab 
1311cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
1312cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories");
1313cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1314