xref: /openbmc/linux/drivers/media/i2c/adv7180.c (revision 6de690dd)
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>
28250121d3SBen Dooks #include <linux/of.h>
2965d9e14aSSteve Longerbeam #include <linux/gpio/consumer.h>
30cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
31937feeedSHans Verkuil #include <media/v4l2-ioctl.h>
32937feeedSHans Verkuil #include <media/v4l2-event.h>
33cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
34cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
35cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h>
36c18818e9SLars-Peter Clausen #include <linux/delay.h>
37cb7a01acSMauro Carvalho Chehab 
38f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
39f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
40f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
41f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
42f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_J				0x4
43f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_M				0x5
44f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL60				0x6
45f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_443				0x7
46f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_BG				0x8
47f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_N				0x9
48f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M				0xa
49f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M_PED				0xb
50f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N				0xc
51f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N_PED			0xd
52f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM				0xe
53f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM_PED			0xf
54f5dde49bSLars-Peter Clausen 
553999e5d0SLars-Peter Clausen #define ADV7180_REG_INPUT_CONTROL			0x0000
56cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
57cb7a01acSMauro Carvalho Chehab 
58c5ef8f8cSLars-Peter Clausen #define ADV7182_REG_INPUT_VIDSEL			0x0002
59c5ef8f8cSLars-Peter Clausen 
60ce5d6290SSteve Longerbeam #define ADV7180_REG_OUTPUT_CONTROL			0x0003
613999e5d0SLars-Peter Clausen #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
62cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
63cb7a01acSMauro Carvalho Chehab 
64ce5d6290SSteve Longerbeam #define ADV7180_REG_AUTODETECT_ENABLE			0x0007
65cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_DEFAULT			0x7f
66cb7a01acSMauro Carvalho Chehab /* Contrast */
673999e5d0SLars-Peter Clausen #define ADV7180_REG_CON		0x0008	/*Unsigned */
68cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MIN		0
69cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_DEF		128
70cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MAX		255
71cb7a01acSMauro Carvalho Chehab /* Brightness*/
723999e5d0SLars-Peter Clausen #define ADV7180_REG_BRI		0x000a	/*Signed */
73cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MIN		-128
74cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_DEF		0
75cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MAX		127
76cb7a01acSMauro Carvalho Chehab /* Hue */
773999e5d0SLars-Peter Clausen #define ADV7180_REG_HUE		0x000b	/*Signed, inverted */
78cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MIN		-127
79cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_DEF		0
80cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MAX		128
81cb7a01acSMauro Carvalho Chehab 
823999e5d0SLars-Peter Clausen #define ADV7180_REG_CTRL		0x000e
83029d6177SLars-Peter Clausen #define ADV7180_CTRL_IRQ_SPACE		0x20
84cb7a01acSMauro Carvalho Chehab 
85029d6177SLars-Peter Clausen #define ADV7180_REG_PWR_MAN		0x0f
86cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_ON		0x04
87cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_OFF		0x24
88cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_RES		0x80
89cb7a01acSMauro Carvalho Chehab 
903999e5d0SLars-Peter Clausen #define ADV7180_REG_STATUS1		0x0010
91cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_IN_LOCK		0x01
92cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_MASK	0x70
93cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
94cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
95cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_M	0x20
96cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_60	0x30
97cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_B_G	0x40
98cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM	0x50
99cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
100cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
101cb7a01acSMauro Carvalho Chehab 
1023999e5d0SLars-Peter Clausen #define ADV7180_REG_IDENT 0x0011
103cb7a01acSMauro Carvalho Chehab #define ADV7180_ID_7180 0x18
104cb7a01acSMauro Carvalho Chehab 
105ce5d6290SSteve Longerbeam #define ADV7180_REG_STATUS3		0x0013
106ce5d6290SSteve Longerbeam #define ADV7180_REG_ANALOG_CLAMP_CTL	0x0014
107ce5d6290SSteve Longerbeam #define ADV7180_REG_SHAP_FILTER_CTL_1	0x0017
108ce5d6290SSteve Longerbeam #define ADV7180_REG_CTRL_2		0x001d
109ce5d6290SSteve Longerbeam #define ADV7180_REG_VSYNC_FIELD_CTL_1	0x0031
110ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_1	0x003d
111ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_2	0x003e
112ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_3	0x003f
113ce5d6290SSteve Longerbeam #define ADV7180_REG_LOCK_CNT		0x0051
114ce5d6290SSteve Longerbeam #define ADV7180_REG_CVBS_TRIM		0x0052
115ce5d6290SSteve Longerbeam #define ADV7180_REG_CLAMP_ADJ		0x005a
116ce5d6290SSteve Longerbeam #define ADV7180_REG_RES_CIR		0x005f
117ce5d6290SSteve Longerbeam #define ADV7180_REG_DIFF_MODE		0x0060
118ce5d6290SSteve Longerbeam 
11952e37f0aSSteve Longerbeam #define ADV7180_REG_ICONF1		0x2040
120cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW	0x01
121cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY	0x10
122cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
123cb7a01acSMauro Carvalho Chehab /* Saturation */
1243999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
1253999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
126cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN		0
127cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF		128
128cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX		255
129cb7a01acSMauro Carvalho Chehab 
130cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK	0x01
131cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK	0x02
13252e37f0aSSteve Longerbeam #define ADV7180_REG_ISR1	0x2042
13352e37f0aSSteve Longerbeam #define ADV7180_REG_ICR1	0x2043
13452e37f0aSSteve Longerbeam #define ADV7180_REG_IMR1	0x2044
13552e37f0aSSteve Longerbeam #define ADV7180_REG_IMR2	0x2048
136cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE	0x08
13752e37f0aSSteve Longerbeam #define ADV7180_REG_ISR3	0x204A
13852e37f0aSSteve Longerbeam #define ADV7180_REG_ICR3	0x204B
13952e37f0aSSteve Longerbeam #define ADV7180_REG_IMR3	0x204C
14052e37f0aSSteve Longerbeam #define ADV7180_REG_IMR4	0x2050
141cb7a01acSMauro Carvalho Chehab 
1423999e5d0SLars-Peter Clausen #define ADV7180_REG_NTSC_V_BIT_END	0x00E6
143cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
144cb7a01acSMauro Carvalho Chehab 
145851a54efSLars-Peter Clausen #define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
146b37135e3SLars-Peter Clausen #define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
147b37135e3SLars-Peter Clausen 
148ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL1		0x4080
149ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL5		0x4084
15008b717c2SLars-Peter Clausen #define ADV7180_REG_FLCONTROL		0x40e0
15108b717c2SLars-Peter Clausen #define ADV7180_FLCONTROL_FL_ENABLE 0x1
15208b717c2SLars-Peter Clausen 
153ce5d6290SSteve Longerbeam #define ADV7180_REG_RST_CLAMP	0x809c
154ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ1	0x80b6
155ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ2	0x80c0
156ce5d6290SSteve Longerbeam 
157b37135e3SLars-Peter Clausen #define ADV7180_CSI_REG_PWRDN	0x00
158b37135e3SLars-Peter Clausen #define ADV7180_CSI_PWRDN	0x80
159b37135e3SLars-Peter Clausen 
160f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN1 0x00
161f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN2 0x01
162f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN3 0x02
163f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN4 0x03
164f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN5 0x04
165f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN6 0x05
166f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
167f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
168f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
169f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
170f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
171f5dde49bSLars-Peter Clausen 
172c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN1 0x00
173c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN2 0x01
174c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN3 0x02
175c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN4 0x03
176c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN5 0x04
177c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN6 0x05
178c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN7 0x06
179c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN8 0x07
180c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
181c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
182c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
183c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
184c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
185c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
186c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
187c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
188c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
189c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
190c5ef8f8cSLars-Peter Clausen 
191b37135e3SLars-Peter Clausen #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
192851a54efSLars-Peter Clausen #define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
193b37135e3SLars-Peter Clausen 
19408b717c2SLars-Peter Clausen #define V4L2_CID_ADV_FAST_SWITCH	(V4L2_CID_USER_ADV7180_BASE + 0x00)
19508b717c2SLars-Peter Clausen 
196f5dde49bSLars-Peter Clausen struct adv7180_state;
197f5dde49bSLars-Peter Clausen 
198f5dde49bSLars-Peter Clausen #define ADV7180_FLAG_RESET_POWERED	BIT(0)
199bf7dcb80SLars-Peter Clausen #define ADV7180_FLAG_V2			BIT(1)
200b37135e3SLars-Peter Clausen #define ADV7180_FLAG_MIPI_CSI2		BIT(2)
201851a54efSLars-Peter Clausen #define ADV7180_FLAG_I2P		BIT(3)
202f5dde49bSLars-Peter Clausen 
203f5dde49bSLars-Peter Clausen struct adv7180_chip_info {
204f5dde49bSLars-Peter Clausen 	unsigned int flags;
205f5dde49bSLars-Peter Clausen 	unsigned int valid_input_mask;
206f5dde49bSLars-Peter Clausen 	int (*set_std)(struct adv7180_state *st, unsigned int std);
207f5dde49bSLars-Peter Clausen 	int (*select_input)(struct adv7180_state *st, unsigned int input);
208f5dde49bSLars-Peter Clausen 	int (*init)(struct adv7180_state *state);
209f5dde49bSLars-Peter Clausen };
210f5dde49bSLars-Peter Clausen 
211cb7a01acSMauro Carvalho Chehab struct adv7180_state {
212cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;
213cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev	sd;
214d5d51a82SLars-Peter Clausen 	struct media_pad	pad;
215cb7a01acSMauro Carvalho Chehab 	struct mutex		mutex; /* mutual excl. when accessing chip */
216cb7a01acSMauro Carvalho Chehab 	int			irq;
21765d9e14aSSteve Longerbeam 	struct gpio_desc	*pwdn_gpio;
218cb7a01acSMauro Carvalho Chehab 	v4l2_std_id		curr_norm;
219e246c333SLars-Peter Clausen 	bool			powered;
220937feeedSHans Verkuil 	bool			streaming;
221cb7a01acSMauro Carvalho Chehab 	u8			input;
2223999e5d0SLars-Peter Clausen 
2233999e5d0SLars-Peter Clausen 	struct i2c_client	*client;
2243999e5d0SLars-Peter Clausen 	unsigned int		register_page;
225b37135e3SLars-Peter Clausen 	struct i2c_client	*csi_client;
226851a54efSLars-Peter Clausen 	struct i2c_client	*vpp_client;
227f5dde49bSLars-Peter Clausen 	const struct adv7180_chip_info *chip_info;
228851a54efSLars-Peter Clausen 	enum v4l2_field		field;
229cb7a01acSMauro Carvalho Chehab };
230cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
231cb7a01acSMauro Carvalho Chehab 					    struct adv7180_state,	\
232cb7a01acSMauro Carvalho Chehab 					    ctrl_hdl)->sd)
233cb7a01acSMauro Carvalho Chehab 
2343999e5d0SLars-Peter Clausen static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
2353999e5d0SLars-Peter Clausen {
2363999e5d0SLars-Peter Clausen 	if (state->register_page != page) {
2373999e5d0SLars-Peter Clausen 		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
2383999e5d0SLars-Peter Clausen 			page);
2393999e5d0SLars-Peter Clausen 		state->register_page = page;
2403999e5d0SLars-Peter Clausen 	}
2413999e5d0SLars-Peter Clausen 
2423999e5d0SLars-Peter Clausen 	return 0;
2433999e5d0SLars-Peter Clausen }
2443999e5d0SLars-Peter Clausen 
2453999e5d0SLars-Peter Clausen static int adv7180_write(struct adv7180_state *state, unsigned int reg,
2463999e5d0SLars-Peter Clausen 	unsigned int value)
2473999e5d0SLars-Peter Clausen {
2483999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2493999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2503999e5d0SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
2513999e5d0SLars-Peter Clausen }
2523999e5d0SLars-Peter Clausen 
2533999e5d0SLars-Peter Clausen static int adv7180_read(struct adv7180_state *state, unsigned int reg)
2543999e5d0SLars-Peter Clausen {
2553999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2563999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2573999e5d0SLars-Peter Clausen 	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
2583999e5d0SLars-Peter Clausen }
2593999e5d0SLars-Peter Clausen 
260b37135e3SLars-Peter Clausen static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
261b37135e3SLars-Peter Clausen 	unsigned int value)
262b37135e3SLars-Peter Clausen {
263b37135e3SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
264b37135e3SLars-Peter Clausen }
265b37135e3SLars-Peter Clausen 
266f5dde49bSLars-Peter Clausen static int adv7180_set_video_standard(struct adv7180_state *state,
267f5dde49bSLars-Peter Clausen 	unsigned int std)
268f5dde49bSLars-Peter Clausen {
269f5dde49bSLars-Peter Clausen 	return state->chip_info->set_std(state, std);
270f5dde49bSLars-Peter Clausen }
2713999e5d0SLars-Peter Clausen 
272851a54efSLars-Peter Clausen static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
273851a54efSLars-Peter Clausen 	unsigned int value)
274851a54efSLars-Peter Clausen {
275851a54efSLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
276851a54efSLars-Peter Clausen }
277851a54efSLars-Peter Clausen 
278cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
279cb7a01acSMauro Carvalho Chehab {
280b294a192SVladimir Barinov 	/* in case V4L2_IN_ST_NO_SIGNAL */
281b294a192SVladimir Barinov 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
282b294a192SVladimir Barinov 		return V4L2_STD_UNKNOWN;
283b294a192SVladimir Barinov 
284cb7a01acSMauro Carvalho Chehab 	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
285cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
286cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
287cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
288cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
289cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_M:
290cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
291cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_60:
292cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_60;
293cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_B_G:
294cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
295cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM:
296cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
297cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_COMB:
298cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
299cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM_525:
300cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
301cb7a01acSMauro Carvalho Chehab 	default:
302cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
303cb7a01acSMauro Carvalho Chehab 	}
304cb7a01acSMauro Carvalho Chehab }
305cb7a01acSMauro Carvalho Chehab 
306cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std)
307cb7a01acSMauro Carvalho Chehab {
308cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
309f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL60;
310cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_NTSC_443)
311f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_443;
312cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_N)
313f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_N;
314cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_M)
315f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_M;
316cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_Nc)
317f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_COMB_N;
318cb7a01acSMauro Carvalho Chehab 
319cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_PAL)
320f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_BG;
321cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC)
322f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_M;
323cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_SECAM)
324f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_SECAM;
325cb7a01acSMauro Carvalho Chehab 
326cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
327cb7a01acSMauro Carvalho Chehab }
328cb7a01acSMauro Carvalho Chehab 
329cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1)
330cb7a01acSMauro Carvalho Chehab {
331cb7a01acSMauro Carvalho Chehab 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
332cb7a01acSMauro Carvalho Chehab 		return V4L2_IN_ST_NO_SIGNAL;
333cb7a01acSMauro Carvalho Chehab 
334cb7a01acSMauro Carvalho Chehab 	return 0;
335cb7a01acSMauro Carvalho Chehab }
336cb7a01acSMauro Carvalho Chehab 
3373999e5d0SLars-Peter Clausen static int __adv7180_status(struct adv7180_state *state, u32 *status,
338cb7a01acSMauro Carvalho Chehab 			    v4l2_std_id *std)
339cb7a01acSMauro Carvalho Chehab {
3403999e5d0SLars-Peter Clausen 	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab 	if (status1 < 0)
343cb7a01acSMauro Carvalho Chehab 		return status1;
344cb7a01acSMauro Carvalho Chehab 
345cb7a01acSMauro Carvalho Chehab 	if (status)
346cb7a01acSMauro Carvalho Chehab 		*status = adv7180_status_to_v4l2(status1);
347cb7a01acSMauro Carvalho Chehab 	if (std)
348cb7a01acSMauro Carvalho Chehab 		*std = adv7180_std_to_v4l2(status1);
349cb7a01acSMauro Carvalho Chehab 
350cb7a01acSMauro Carvalho Chehab 	return 0;
351cb7a01acSMauro Carvalho Chehab }
352cb7a01acSMauro Carvalho Chehab 
353cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
354cb7a01acSMauro Carvalho Chehab {
355cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7180_state, sd);
356cb7a01acSMauro Carvalho Chehab }
357cb7a01acSMauro Carvalho Chehab 
358cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
359cb7a01acSMauro Carvalho Chehab {
360cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
361cb7a01acSMauro Carvalho Chehab 	int err = mutex_lock_interruptible(&state->mutex);
362cb7a01acSMauro Carvalho Chehab 	if (err)
363cb7a01acSMauro Carvalho Chehab 		return err;
364cb7a01acSMauro Carvalho Chehab 
365937feeedSHans Verkuil 	if (state->streaming) {
366937feeedSHans Verkuil 		err = -EBUSY;
367937feeedSHans Verkuil 		goto unlock;
368937feeedSHans Verkuil 	}
369cb7a01acSMauro Carvalho Chehab 
370937feeedSHans Verkuil 	err = adv7180_set_video_standard(state,
371937feeedSHans Verkuil 			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
372937feeedSHans Verkuil 	if (err)
373937feeedSHans Verkuil 		goto unlock;
374937feeedSHans Verkuil 
375937feeedSHans Verkuil 	msleep(100);
376937feeedSHans Verkuil 	__adv7180_status(state, NULL, std);
377937feeedSHans Verkuil 
378937feeedSHans Verkuil 	err = v4l2_std_to_adv7180(state->curr_norm);
379937feeedSHans Verkuil 	if (err < 0)
380937feeedSHans Verkuil 		goto unlock;
381937feeedSHans Verkuil 
382937feeedSHans Verkuil 	err = adv7180_set_video_standard(state, err);
383937feeedSHans Verkuil 
384937feeedSHans Verkuil unlock:
385cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
386cb7a01acSMauro Carvalho Chehab 	return err;
387cb7a01acSMauro Carvalho Chehab }
388cb7a01acSMauro Carvalho Chehab 
389cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
390cb7a01acSMauro Carvalho Chehab 			     u32 output, u32 config)
391cb7a01acSMauro Carvalho Chehab {
392cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
393cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
394cb7a01acSMauro Carvalho Chehab 
395cb7a01acSMauro Carvalho Chehab 	if (ret)
396cb7a01acSMauro Carvalho Chehab 		return ret;
397cb7a01acSMauro Carvalho Chehab 
398f5dde49bSLars-Peter Clausen 	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
399f5dde49bSLars-Peter Clausen 		ret = -EINVAL;
400cb7a01acSMauro Carvalho Chehab 		goto out;
401f5dde49bSLars-Peter Clausen 	}
402cb7a01acSMauro Carvalho Chehab 
403f5dde49bSLars-Peter Clausen 	ret = state->chip_info->select_input(state, input);
404cb7a01acSMauro Carvalho Chehab 
405f5dde49bSLars-Peter Clausen 	if (ret == 0)
406cb7a01acSMauro Carvalho Chehab 		state->input = input;
407cb7a01acSMauro Carvalho Chehab out:
408cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
409cb7a01acSMauro Carvalho Chehab 	return ret;
410cb7a01acSMauro Carvalho Chehab }
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
413cb7a01acSMauro Carvalho Chehab {
414cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
415cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
416cb7a01acSMauro Carvalho Chehab 	if (ret)
417cb7a01acSMauro Carvalho Chehab 		return ret;
418cb7a01acSMauro Carvalho Chehab 
4193999e5d0SLars-Peter Clausen 	ret = __adv7180_status(state, status, NULL);
420cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
421cb7a01acSMauro Carvalho Chehab 	return ret;
422cb7a01acSMauro Carvalho Chehab }
423cb7a01acSMauro Carvalho Chehab 
4243e35e33cSLars-Peter Clausen static int adv7180_program_std(struct adv7180_state *state)
4253e35e33cSLars-Peter Clausen {
4263e35e33cSLars-Peter Clausen 	int ret;
4273e35e33cSLars-Peter Clausen 
4283e35e33cSLars-Peter Clausen 	ret = v4l2_std_to_adv7180(state->curr_norm);
4293e35e33cSLars-Peter Clausen 	if (ret < 0)
4303e35e33cSLars-Peter Clausen 		return ret;
4313e35e33cSLars-Peter Clausen 
432f5dde49bSLars-Peter Clausen 	ret = adv7180_set_video_standard(state, ret);
4333e35e33cSLars-Peter Clausen 	if (ret < 0)
4343e35e33cSLars-Peter Clausen 		return ret;
4353e35e33cSLars-Peter Clausen 	return 0;
4363e35e33cSLars-Peter Clausen }
4373e35e33cSLars-Peter Clausen 
438cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
439cb7a01acSMauro Carvalho Chehab {
440cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
441cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
4423e35e33cSLars-Peter Clausen 
443cb7a01acSMauro Carvalho Chehab 	if (ret)
444cb7a01acSMauro Carvalho Chehab 		return ret;
445cb7a01acSMauro Carvalho Chehab 
4463e35e33cSLars-Peter Clausen 	/* Make sure we can support this std */
447cb7a01acSMauro Carvalho Chehab 	ret = v4l2_std_to_adv7180(std);
448cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
449cb7a01acSMauro Carvalho Chehab 		goto out;
450cb7a01acSMauro Carvalho Chehab 
451cb7a01acSMauro Carvalho Chehab 	state->curr_norm = std;
4523e35e33cSLars-Peter Clausen 
4533e35e33cSLars-Peter Clausen 	ret = adv7180_program_std(state);
454cb7a01acSMauro Carvalho Chehab out:
455cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
456cb7a01acSMauro Carvalho Chehab 	return ret;
457cb7a01acSMauro Carvalho Chehab }
458cb7a01acSMauro Carvalho Chehab 
459d0fadc86SNiklas Söderlund static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
460d0fadc86SNiklas Söderlund {
461d0fadc86SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
462d0fadc86SNiklas Söderlund 
463d0fadc86SNiklas Söderlund 	*norm = state->curr_norm;
464d0fadc86SNiklas Söderlund 
465d0fadc86SNiklas Söderlund 	return 0;
466d0fadc86SNiklas Söderlund }
467d0fadc86SNiklas Söderlund 
46865d9e14aSSteve Longerbeam static void adv7180_set_power_pin(struct adv7180_state *state, bool on)
46965d9e14aSSteve Longerbeam {
47065d9e14aSSteve Longerbeam 	if (!state->pwdn_gpio)
47165d9e14aSSteve Longerbeam 		return;
47265d9e14aSSteve Longerbeam 
47365d9e14aSSteve Longerbeam 	if (on) {
47465d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 0);
47565d9e14aSSteve Longerbeam 		usleep_range(5000, 10000);
47665d9e14aSSteve Longerbeam 	} else {
47765d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 1);
47865d9e14aSSteve Longerbeam 	}
47965d9e14aSSteve Longerbeam }
48065d9e14aSSteve Longerbeam 
4813999e5d0SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state, bool on)
482e246c333SLars-Peter Clausen {
483e246c333SLars-Peter Clausen 	u8 val;
484b37135e3SLars-Peter Clausen 	int ret;
485e246c333SLars-Peter Clausen 
486e246c333SLars-Peter Clausen 	if (on)
487e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_ON;
488e246c333SLars-Peter Clausen 	else
489e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_OFF;
490e246c333SLars-Peter Clausen 
491b37135e3SLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
492b37135e3SLars-Peter Clausen 	if (ret)
493b37135e3SLars-Peter Clausen 		return ret;
494b37135e3SLars-Peter Clausen 
495b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
496b37135e3SLars-Peter Clausen 		if (on) {
497b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xDE, 0x02);
498b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD2, 0xF7);
499b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD8, 0x65);
500b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xE0, 0x09);
501b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x2C, 0x00);
502851a54efSLars-Peter Clausen 			if (state->field == V4L2_FIELD_NONE)
503851a54efSLars-Peter Clausen 				adv7180_csi_write(state, 0x1D, 0x80);
504b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x00);
505b37135e3SLars-Peter Clausen 		} else {
506b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x80);
507b37135e3SLars-Peter Clausen 		}
508b37135e3SLars-Peter Clausen 	}
509b37135e3SLars-Peter Clausen 
510b37135e3SLars-Peter Clausen 	return 0;
511e246c333SLars-Peter Clausen }
512e246c333SLars-Peter Clausen 
513e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on)
514e246c333SLars-Peter Clausen {
515e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
516e246c333SLars-Peter Clausen 	int ret;
517e246c333SLars-Peter Clausen 
518e246c333SLars-Peter Clausen 	ret = mutex_lock_interruptible(&state->mutex);
519e246c333SLars-Peter Clausen 	if (ret)
520e246c333SLars-Peter Clausen 		return ret;
521e246c333SLars-Peter Clausen 
5223999e5d0SLars-Peter Clausen 	ret = adv7180_set_power(state, on);
523e246c333SLars-Peter Clausen 	if (ret == 0)
524e246c333SLars-Peter Clausen 		state->powered = on;
525e246c333SLars-Peter Clausen 
526e246c333SLars-Peter Clausen 	mutex_unlock(&state->mutex);
527e246c333SLars-Peter Clausen 	return ret;
528e246c333SLars-Peter Clausen }
529e246c333SLars-Peter Clausen 
530cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
531cb7a01acSMauro Carvalho Chehab {
532cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
533cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
534cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
535cb7a01acSMauro Carvalho Chehab 	int val;
536cb7a01acSMauro Carvalho Chehab 
537cb7a01acSMauro Carvalho Chehab 	if (ret)
538cb7a01acSMauro Carvalho Chehab 		return ret;
539cb7a01acSMauro Carvalho Chehab 	val = ctrl->val;
540cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
541cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
5423999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_BRI, val);
543cb7a01acSMauro Carvalho Chehab 		break;
544cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
545cb7a01acSMauro Carvalho Chehab 		/*Hue is inverted according to HSL chart */
5463999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
547cb7a01acSMauro Carvalho Chehab 		break;
548cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
5493999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_CON, val);
550cb7a01acSMauro Carvalho Chehab 		break;
551cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
552cb7a01acSMauro Carvalho Chehab 		/*
553cb7a01acSMauro Carvalho Chehab 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
554cb7a01acSMauro Carvalho Chehab 		 *Let's not confuse the user, everybody understands saturation
555cb7a01acSMauro Carvalho Chehab 		 */
5563999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
557cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
558cb7a01acSMauro Carvalho Chehab 			break;
5593999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
560cb7a01acSMauro Carvalho Chehab 		break;
56108b717c2SLars-Peter Clausen 	case V4L2_CID_ADV_FAST_SWITCH:
56208b717c2SLars-Peter Clausen 		if (ctrl->val) {
56308b717c2SLars-Peter Clausen 			/* ADI required write */
56408b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0x44);
56508b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL,
56608b717c2SLars-Peter Clausen 				ADV7180_FLCONTROL_FL_ENABLE);
56708b717c2SLars-Peter Clausen 		} else {
56808b717c2SLars-Peter Clausen 			/* ADI required write */
56908b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0xc4);
57008b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
57108b717c2SLars-Peter Clausen 		}
57208b717c2SLars-Peter Clausen 		break;
573cb7a01acSMauro Carvalho Chehab 	default:
574cb7a01acSMauro Carvalho Chehab 		ret = -EINVAL;
575cb7a01acSMauro Carvalho Chehab 	}
576cb7a01acSMauro Carvalho Chehab 
577cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
578cb7a01acSMauro Carvalho Chehab 	return ret;
579cb7a01acSMauro Carvalho Chehab }
580cb7a01acSMauro Carvalho Chehab 
581cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
582cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7180_s_ctrl,
583cb7a01acSMauro Carvalho Chehab };
584cb7a01acSMauro Carvalho Chehab 
58508b717c2SLars-Peter Clausen static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
58608b717c2SLars-Peter Clausen 	.ops = &adv7180_ctrl_ops,
58708b717c2SLars-Peter Clausen 	.id = V4L2_CID_ADV_FAST_SWITCH,
58808b717c2SLars-Peter Clausen 	.name = "Fast Switching",
58908b717c2SLars-Peter Clausen 	.type = V4L2_CTRL_TYPE_BOOLEAN,
59008b717c2SLars-Peter Clausen 	.min = 0,
59108b717c2SLars-Peter Clausen 	.max = 1,
59208b717c2SLars-Peter Clausen 	.step = 1,
59308b717c2SLars-Peter Clausen };
59408b717c2SLars-Peter Clausen 
595cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state)
596cb7a01acSMauro Carvalho Chehab {
597cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
598cb7a01acSMauro Carvalho Chehab 
599cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
600cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
601cb7a01acSMauro Carvalho Chehab 			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
602cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
603cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
604cb7a01acSMauro Carvalho Chehab 			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
605cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
606cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
607cb7a01acSMauro Carvalho Chehab 			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
608cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
609cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
610cb7a01acSMauro Carvalho Chehab 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
61108b717c2SLars-Peter Clausen 	v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
61208b717c2SLars-Peter Clausen 
613cb7a01acSMauro Carvalho Chehab 	state->sd.ctrl_handler = &state->ctrl_hdl;
614cb7a01acSMauro Carvalho Chehab 	if (state->ctrl_hdl.error) {
615cb7a01acSMauro Carvalho Chehab 		int err = state->ctrl_hdl.error;
616cb7a01acSMauro Carvalho Chehab 
617cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->ctrl_hdl);
618cb7a01acSMauro Carvalho Chehab 		return err;
619cb7a01acSMauro Carvalho Chehab 	}
620cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
621cb7a01acSMauro Carvalho Chehab 
622cb7a01acSMauro Carvalho Chehab 	return 0;
623cb7a01acSMauro Carvalho Chehab }
624cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state)
625cb7a01acSMauro Carvalho Chehab {
626cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
627cb7a01acSMauro Carvalho Chehab }
628cb7a01acSMauro Carvalho Chehab 
629d5d51a82SLars-Peter Clausen static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
630f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
631d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_mbus_code_enum *code)
632cccb83f7SVladimir Barinov {
633d5d51a82SLars-Peter Clausen 	if (code->index != 0)
634cccb83f7SVladimir Barinov 		return -EINVAL;
635cccb83f7SVladimir Barinov 
6366de690ddSNiklas Söderlund 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
637cccb83f7SVladimir Barinov 
638cccb83f7SVladimir Barinov 	return 0;
639cccb83f7SVladimir Barinov }
640cccb83f7SVladimir Barinov 
641cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
642cccb83f7SVladimir Barinov 			    struct v4l2_mbus_framefmt *fmt)
643cccb83f7SVladimir Barinov {
644cccb83f7SVladimir Barinov 	struct adv7180_state *state = to_state(sd);
645cccb83f7SVladimir Barinov 
6466de690ddSNiklas Söderlund 	fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
647cccb83f7SVladimir Barinov 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
648cccb83f7SVladimir Barinov 	fmt->width = 720;
649cccb83f7SVladimir Barinov 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
650cccb83f7SVladimir Barinov 
651cccb83f7SVladimir Barinov 	return 0;
652cccb83f7SVladimir Barinov }
653cccb83f7SVladimir Barinov 
654851a54efSLars-Peter Clausen static int adv7180_set_field_mode(struct adv7180_state *state)
655851a54efSLars-Peter Clausen {
656851a54efSLars-Peter Clausen 	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
657851a54efSLars-Peter Clausen 		return 0;
658851a54efSLars-Peter Clausen 
659851a54efSLars-Peter Clausen 	if (state->field == V4L2_FIELD_NONE) {
660851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
661851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x20);
662851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x28);
663851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x38);
664851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x30);
665851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x30);
666851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x80);
667851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x70);
668851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x50);
669851a54efSLars-Peter Clausen 		}
670851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x00);
671851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x00);
672851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x80);
673851a54efSLars-Peter Clausen 	} else {
674851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
675851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x18);
676851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x18);
677851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x30);
678851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x20);
679851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x28);
680851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x40);
681851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x58);
682851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x30);
683851a54efSLars-Peter Clausen 		}
684851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x70);
685851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x80);
686851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x00);
687851a54efSLars-Peter Clausen 	}
688851a54efSLars-Peter Clausen 
689851a54efSLars-Peter Clausen 	return 0;
690851a54efSLars-Peter Clausen }
691851a54efSLars-Peter Clausen 
692d5d51a82SLars-Peter Clausen static int adv7180_get_pad_format(struct v4l2_subdev *sd,
693f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
694d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
695d5d51a82SLars-Peter Clausen {
696851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
697851a54efSLars-Peter Clausen 
698851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
699f7234138SHans Verkuil 		format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
700851a54efSLars-Peter Clausen 	} else {
701851a54efSLars-Peter Clausen 		adv7180_mbus_fmt(sd, &format->format);
702851a54efSLars-Peter Clausen 		format->format.field = state->field;
703851a54efSLars-Peter Clausen 	}
704851a54efSLars-Peter Clausen 
705851a54efSLars-Peter Clausen 	return 0;
706d5d51a82SLars-Peter Clausen }
707d5d51a82SLars-Peter Clausen 
708d5d51a82SLars-Peter Clausen static int adv7180_set_pad_format(struct v4l2_subdev *sd,
709f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
710d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
711d5d51a82SLars-Peter Clausen {
712851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
713851a54efSLars-Peter Clausen 	struct v4l2_mbus_framefmt *framefmt;
714851a54efSLars-Peter Clausen 
715851a54efSLars-Peter Clausen 	switch (format->format.field) {
716851a54efSLars-Peter Clausen 	case V4L2_FIELD_NONE:
717851a54efSLars-Peter Clausen 		if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
718851a54efSLars-Peter Clausen 			format->format.field = V4L2_FIELD_INTERLACED;
719851a54efSLars-Peter Clausen 		break;
720851a54efSLars-Peter Clausen 	default:
721851a54efSLars-Peter Clausen 		format->format.field = V4L2_FIELD_INTERLACED;
722851a54efSLars-Peter Clausen 		break;
723851a54efSLars-Peter Clausen 	}
724851a54efSLars-Peter Clausen 
725851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
726851a54efSLars-Peter Clausen 		framefmt = &format->format;
727851a54efSLars-Peter Clausen 		if (state->field != format->format.field) {
728851a54efSLars-Peter Clausen 			state->field = format->format.field;
729851a54efSLars-Peter Clausen 			adv7180_set_power(state, false);
730851a54efSLars-Peter Clausen 			adv7180_set_field_mode(state);
731851a54efSLars-Peter Clausen 			adv7180_set_power(state, true);
732851a54efSLars-Peter Clausen 		}
733851a54efSLars-Peter Clausen 	} else {
734f7234138SHans Verkuil 		framefmt = v4l2_subdev_get_try_format(sd, cfg, 0);
735851a54efSLars-Peter Clausen 		*framefmt = format->format;
736851a54efSLars-Peter Clausen 	}
737851a54efSLars-Peter Clausen 
738851a54efSLars-Peter Clausen 	return adv7180_mbus_fmt(sd, framefmt);
739d5d51a82SLars-Peter Clausen }
740d5d51a82SLars-Peter Clausen 
741cccb83f7SVladimir Barinov static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
742cccb83f7SVladimir Barinov 				 struct v4l2_mbus_config *cfg)
743cccb83f7SVladimir Barinov {
744b37135e3SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
745b37135e3SLars-Peter Clausen 
746b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
747b37135e3SLars-Peter Clausen 		cfg->type = V4L2_MBUS_CSI2;
748b37135e3SLars-Peter Clausen 		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
749b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CHANNEL_0 |
750b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
751b37135e3SLars-Peter Clausen 	} else {
752cccb83f7SVladimir Barinov 		/*
753cccb83f7SVladimir Barinov 		 * The ADV7180 sensor supports BT.601/656 output modes.
754cccb83f7SVladimir Barinov 		 * The BT.656 is default and not yet configurable by s/w.
755cccb83f7SVladimir Barinov 		 */
756cccb83f7SVladimir Barinov 		cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
757cccb83f7SVladimir Barinov 				 V4L2_MBUS_DATA_ACTIVE_HIGH;
758cccb83f7SVladimir Barinov 		cfg->type = V4L2_MBUS_BT656;
759b37135e3SLars-Peter Clausen 	}
760cccb83f7SVladimir Barinov 
761cccb83f7SVladimir Barinov 	return 0;
762cccb83f7SVladimir Barinov }
763cccb83f7SVladimir Barinov 
764ecf37493SHans Verkuil static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect)
76564b3df92SNiklas Söderlund {
76664b3df92SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
76764b3df92SNiklas Söderlund 
76864b3df92SNiklas Söderlund 	if (state->curr_norm & V4L2_STD_525_60) {
769ecf37493SHans Verkuil 		aspect->numerator = 11;
770ecf37493SHans Verkuil 		aspect->denominator = 10;
77164b3df92SNiklas Söderlund 	} else {
772ecf37493SHans Verkuil 		aspect->numerator = 54;
773ecf37493SHans Verkuil 		aspect->denominator = 59;
77464b3df92SNiklas Söderlund 	}
77564b3df92SNiklas Söderlund 
77664b3df92SNiklas Söderlund 	return 0;
77764b3df92SNiklas Söderlund }
77864b3df92SNiklas Söderlund 
779bae4c757SNiklas Söderlund static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
780bae4c757SNiklas Söderlund {
781bae4c757SNiklas Söderlund 	*norm = V4L2_STD_ALL;
782bae4c757SNiklas Söderlund 	return 0;
783bae4c757SNiklas Söderlund }
784bae4c757SNiklas Söderlund 
785937feeedSHans Verkuil static int adv7180_s_stream(struct v4l2_subdev *sd, int enable)
786937feeedSHans Verkuil {
787937feeedSHans Verkuil 	struct adv7180_state *state = to_state(sd);
788937feeedSHans Verkuil 	int ret;
789937feeedSHans Verkuil 
790937feeedSHans Verkuil 	/* It's always safe to stop streaming, no need to take the lock */
791937feeedSHans Verkuil 	if (!enable) {
792937feeedSHans Verkuil 		state->streaming = enable;
793937feeedSHans Verkuil 		return 0;
794937feeedSHans Verkuil 	}
795937feeedSHans Verkuil 
796937feeedSHans Verkuil 	/* Must wait until querystd released the lock */
797937feeedSHans Verkuil 	ret = mutex_lock_interruptible(&state->mutex);
798937feeedSHans Verkuil 	if (ret)
799937feeedSHans Verkuil 		return ret;
800937feeedSHans Verkuil 	state->streaming = enable;
801937feeedSHans Verkuil 	mutex_unlock(&state->mutex);
802937feeedSHans Verkuil 	return 0;
803937feeedSHans Verkuil }
804937feeedSHans Verkuil 
805937feeedSHans Verkuil static int adv7180_subscribe_event(struct v4l2_subdev *sd,
806937feeedSHans Verkuil 				   struct v4l2_fh *fh,
807937feeedSHans Verkuil 				   struct v4l2_event_subscription *sub)
808937feeedSHans Verkuil {
809937feeedSHans Verkuil 	switch (sub->type) {
810937feeedSHans Verkuil 	case V4L2_EVENT_SOURCE_CHANGE:
811937feeedSHans Verkuil 		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
812937feeedSHans Verkuil 	case V4L2_EVENT_CTRL:
813937feeedSHans Verkuil 		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
814937feeedSHans Verkuil 	default:
815937feeedSHans Verkuil 		return -EINVAL;
816937feeedSHans Verkuil 	}
817937feeedSHans Verkuil }
818937feeedSHans Verkuil 
819cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = {
8208774bed9SLaurent Pinchart 	.s_std = adv7180_s_std,
821d0fadc86SNiklas Söderlund 	.g_std = adv7180_g_std,
822cb7a01acSMauro Carvalho Chehab 	.querystd = adv7180_querystd,
823cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7180_g_input_status,
824cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7180_s_routing,
825cccb83f7SVladimir Barinov 	.g_mbus_config = adv7180_g_mbus_config,
826ecf37493SHans Verkuil 	.g_pixelaspect = adv7180_g_pixelaspect,
827bae4c757SNiklas Söderlund 	.g_tvnorms = adv7180_g_tvnorms,
828937feeedSHans Verkuil 	.s_stream = adv7180_s_stream,
829cb7a01acSMauro Carvalho Chehab };
830cb7a01acSMauro Carvalho Chehab 
831cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = {
832e246c333SLars-Peter Clausen 	.s_power = adv7180_s_power,
833937feeedSHans Verkuil 	.subscribe_event = adv7180_subscribe_event,
834937feeedSHans Verkuil 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
835cb7a01acSMauro Carvalho Chehab };
836cb7a01acSMauro Carvalho Chehab 
837d5d51a82SLars-Peter Clausen static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
838d5d51a82SLars-Peter Clausen 	.enum_mbus_code = adv7180_enum_mbus_code,
839d5d51a82SLars-Peter Clausen 	.set_fmt = adv7180_set_pad_format,
840d5d51a82SLars-Peter Clausen 	.get_fmt = adv7180_get_pad_format,
841d5d51a82SLars-Peter Clausen };
842d5d51a82SLars-Peter Clausen 
843cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = {
844cb7a01acSMauro Carvalho Chehab 	.core = &adv7180_core_ops,
845cb7a01acSMauro Carvalho Chehab 	.video = &adv7180_video_ops,
846d5d51a82SLars-Peter Clausen 	.pad = &adv7180_pad_ops,
847cb7a01acSMauro Carvalho Chehab };
848cb7a01acSMauro Carvalho Chehab 
8490c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid)
850cb7a01acSMauro Carvalho Chehab {
8510c25534dSLars-Peter Clausen 	struct adv7180_state *state = devid;
852cb7a01acSMauro Carvalho Chehab 	u8 isr3;
853cb7a01acSMauro Carvalho Chehab 
854cb7a01acSMauro Carvalho Chehab 	mutex_lock(&state->mutex);
8553999e5d0SLars-Peter Clausen 	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
856cb7a01acSMauro Carvalho Chehab 	/* clear */
8573999e5d0SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_ICR3, isr3);
858cb7a01acSMauro Carvalho Chehab 
859937feeedSHans Verkuil 	if (isr3 & ADV7180_IRQ3_AD_CHANGE) {
860937feeedSHans Verkuil 		static const struct v4l2_event src_ch = {
861937feeedSHans Verkuil 			.type = V4L2_EVENT_SOURCE_CHANGE,
862937feeedSHans Verkuil 			.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
863937feeedSHans Verkuil 		};
864937feeedSHans Verkuil 
865937feeedSHans Verkuil 		v4l2_subdev_notify_event(&state->sd, &src_ch);
866937feeedSHans Verkuil 	}
867cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
868cb7a01acSMauro Carvalho Chehab 
869cb7a01acSMauro Carvalho Chehab 	return IRQ_HANDLED;
870cb7a01acSMauro Carvalho Chehab }
871cb7a01acSMauro Carvalho Chehab 
872f5dde49bSLars-Peter Clausen static int adv7180_init(struct adv7180_state *state)
873f5dde49bSLars-Peter Clausen {
874f5dde49bSLars-Peter Clausen 	int ret;
875f5dde49bSLars-Peter Clausen 
876f5dde49bSLars-Peter Clausen 	/* ITU-R BT.656-4 compatible */
877f5dde49bSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
878f5dde49bSLars-Peter Clausen 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
879f5dde49bSLars-Peter Clausen 	if (ret < 0)
880f5dde49bSLars-Peter Clausen 		return ret;
881f5dde49bSLars-Peter Clausen 
882f5dde49bSLars-Peter Clausen 	/* Manually set V bit end position in NTSC mode */
883f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
884f5dde49bSLars-Peter Clausen 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
885f5dde49bSLars-Peter Clausen }
886f5dde49bSLars-Peter Clausen 
887f5dde49bSLars-Peter Clausen static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
888f5dde49bSLars-Peter Clausen {
889f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
890f5dde49bSLars-Peter Clausen 		(std << 4) | state->input);
891f5dde49bSLars-Peter Clausen }
892f5dde49bSLars-Peter Clausen 
893f5dde49bSLars-Peter Clausen static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
894f5dde49bSLars-Peter Clausen {
895f5dde49bSLars-Peter Clausen 	int ret;
896f5dde49bSLars-Peter Clausen 
897f5dde49bSLars-Peter Clausen 	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
898f5dde49bSLars-Peter Clausen 	if (ret < 0)
899f5dde49bSLars-Peter Clausen 		return ret;
900f5dde49bSLars-Peter Clausen 
901f5dde49bSLars-Peter Clausen 	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
902f5dde49bSLars-Peter Clausen 	ret |= input;
903f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
904f5dde49bSLars-Peter Clausen }
905f5dde49bSLars-Peter Clausen 
906c5ef8f8cSLars-Peter Clausen static int adv7182_init(struct adv7180_state *state)
907c5ef8f8cSLars-Peter Clausen {
908b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
909b37135e3SLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
910b37135e3SLars-Peter Clausen 			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
911b37135e3SLars-Peter Clausen 
912851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
913851a54efSLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
914851a54efSLars-Peter Clausen 			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
915851a54efSLars-Peter Clausen 
916bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2) {
917bf7dcb80SLars-Peter Clausen 		/* ADI recommended writes for improved video quality */
918bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0080, 0x51);
919bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0081, 0x51);
920bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0082, 0x68);
921bf7dcb80SLars-Peter Clausen 	}
922bf7dcb80SLars-Peter Clausen 
923c5ef8f8cSLars-Peter Clausen 	/* ADI required writes */
924b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
925ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x4e);
926ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57);
927ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0);
928b37135e3SLars-Peter Clausen 	} else {
929b37135e3SLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_V2)
930ce5d6290SSteve Longerbeam 			adv7180_write(state,
931ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
932ce5d6290SSteve Longerbeam 				      0x17);
933b37135e3SLars-Peter Clausen 		else
934ce5d6290SSteve Longerbeam 			adv7180_write(state,
935ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
936ce5d6290SSteve Longerbeam 				      0x07);
937ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x0c);
938ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0x40);
939b37135e3SLars-Peter Clausen 	}
940b37135e3SLars-Peter Clausen 
941b37135e3SLars-Peter Clausen 	adv7180_write(state, 0x0013, 0x00);
942c5ef8f8cSLars-Peter Clausen 
943c5ef8f8cSLars-Peter Clausen 	return 0;
944c5ef8f8cSLars-Peter Clausen }
945c5ef8f8cSLars-Peter Clausen 
946c5ef8f8cSLars-Peter Clausen static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
947c5ef8f8cSLars-Peter Clausen {
948c5ef8f8cSLars-Peter Clausen 	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
949c5ef8f8cSLars-Peter Clausen }
950c5ef8f8cSLars-Peter Clausen 
951c5ef8f8cSLars-Peter Clausen enum adv7182_input_type {
952c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_CVBS,
953c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_DIFF_CVBS,
954c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_SVIDEO,
955c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_YPBPR,
956c5ef8f8cSLars-Peter Clausen };
957c5ef8f8cSLars-Peter Clausen 
958c5ef8f8cSLars-Peter Clausen static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
959c5ef8f8cSLars-Peter Clausen {
960c5ef8f8cSLars-Peter Clausen 	switch (input) {
961c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN1:
962c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN2:
963c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN3:
964c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN4:
965c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN5:
966c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN6:
967c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN7:
968c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN8:
969c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_CVBS;
970c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
971c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
972c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
973c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
974c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_SVIDEO;
975c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
976c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
977c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_YPBPR;
978c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
979c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
980c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
981c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
982c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_DIFF_CVBS;
983c5ef8f8cSLars-Peter Clausen 	default: /* Will never happen */
984c5ef8f8cSLars-Peter Clausen 		return 0;
985c5ef8f8cSLars-Peter Clausen 	}
986c5ef8f8cSLars-Peter Clausen }
987c5ef8f8cSLars-Peter Clausen 
988c5ef8f8cSLars-Peter Clausen /* ADI recommended writes to registers 0x52, 0x53, 0x54 */
989c5ef8f8cSLars-Peter Clausen static unsigned int adv7182_lbias_settings[][3] = {
990c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
991c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
992c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
993c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
994c5ef8f8cSLars-Peter Clausen };
995c5ef8f8cSLars-Peter Clausen 
996bf7dcb80SLars-Peter Clausen static unsigned int adv7280_lbias_settings[][3] = {
997bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
998bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
999bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
1000bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
1001bf7dcb80SLars-Peter Clausen };
1002bf7dcb80SLars-Peter Clausen 
1003c5ef8f8cSLars-Peter Clausen static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
1004c5ef8f8cSLars-Peter Clausen {
1005c5ef8f8cSLars-Peter Clausen 	enum adv7182_input_type input_type;
1006c5ef8f8cSLars-Peter Clausen 	unsigned int *lbias;
1007c5ef8f8cSLars-Peter Clausen 	unsigned int i;
1008c5ef8f8cSLars-Peter Clausen 	int ret;
1009c5ef8f8cSLars-Peter Clausen 
1010c5ef8f8cSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
1011c5ef8f8cSLars-Peter Clausen 	if (ret)
1012c5ef8f8cSLars-Peter Clausen 		return ret;
1013c5ef8f8cSLars-Peter Clausen 
1014c5ef8f8cSLars-Peter Clausen 	/* Reset clamp circuitry - ADI recommended writes */
1015ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0x00);
1016ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0xff);
1017c5ef8f8cSLars-Peter Clausen 
1018c5ef8f8cSLars-Peter Clausen 	input_type = adv7182_get_input_type(input);
1019c5ef8f8cSLars-Peter Clausen 
1020c5ef8f8cSLars-Peter Clausen 	switch (input_type) {
1021c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_CVBS:
1022c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_DIFF_CVBS:
1023c5ef8f8cSLars-Peter Clausen 		/* ADI recommends to use the SH1 filter */
1024ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x41);
1025c5ef8f8cSLars-Peter Clausen 		break;
1026c5ef8f8cSLars-Peter Clausen 	default:
1027ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x01);
1028c5ef8f8cSLars-Peter Clausen 		break;
1029c5ef8f8cSLars-Peter Clausen 	}
1030c5ef8f8cSLars-Peter Clausen 
1031bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2)
1032bf7dcb80SLars-Peter Clausen 		lbias = adv7280_lbias_settings[input_type];
1033bf7dcb80SLars-Peter Clausen 	else
1034c5ef8f8cSLars-Peter Clausen 		lbias = adv7182_lbias_settings[input_type];
1035c5ef8f8cSLars-Peter Clausen 
1036c5ef8f8cSLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
1037ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CVBS_TRIM + i, lbias[i]);
1038c5ef8f8cSLars-Peter Clausen 
1039c5ef8f8cSLars-Peter Clausen 	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
1040c5ef8f8cSLars-Peter Clausen 		/* ADI required writes to make differential CVBS work */
1041ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xa8);
1042ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0x90);
1043ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0xb0);
1044ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x08);
1045ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0xa0);
1046c5ef8f8cSLars-Peter Clausen 	} else {
1047ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xf0);
1048ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0xd0);
1049ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0x10);
1050ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x9c);
1051ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0x00);
1052c5ef8f8cSLars-Peter Clausen 	}
1053c5ef8f8cSLars-Peter Clausen 
1054c5ef8f8cSLars-Peter Clausen 	return 0;
1055c5ef8f8cSLars-Peter Clausen }
1056c5ef8f8cSLars-Peter Clausen 
1057f5dde49bSLars-Peter Clausen static const struct adv7180_chip_info adv7180_info = {
1058f5dde49bSLars-Peter Clausen 	.flags = ADV7180_FLAG_RESET_POWERED,
1059f5dde49bSLars-Peter Clausen 	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
1060f5dde49bSLars-Peter Clausen 	 * all inputs and let the card driver take care of validation
1061f5dde49bSLars-Peter Clausen 	 */
1062f5dde49bSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
1063f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN2) |
1064f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN3) |
1065f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN4) |
1066f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN5) |
1067f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN6) |
1068f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
1069f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
1070f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
1071f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1072f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
1073f5dde49bSLars-Peter Clausen 	.init = adv7180_init,
1074f5dde49bSLars-Peter Clausen 	.set_std = adv7180_set_std,
1075f5dde49bSLars-Peter Clausen 	.select_input = adv7180_select_input,
1076f5dde49bSLars-Peter Clausen };
1077f5dde49bSLars-Peter Clausen 
1078c5ef8f8cSLars-Peter Clausen static const struct adv7180_chip_info adv7182_info = {
1079c5ef8f8cSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1080c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1081c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1082c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1083c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1084c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1085c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1086c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1087c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
1088c5ef8f8cSLars-Peter Clausen 	.init = adv7182_init,
1089c5ef8f8cSLars-Peter Clausen 	.set_std = adv7182_set_std,
1090c5ef8f8cSLars-Peter Clausen 	.select_input = adv7182_select_input,
1091c5ef8f8cSLars-Peter Clausen };
1092c5ef8f8cSLars-Peter Clausen 
1093bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7280_info = {
1094851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1095bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1096bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1097bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1098bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1099bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1100bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1101bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
1102bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1103bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1104bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1105bf7dcb80SLars-Peter Clausen };
1106bf7dcb80SLars-Peter Clausen 
1107b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7280_m_info = {
1108851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1109b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1110b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1111b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1112b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1113b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1114b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1115b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1116b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1117b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1118b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1119b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1120b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1121b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1122b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
1123b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1124b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1125b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1126b37135e3SLars-Peter Clausen };
1127b37135e3SLars-Peter Clausen 
1128bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7281_info = {
1129b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1130bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1131bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1132bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1133bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1134bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1135bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1136bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1137bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1138bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1139bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1140bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1141bf7dcb80SLars-Peter Clausen };
1142bf7dcb80SLars-Peter Clausen 
1143b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_m_info = {
1144b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1145b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1146b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1147b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1148b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1149b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1150b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1151b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1152b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1153b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1154b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1155b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1156b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1157b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1158b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1159b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1160b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1161b37135e3SLars-Peter Clausen };
1162b37135e3SLars-Peter Clausen 
1163b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_ma_info = {
1164b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1165b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1166b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1167b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1168b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1169b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1170b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1171b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1172b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1173b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1174b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1175b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1176b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1177b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1178b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
1179b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1180b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1181b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
1182b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1183b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1184b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1185b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1186b37135e3SLars-Peter Clausen };
1187b37135e3SLars-Peter Clausen 
1188851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_info = {
1189851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1190851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1191851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1192851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1193851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1194851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1195851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1196851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1197851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1198851a54efSLars-Peter Clausen 	.init = adv7182_init,
1199851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1200851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1201851a54efSLars-Peter Clausen };
1202851a54efSLars-Peter Clausen 
1203851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_m_info = {
1204851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1205851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1206851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1207851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1208851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1209851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1210851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1211851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1212851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1213851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1214851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1215851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1216851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1217851a54efSLars-Peter Clausen 	.init = adv7182_init,
1218851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1219851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1220851a54efSLars-Peter Clausen };
1221851a54efSLars-Peter Clausen 
12223999e5d0SLars-Peter Clausen static int init_device(struct adv7180_state *state)
1223cb7a01acSMauro Carvalho Chehab {
1224cb7a01acSMauro Carvalho Chehab 	int ret;
1225cb7a01acSMauro Carvalho Chehab 
12263999e5d0SLars-Peter Clausen 	mutex_lock(&state->mutex);
12273999e5d0SLars-Peter Clausen 
122865d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, true);
122965d9e14aSSteve Longerbeam 
1230c18818e9SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
123116dfe72fSUlrich Hecht 	usleep_range(5000, 10000);
1232c18818e9SLars-Peter Clausen 
1233f5dde49bSLars-Peter Clausen 	ret = state->chip_info->init(state);
12343e35e33cSLars-Peter Clausen 	if (ret)
12353999e5d0SLars-Peter Clausen 		goto out_unlock;
1236cb7a01acSMauro Carvalho Chehab 
1237f5dde49bSLars-Peter Clausen 	ret = adv7180_program_std(state);
1238f5dde49bSLars-Peter Clausen 	if (ret)
12393999e5d0SLars-Peter Clausen 		goto out_unlock;
1240cb7a01acSMauro Carvalho Chehab 
1241851a54efSLars-Peter Clausen 	adv7180_set_field_mode(state);
1242851a54efSLars-Peter Clausen 
1243cb7a01acSMauro Carvalho Chehab 	/* register for interrupts */
1244cb7a01acSMauro Carvalho Chehab 	if (state->irq > 0) {
1245cb7a01acSMauro Carvalho Chehab 		/* config the Interrupt pin to be active low */
12463999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_ICONF1,
1247cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_ACTIVE_LOW |
1248cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_PSYNC_ONLY);
1249cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12503999e5d0SLars-Peter Clausen 			goto out_unlock;
1251cb7a01acSMauro Carvalho Chehab 
12523999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
1253cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12543999e5d0SLars-Peter Clausen 			goto out_unlock;
1255cb7a01acSMauro Carvalho Chehab 
12563999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
1257cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12583999e5d0SLars-Peter Clausen 			goto out_unlock;
1259cb7a01acSMauro Carvalho Chehab 
1260cb7a01acSMauro Carvalho Chehab 		/* enable AD change interrupts interrupts */
12613999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR3,
1262cb7a01acSMauro Carvalho Chehab 						ADV7180_IRQ3_AD_CHANGE);
1263cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12643999e5d0SLars-Peter Clausen 			goto out_unlock;
1265cb7a01acSMauro Carvalho Chehab 
12663999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
1267cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12683999e5d0SLars-Peter Clausen 			goto out_unlock;
1269cb7a01acSMauro Carvalho Chehab 	}
1270cb7a01acSMauro Carvalho Chehab 
12713999e5d0SLars-Peter Clausen out_unlock:
12723999e5d0SLars-Peter Clausen 	mutex_unlock(&state->mutex);
1273df065b37SAlexey Khoroshilov 
1274df065b37SAlexey Khoroshilov 	return ret;
1275cb7a01acSMauro Carvalho Chehab }
1276cb7a01acSMauro Carvalho Chehab 
12774c62e976SGreg Kroah-Hartman static int adv7180_probe(struct i2c_client *client,
1278cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1279cb7a01acSMauro Carvalho Chehab {
1280cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state;
1281cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1282cb7a01acSMauro Carvalho Chehab 	int ret;
1283cb7a01acSMauro Carvalho Chehab 
1284cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1285cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
1286cb7a01acSMauro Carvalho Chehab 		return -EIO;
1287cb7a01acSMauro Carvalho Chehab 
1288cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1289cb7a01acSMauro Carvalho Chehab 		 client->addr, client->adapter->name);
1290cb7a01acSMauro Carvalho Chehab 
1291c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
12927657e064SFabio Estevam 	if (state == NULL)
12937657e064SFabio Estevam 		return -ENOMEM;
1294cb7a01acSMauro Carvalho Chehab 
12953999e5d0SLars-Peter Clausen 	state->client = client;
1296851a54efSLars-Peter Clausen 	state->field = V4L2_FIELD_INTERLACED;
1297f5dde49bSLars-Peter Clausen 	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
12983999e5d0SLars-Peter Clausen 
129965d9e14aSSteve Longerbeam 	state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
130065d9e14aSSteve Longerbeam 						   GPIOD_OUT_HIGH);
130165d9e14aSSteve Longerbeam 	if (IS_ERR(state->pwdn_gpio)) {
130265d9e14aSSteve Longerbeam 		ret = PTR_ERR(state->pwdn_gpio);
130365d9e14aSSteve Longerbeam 		v4l_err(client, "request for power pin failed: %d\n", ret);
130465d9e14aSSteve Longerbeam 		return ret;
130565d9e14aSSteve Longerbeam 	}
130665d9e14aSSteve Longerbeam 
1307b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
1308b37135e3SLars-Peter Clausen 		state->csi_client = i2c_new_dummy(client->adapter,
1309b37135e3SLars-Peter Clausen 				ADV7180_DEFAULT_CSI_I2C_ADDR);
1310b37135e3SLars-Peter Clausen 		if (!state->csi_client)
1311b37135e3SLars-Peter Clausen 			return -ENOMEM;
1312b37135e3SLars-Peter Clausen 	}
1313b37135e3SLars-Peter Clausen 
1314851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
1315851a54efSLars-Peter Clausen 		state->vpp_client = i2c_new_dummy(client->adapter,
1316851a54efSLars-Peter Clausen 				ADV7180_DEFAULT_VPP_I2C_ADDR);
1317851a54efSLars-Peter Clausen 		if (!state->vpp_client) {
1318851a54efSLars-Peter Clausen 			ret = -ENOMEM;
1319851a54efSLars-Peter Clausen 			goto err_unregister_csi_client;
1320851a54efSLars-Peter Clausen 		}
1321851a54efSLars-Peter Clausen 	}
1322851a54efSLars-Peter Clausen 
1323cb7a01acSMauro Carvalho Chehab 	state->irq = client->irq;
1324cb7a01acSMauro Carvalho Chehab 	mutex_init(&state->mutex);
1325937feeedSHans Verkuil 	state->curr_norm = V4L2_STD_NTSC;
1326f5dde49bSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
1327e246c333SLars-Peter Clausen 		state->powered = true;
1328f5dde49bSLars-Peter Clausen 	else
1329f5dde49bSLars-Peter Clausen 		state->powered = false;
1330cb7a01acSMauro Carvalho Chehab 	state->input = 0;
1331cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
1332cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
1333937feeedSHans Verkuil 	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
1334cb7a01acSMauro Carvalho Chehab 
1335cb7a01acSMauro Carvalho Chehab 	ret = adv7180_init_controls(state);
1336cb7a01acSMauro Carvalho Chehab 	if (ret)
1337851a54efSLars-Peter Clausen 		goto err_unregister_vpp_client;
1338d5d51a82SLars-Peter Clausen 
1339d5d51a82SLars-Peter Clausen 	state->pad.flags = MEDIA_PAD_FL_SOURCE;
13404ca72efaSMauro Carvalho Chehab 	sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER;
1341ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
1342cb7a01acSMauro Carvalho Chehab 	if (ret)
1343cb7a01acSMauro Carvalho Chehab 		goto err_free_ctrl;
1344fa5b7945SLars-Peter Clausen 
1345d5d51a82SLars-Peter Clausen 	ret = init_device(state);
1346d5d51a82SLars-Peter Clausen 	if (ret)
1347d5d51a82SLars-Peter Clausen 		goto err_media_entity_cleanup;
1348d5d51a82SLars-Peter Clausen 
1349fa5721d1SLars-Peter Clausen 	if (state->irq) {
1350fa5721d1SLars-Peter Clausen 		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
1351f3e991d4SLars-Peter Clausen 					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
1352f3e991d4SLars-Peter Clausen 					   KBUILD_MODNAME, state);
1353fa5721d1SLars-Peter Clausen 		if (ret)
1354d5d51a82SLars-Peter Clausen 			goto err_media_entity_cleanup;
1355fa5721d1SLars-Peter Clausen 	}
1356fa5721d1SLars-Peter Clausen 
1357fa5b7945SLars-Peter Clausen 	ret = v4l2_async_register_subdev(sd);
1358fa5b7945SLars-Peter Clausen 	if (ret)
1359fa5b7945SLars-Peter Clausen 		goto err_free_irq;
1360fa5b7945SLars-Peter Clausen 
1361cb7a01acSMauro Carvalho Chehab 	return 0;
1362cb7a01acSMauro Carvalho Chehab 
1363fa5b7945SLars-Peter Clausen err_free_irq:
1364fa5b7945SLars-Peter Clausen 	if (state->irq > 0)
1365fa5b7945SLars-Peter Clausen 		free_irq(client->irq, state);
1366d5d51a82SLars-Peter Clausen err_media_entity_cleanup:
1367d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1368cb7a01acSMauro Carvalho Chehab err_free_ctrl:
1369cb7a01acSMauro Carvalho Chehab 	adv7180_exit_controls(state);
1370851a54efSLars-Peter Clausen err_unregister_vpp_client:
1371851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
1372851a54efSLars-Peter Clausen 		i2c_unregister_device(state->vpp_client);
1373b37135e3SLars-Peter Clausen err_unregister_csi_client:
1374b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
1375b37135e3SLars-Peter Clausen 		i2c_unregister_device(state->csi_client);
1376297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1377cb7a01acSMauro Carvalho Chehab 	return ret;
1378cb7a01acSMauro Carvalho Chehab }
1379cb7a01acSMauro Carvalho Chehab 
13804c62e976SGreg Kroah-Hartman static int adv7180_remove(struct i2c_client *client)
1381cb7a01acSMauro Carvalho Chehab {
1382cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1383cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1384cb7a01acSMauro Carvalho Chehab 
1385fa5b7945SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
1386fa5b7945SLars-Peter Clausen 
13870c25534dSLars-Peter Clausen 	if (state->irq > 0)
1388cb7a01acSMauro Carvalho Chehab 		free_irq(client->irq, state);
1389cb7a01acSMauro Carvalho Chehab 
1390d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1391b13f4af2SLars-Peter Clausen 	adv7180_exit_controls(state);
1392b37135e3SLars-Peter Clausen 
1393851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
1394851a54efSLars-Peter Clausen 		i2c_unregister_device(state->vpp_client);
1395b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
1396b37135e3SLars-Peter Clausen 		i2c_unregister_device(state->csi_client);
1397b37135e3SLars-Peter Clausen 
139865d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, false);
139965d9e14aSSteve Longerbeam 
1400297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1401b37135e3SLars-Peter Clausen 
1402cb7a01acSMauro Carvalho Chehab 	return 0;
1403cb7a01acSMauro Carvalho Chehab }
1404cb7a01acSMauro Carvalho Chehab 
1405cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = {
1406f5dde49bSLars-Peter Clausen 	{ "adv7180", (kernel_ulong_t)&adv7180_info },
1407c5ef8f8cSLars-Peter Clausen 	{ "adv7182", (kernel_ulong_t)&adv7182_info },
1408bf7dcb80SLars-Peter Clausen 	{ "adv7280", (kernel_ulong_t)&adv7280_info },
1409b37135e3SLars-Peter Clausen 	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
1410bf7dcb80SLars-Peter Clausen 	{ "adv7281", (kernel_ulong_t)&adv7281_info },
1411b37135e3SLars-Peter Clausen 	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
1412b37135e3SLars-Peter Clausen 	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
1413851a54efSLars-Peter Clausen 	{ "adv7282", (kernel_ulong_t)&adv7282_info },
1414851a54efSLars-Peter Clausen 	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
1415cb7a01acSMauro Carvalho Chehab 	{},
1416cb7a01acSMauro Carvalho Chehab };
1417f5dde49bSLars-Peter Clausen MODULE_DEVICE_TABLE(i2c, adv7180_id);
1418cb7a01acSMauro Carvalho Chehab 
1419cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
1420cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev)
1421cb7a01acSMauro Carvalho Chehab {
1422cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
1423e246c333SLars-Peter Clausen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1424e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
1425cb7a01acSMauro Carvalho Chehab 
14263999e5d0SLars-Peter Clausen 	return adv7180_set_power(state, false);
1427cb7a01acSMauro Carvalho Chehab }
1428cb7a01acSMauro Carvalho Chehab 
1429cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev)
1430cb7a01acSMauro Carvalho Chehab {
1431cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
1432cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1433cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1434cb7a01acSMauro Carvalho Chehab 	int ret;
1435cb7a01acSMauro Carvalho Chehab 
14363999e5d0SLars-Peter Clausen 	ret = init_device(state);
1437cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1438cb7a01acSMauro Carvalho Chehab 		return ret;
1439c18818e9SLars-Peter Clausen 
1440c18818e9SLars-Peter Clausen 	ret = adv7180_set_power(state, state->powered);
1441c18818e9SLars-Peter Clausen 	if (ret)
1442c18818e9SLars-Peter Clausen 		return ret;
1443c18818e9SLars-Peter Clausen 
1444cb7a01acSMauro Carvalho Chehab 	return 0;
1445cb7a01acSMauro Carvalho Chehab }
1446cc1088dcSLars-Peter Clausen 
1447cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
1448cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops)
1449cc1088dcSLars-Peter Clausen 
1450cc1088dcSLars-Peter Clausen #else
1451cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL
1452cb7a01acSMauro Carvalho Chehab #endif
1453cb7a01acSMauro Carvalho Chehab 
1454250121d3SBen Dooks #ifdef CONFIG_OF
1455250121d3SBen Dooks static const struct of_device_id adv7180_of_id[] = {
1456250121d3SBen Dooks 	{ .compatible = "adi,adv7180", },
1457bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7182", },
1458bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280", },
1459bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280-m", },
1460bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281", },
1461bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-m", },
1462bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-ma", },
1463bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282", },
1464bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282-m", },
1465250121d3SBen Dooks 	{ },
1466250121d3SBen Dooks };
1467250121d3SBen Dooks 
1468250121d3SBen Dooks MODULE_DEVICE_TABLE(of, adv7180_of_id);
1469250121d3SBen Dooks #endif
1470250121d3SBen Dooks 
1471cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = {
1472cb7a01acSMauro Carvalho Chehab 	.driver = {
1473cb7a01acSMauro Carvalho Chehab 		   .name = KBUILD_MODNAME,
1474cc1088dcSLars-Peter Clausen 		   .pm = ADV7180_PM_OPS,
1475250121d3SBen Dooks 		   .of_match_table = of_match_ptr(adv7180_of_id),
1476cb7a01acSMauro Carvalho Chehab 		   },
1477cb7a01acSMauro Carvalho Chehab 	.probe = adv7180_probe,
14784c62e976SGreg Kroah-Hartman 	.remove = adv7180_remove,
1479cb7a01acSMauro Carvalho Chehab 	.id_table = adv7180_id,
1480cb7a01acSMauro Carvalho Chehab };
1481cb7a01acSMauro Carvalho Chehab 
1482cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver);
1483cb7a01acSMauro Carvalho Chehab 
1484cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
1485cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories");
1486cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1487