xref: /openbmc/linux/drivers/media/i2c/adv7180.c (revision ca0fa5f0)
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 
17cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
18cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
19cb7a01acSMauro Carvalho Chehab #include <linux/errno.h>
20cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/interrupt.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
24250121d3SBen Dooks #include <linux/of.h>
2565d9e14aSSteve Longerbeam #include <linux/gpio/consumer.h>
26cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
27937feeedSHans Verkuil #include <media/v4l2-ioctl.h>
28937feeedSHans Verkuil #include <media/v4l2-event.h>
29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
31cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h>
32c18818e9SLars-Peter Clausen #include <linux/delay.h>
33cb7a01acSMauro Carvalho Chehab 
34f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
35f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
36f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
37f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
38f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_J				0x4
39f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_M				0x5
40f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL60				0x6
41f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_443				0x7
42f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_BG				0x8
43f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_N				0x9
44f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M				0xa
45f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M_PED				0xb
46f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N				0xc
47f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N_PED			0xd
48f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM				0xe
49f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM_PED			0xf
50f5dde49bSLars-Peter Clausen 
513999e5d0SLars-Peter Clausen #define ADV7180_REG_INPUT_CONTROL			0x0000
52cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
53cb7a01acSMauro Carvalho Chehab 
54c5ef8f8cSLars-Peter Clausen #define ADV7182_REG_INPUT_VIDSEL			0x0002
55c5ef8f8cSLars-Peter Clausen 
56ce5d6290SSteve Longerbeam #define ADV7180_REG_OUTPUT_CONTROL			0x0003
573999e5d0SLars-Peter Clausen #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
58cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
59cb7a01acSMauro Carvalho Chehab 
60ce5d6290SSteve Longerbeam #define ADV7180_REG_AUTODETECT_ENABLE			0x0007
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 
101ce5d6290SSteve Longerbeam #define ADV7180_REG_STATUS3		0x0013
102ce5d6290SSteve Longerbeam #define ADV7180_REG_ANALOG_CLAMP_CTL	0x0014
103ce5d6290SSteve Longerbeam #define ADV7180_REG_SHAP_FILTER_CTL_1	0x0017
104ce5d6290SSteve Longerbeam #define ADV7180_REG_CTRL_2		0x001d
105ce5d6290SSteve Longerbeam #define ADV7180_REG_VSYNC_FIELD_CTL_1	0x0031
106ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_1	0x003d
107ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_2	0x003e
108ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_3	0x003f
109ce5d6290SSteve Longerbeam #define ADV7180_REG_LOCK_CNT		0x0051
110ce5d6290SSteve Longerbeam #define ADV7180_REG_CVBS_TRIM		0x0052
111ce5d6290SSteve Longerbeam #define ADV7180_REG_CLAMP_ADJ		0x005a
112ce5d6290SSteve Longerbeam #define ADV7180_REG_RES_CIR		0x005f
113ce5d6290SSteve Longerbeam #define ADV7180_REG_DIFF_MODE		0x0060
114ce5d6290SSteve Longerbeam 
11552e37f0aSSteve Longerbeam #define ADV7180_REG_ICONF1		0x2040
116cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW	0x01
117cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY	0x10
118cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
119cb7a01acSMauro Carvalho Chehab /* Saturation */
1203999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
1213999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
122cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN		0
123cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF		128
124cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX		255
125cb7a01acSMauro Carvalho Chehab 
126cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK	0x01
127cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK	0x02
12852e37f0aSSteve Longerbeam #define ADV7180_REG_ISR1	0x2042
12952e37f0aSSteve Longerbeam #define ADV7180_REG_ICR1	0x2043
13052e37f0aSSteve Longerbeam #define ADV7180_REG_IMR1	0x2044
13152e37f0aSSteve Longerbeam #define ADV7180_REG_IMR2	0x2048
132cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE	0x08
13352e37f0aSSteve Longerbeam #define ADV7180_REG_ISR3	0x204A
13452e37f0aSSteve Longerbeam #define ADV7180_REG_ICR3	0x204B
13552e37f0aSSteve Longerbeam #define ADV7180_REG_IMR3	0x204C
13652e37f0aSSteve Longerbeam #define ADV7180_REG_IMR4	0x2050
137cb7a01acSMauro Carvalho Chehab 
1383999e5d0SLars-Peter Clausen #define ADV7180_REG_NTSC_V_BIT_END	0x00E6
139cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
140cb7a01acSMauro Carvalho Chehab 
141851a54efSLars-Peter Clausen #define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
142b37135e3SLars-Peter Clausen #define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
143b37135e3SLars-Peter Clausen 
144ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL1		0x4080
145ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL5		0x4084
14608b717c2SLars-Peter Clausen #define ADV7180_REG_FLCONTROL		0x40e0
14708b717c2SLars-Peter Clausen #define ADV7180_FLCONTROL_FL_ENABLE 0x1
14808b717c2SLars-Peter Clausen 
149ce5d6290SSteve Longerbeam #define ADV7180_REG_RST_CLAMP	0x809c
150ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ1	0x80b6
151ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ2	0x80c0
152ce5d6290SSteve Longerbeam 
153b37135e3SLars-Peter Clausen #define ADV7180_CSI_REG_PWRDN	0x00
154b37135e3SLars-Peter Clausen #define ADV7180_CSI_PWRDN	0x80
155b37135e3SLars-Peter Clausen 
156f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN1 0x00
157f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN2 0x01
158f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN3 0x02
159f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN4 0x03
160f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN5 0x04
161f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN6 0x05
162f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
163f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
164f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
165f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
166f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
167f5dde49bSLars-Peter Clausen 
168c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN1 0x00
169c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN2 0x01
170c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN3 0x02
171c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN4 0x03
172c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN5 0x04
173c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN6 0x05
174c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN7 0x06
175c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN8 0x07
176c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
177c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
178c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
179c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
180c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
181c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
182c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
183c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
184c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
185c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
186c5ef8f8cSLars-Peter Clausen 
187b37135e3SLars-Peter Clausen #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
188851a54efSLars-Peter Clausen #define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
189b37135e3SLars-Peter Clausen 
19008b717c2SLars-Peter Clausen #define V4L2_CID_ADV_FAST_SWITCH	(V4L2_CID_USER_ADV7180_BASE + 0x00)
19108b717c2SLars-Peter Clausen 
192f5dde49bSLars-Peter Clausen struct adv7180_state;
193f5dde49bSLars-Peter Clausen 
194f5dde49bSLars-Peter Clausen #define ADV7180_FLAG_RESET_POWERED	BIT(0)
195bf7dcb80SLars-Peter Clausen #define ADV7180_FLAG_V2			BIT(1)
196b37135e3SLars-Peter Clausen #define ADV7180_FLAG_MIPI_CSI2		BIT(2)
197851a54efSLars-Peter Clausen #define ADV7180_FLAG_I2P		BIT(3)
198f5dde49bSLars-Peter Clausen 
199f5dde49bSLars-Peter Clausen struct adv7180_chip_info {
200f5dde49bSLars-Peter Clausen 	unsigned int flags;
201f5dde49bSLars-Peter Clausen 	unsigned int valid_input_mask;
202f5dde49bSLars-Peter Clausen 	int (*set_std)(struct adv7180_state *st, unsigned int std);
203f5dde49bSLars-Peter Clausen 	int (*select_input)(struct adv7180_state *st, unsigned int input);
204f5dde49bSLars-Peter Clausen 	int (*init)(struct adv7180_state *state);
205f5dde49bSLars-Peter Clausen };
206f5dde49bSLars-Peter Clausen 
207cb7a01acSMauro Carvalho Chehab struct adv7180_state {
208cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;
209cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev	sd;
210d5d51a82SLars-Peter Clausen 	struct media_pad	pad;
211cb7a01acSMauro Carvalho Chehab 	struct mutex		mutex; /* mutual excl. when accessing chip */
212cb7a01acSMauro Carvalho Chehab 	int			irq;
21365d9e14aSSteve Longerbeam 	struct gpio_desc	*pwdn_gpio;
214cb7a01acSMauro Carvalho Chehab 	v4l2_std_id		curr_norm;
215e246c333SLars-Peter Clausen 	bool			powered;
216937feeedSHans Verkuil 	bool			streaming;
217cb7a01acSMauro Carvalho Chehab 	u8			input;
2183999e5d0SLars-Peter Clausen 
2193999e5d0SLars-Peter Clausen 	struct i2c_client	*client;
2203999e5d0SLars-Peter Clausen 	unsigned int		register_page;
221b37135e3SLars-Peter Clausen 	struct i2c_client	*csi_client;
222851a54efSLars-Peter Clausen 	struct i2c_client	*vpp_client;
223f5dde49bSLars-Peter Clausen 	const struct adv7180_chip_info *chip_info;
224851a54efSLars-Peter Clausen 	enum v4l2_field		field;
225cb7a01acSMauro Carvalho Chehab };
226cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
227cb7a01acSMauro Carvalho Chehab 					    struct adv7180_state,	\
228cb7a01acSMauro Carvalho Chehab 					    ctrl_hdl)->sd)
229cb7a01acSMauro Carvalho Chehab 
2303999e5d0SLars-Peter Clausen static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
2313999e5d0SLars-Peter Clausen {
2323999e5d0SLars-Peter Clausen 	if (state->register_page != page) {
2333999e5d0SLars-Peter Clausen 		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
2343999e5d0SLars-Peter Clausen 			page);
2353999e5d0SLars-Peter Clausen 		state->register_page = page;
2363999e5d0SLars-Peter Clausen 	}
2373999e5d0SLars-Peter Clausen 
2383999e5d0SLars-Peter Clausen 	return 0;
2393999e5d0SLars-Peter Clausen }
2403999e5d0SLars-Peter Clausen 
2413999e5d0SLars-Peter Clausen static int adv7180_write(struct adv7180_state *state, unsigned int reg,
2423999e5d0SLars-Peter Clausen 	unsigned int value)
2433999e5d0SLars-Peter Clausen {
2443999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2453999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2463999e5d0SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
2473999e5d0SLars-Peter Clausen }
2483999e5d0SLars-Peter Clausen 
2493999e5d0SLars-Peter Clausen static int adv7180_read(struct adv7180_state *state, unsigned int reg)
2503999e5d0SLars-Peter Clausen {
2513999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2523999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2533999e5d0SLars-Peter Clausen 	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
2543999e5d0SLars-Peter Clausen }
2553999e5d0SLars-Peter Clausen 
256b37135e3SLars-Peter Clausen static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
257b37135e3SLars-Peter Clausen 	unsigned int value)
258b37135e3SLars-Peter Clausen {
259b37135e3SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
260b37135e3SLars-Peter Clausen }
261b37135e3SLars-Peter Clausen 
262f5dde49bSLars-Peter Clausen static int adv7180_set_video_standard(struct adv7180_state *state,
263f5dde49bSLars-Peter Clausen 	unsigned int std)
264f5dde49bSLars-Peter Clausen {
265f5dde49bSLars-Peter Clausen 	return state->chip_info->set_std(state, std);
266f5dde49bSLars-Peter Clausen }
2673999e5d0SLars-Peter Clausen 
268851a54efSLars-Peter Clausen static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
269851a54efSLars-Peter Clausen 	unsigned int value)
270851a54efSLars-Peter Clausen {
271851a54efSLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
272851a54efSLars-Peter Clausen }
273851a54efSLars-Peter Clausen 
274cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
275cb7a01acSMauro Carvalho Chehab {
276b294a192SVladimir Barinov 	/* in case V4L2_IN_ST_NO_SIGNAL */
277b294a192SVladimir Barinov 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
278b294a192SVladimir Barinov 		return V4L2_STD_UNKNOWN;
279b294a192SVladimir Barinov 
280cb7a01acSMauro Carvalho Chehab 	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
281cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
282cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
283cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
284cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
285cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_M:
286cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
287cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_60:
288cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_60;
289cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_B_G:
290cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
291cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM:
292cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
293cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_COMB:
294cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
295cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM_525:
296cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
297cb7a01acSMauro Carvalho Chehab 	default:
298cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
299cb7a01acSMauro Carvalho Chehab 	}
300cb7a01acSMauro Carvalho Chehab }
301cb7a01acSMauro Carvalho Chehab 
302cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std)
303cb7a01acSMauro Carvalho Chehab {
304cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
305f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL60;
306cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_NTSC_443)
307f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_443;
308cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_N)
309f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_N;
310cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_M)
311f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_M;
312cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_Nc)
313f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_COMB_N;
314cb7a01acSMauro Carvalho Chehab 
315cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_PAL)
316f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_BG;
317cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC)
318f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_M;
319cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_SECAM)
320f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_SECAM;
321cb7a01acSMauro Carvalho Chehab 
322cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
323cb7a01acSMauro Carvalho Chehab }
324cb7a01acSMauro Carvalho Chehab 
325cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1)
326cb7a01acSMauro Carvalho Chehab {
327cb7a01acSMauro Carvalho Chehab 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
328cb7a01acSMauro Carvalho Chehab 		return V4L2_IN_ST_NO_SIGNAL;
329cb7a01acSMauro Carvalho Chehab 
330cb7a01acSMauro Carvalho Chehab 	return 0;
331cb7a01acSMauro Carvalho Chehab }
332cb7a01acSMauro Carvalho Chehab 
3333999e5d0SLars-Peter Clausen static int __adv7180_status(struct adv7180_state *state, u32 *status,
334cb7a01acSMauro Carvalho Chehab 			    v4l2_std_id *std)
335cb7a01acSMauro Carvalho Chehab {
3363999e5d0SLars-Peter Clausen 	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
337cb7a01acSMauro Carvalho Chehab 
338cb7a01acSMauro Carvalho Chehab 	if (status1 < 0)
339cb7a01acSMauro Carvalho Chehab 		return status1;
340cb7a01acSMauro Carvalho Chehab 
341cb7a01acSMauro Carvalho Chehab 	if (status)
342cb7a01acSMauro Carvalho Chehab 		*status = adv7180_status_to_v4l2(status1);
343cb7a01acSMauro Carvalho Chehab 	if (std)
344cb7a01acSMauro Carvalho Chehab 		*std = adv7180_std_to_v4l2(status1);
345cb7a01acSMauro Carvalho Chehab 
346cb7a01acSMauro Carvalho Chehab 	return 0;
347cb7a01acSMauro Carvalho Chehab }
348cb7a01acSMauro Carvalho Chehab 
349cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
350cb7a01acSMauro Carvalho Chehab {
351cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7180_state, sd);
352cb7a01acSMauro Carvalho Chehab }
353cb7a01acSMauro Carvalho Chehab 
354cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
355cb7a01acSMauro Carvalho Chehab {
356cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
357cb7a01acSMauro Carvalho Chehab 	int err = mutex_lock_interruptible(&state->mutex);
358cb7a01acSMauro Carvalho Chehab 	if (err)
359cb7a01acSMauro Carvalho Chehab 		return err;
360cb7a01acSMauro Carvalho Chehab 
361937feeedSHans Verkuil 	if (state->streaming) {
362937feeedSHans Verkuil 		err = -EBUSY;
363937feeedSHans Verkuil 		goto unlock;
364937feeedSHans Verkuil 	}
365cb7a01acSMauro Carvalho Chehab 
366937feeedSHans Verkuil 	err = adv7180_set_video_standard(state,
367937feeedSHans Verkuil 			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
368937feeedSHans Verkuil 	if (err)
369937feeedSHans Verkuil 		goto unlock;
370937feeedSHans Verkuil 
371937feeedSHans Verkuil 	msleep(100);
372937feeedSHans Verkuil 	__adv7180_status(state, NULL, std);
373937feeedSHans Verkuil 
374937feeedSHans Verkuil 	err = v4l2_std_to_adv7180(state->curr_norm);
375937feeedSHans Verkuil 	if (err < 0)
376937feeedSHans Verkuil 		goto unlock;
377937feeedSHans Verkuil 
378937feeedSHans Verkuil 	err = adv7180_set_video_standard(state, err);
379937feeedSHans Verkuil 
380937feeedSHans Verkuil unlock:
381cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
382cb7a01acSMauro Carvalho Chehab 	return err;
383cb7a01acSMauro Carvalho Chehab }
384cb7a01acSMauro Carvalho Chehab 
385cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
386cb7a01acSMauro Carvalho Chehab 			     u32 output, u32 config)
387cb7a01acSMauro Carvalho Chehab {
388cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
389cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
390cb7a01acSMauro Carvalho Chehab 
391cb7a01acSMauro Carvalho Chehab 	if (ret)
392cb7a01acSMauro Carvalho Chehab 		return ret;
393cb7a01acSMauro Carvalho Chehab 
394f5dde49bSLars-Peter Clausen 	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
395f5dde49bSLars-Peter Clausen 		ret = -EINVAL;
396cb7a01acSMauro Carvalho Chehab 		goto out;
397f5dde49bSLars-Peter Clausen 	}
398cb7a01acSMauro Carvalho Chehab 
399f5dde49bSLars-Peter Clausen 	ret = state->chip_info->select_input(state, input);
400cb7a01acSMauro Carvalho Chehab 
401f5dde49bSLars-Peter Clausen 	if (ret == 0)
402cb7a01acSMauro Carvalho Chehab 		state->input = input;
403cb7a01acSMauro Carvalho Chehab out:
404cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
405cb7a01acSMauro Carvalho Chehab 	return ret;
406cb7a01acSMauro Carvalho Chehab }
407cb7a01acSMauro Carvalho Chehab 
408cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
409cb7a01acSMauro Carvalho Chehab {
410cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
411cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
412cb7a01acSMauro Carvalho Chehab 	if (ret)
413cb7a01acSMauro Carvalho Chehab 		return ret;
414cb7a01acSMauro Carvalho Chehab 
4153999e5d0SLars-Peter Clausen 	ret = __adv7180_status(state, status, NULL);
416cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
417cb7a01acSMauro Carvalho Chehab 	return ret;
418cb7a01acSMauro Carvalho Chehab }
419cb7a01acSMauro Carvalho Chehab 
4203e35e33cSLars-Peter Clausen static int adv7180_program_std(struct adv7180_state *state)
4213e35e33cSLars-Peter Clausen {
4223e35e33cSLars-Peter Clausen 	int ret;
4233e35e33cSLars-Peter Clausen 
4243e35e33cSLars-Peter Clausen 	ret = v4l2_std_to_adv7180(state->curr_norm);
4253e35e33cSLars-Peter Clausen 	if (ret < 0)
4263e35e33cSLars-Peter Clausen 		return ret;
4273e35e33cSLars-Peter Clausen 
428f5dde49bSLars-Peter Clausen 	ret = adv7180_set_video_standard(state, ret);
4293e35e33cSLars-Peter Clausen 	if (ret < 0)
4303e35e33cSLars-Peter Clausen 		return ret;
4313e35e33cSLars-Peter Clausen 	return 0;
4323e35e33cSLars-Peter Clausen }
4333e35e33cSLars-Peter Clausen 
434cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
435cb7a01acSMauro Carvalho Chehab {
436cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
437cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
4383e35e33cSLars-Peter Clausen 
439cb7a01acSMauro Carvalho Chehab 	if (ret)
440cb7a01acSMauro Carvalho Chehab 		return ret;
441cb7a01acSMauro Carvalho Chehab 
4423e35e33cSLars-Peter Clausen 	/* Make sure we can support this std */
443cb7a01acSMauro Carvalho Chehab 	ret = v4l2_std_to_adv7180(std);
444cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
445cb7a01acSMauro Carvalho Chehab 		goto out;
446cb7a01acSMauro Carvalho Chehab 
447cb7a01acSMauro Carvalho Chehab 	state->curr_norm = std;
4483e35e33cSLars-Peter Clausen 
4493e35e33cSLars-Peter Clausen 	ret = adv7180_program_std(state);
450cb7a01acSMauro Carvalho Chehab out:
451cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
452cb7a01acSMauro Carvalho Chehab 	return ret;
453cb7a01acSMauro Carvalho Chehab }
454cb7a01acSMauro Carvalho Chehab 
455d0fadc86SNiklas Söderlund static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
456d0fadc86SNiklas Söderlund {
457d0fadc86SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
458d0fadc86SNiklas Söderlund 
459d0fadc86SNiklas Söderlund 	*norm = state->curr_norm;
460d0fadc86SNiklas Söderlund 
461d0fadc86SNiklas Söderlund 	return 0;
462d0fadc86SNiklas Söderlund }
463d0fadc86SNiklas Söderlund 
46465d9e14aSSteve Longerbeam static void adv7180_set_power_pin(struct adv7180_state *state, bool on)
46565d9e14aSSteve Longerbeam {
46665d9e14aSSteve Longerbeam 	if (!state->pwdn_gpio)
46765d9e14aSSteve Longerbeam 		return;
46865d9e14aSSteve Longerbeam 
46965d9e14aSSteve Longerbeam 	if (on) {
47065d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 0);
47165d9e14aSSteve Longerbeam 		usleep_range(5000, 10000);
47265d9e14aSSteve Longerbeam 	} else {
47365d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 1);
47465d9e14aSSteve Longerbeam 	}
47565d9e14aSSteve Longerbeam }
47665d9e14aSSteve Longerbeam 
4773999e5d0SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state, bool on)
478e246c333SLars-Peter Clausen {
479e246c333SLars-Peter Clausen 	u8 val;
480b37135e3SLars-Peter Clausen 	int ret;
481e246c333SLars-Peter Clausen 
482e246c333SLars-Peter Clausen 	if (on)
483e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_ON;
484e246c333SLars-Peter Clausen 	else
485e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_OFF;
486e246c333SLars-Peter Clausen 
487b37135e3SLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
488b37135e3SLars-Peter Clausen 	if (ret)
489b37135e3SLars-Peter Clausen 		return ret;
490b37135e3SLars-Peter Clausen 
491b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
492b37135e3SLars-Peter Clausen 		if (on) {
493b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xDE, 0x02);
494b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD2, 0xF7);
495b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD8, 0x65);
496b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xE0, 0x09);
497b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x2C, 0x00);
498851a54efSLars-Peter Clausen 			if (state->field == V4L2_FIELD_NONE)
499851a54efSLars-Peter Clausen 				adv7180_csi_write(state, 0x1D, 0x80);
500b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x00);
501b37135e3SLars-Peter Clausen 		} else {
502b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x80);
503b37135e3SLars-Peter Clausen 		}
504b37135e3SLars-Peter Clausen 	}
505b37135e3SLars-Peter Clausen 
506b37135e3SLars-Peter Clausen 	return 0;
507e246c333SLars-Peter Clausen }
508e246c333SLars-Peter Clausen 
509e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on)
510e246c333SLars-Peter Clausen {
511e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
512e246c333SLars-Peter Clausen 	int ret;
513e246c333SLars-Peter Clausen 
514e246c333SLars-Peter Clausen 	ret = mutex_lock_interruptible(&state->mutex);
515e246c333SLars-Peter Clausen 	if (ret)
516e246c333SLars-Peter Clausen 		return ret;
517e246c333SLars-Peter Clausen 
5183999e5d0SLars-Peter Clausen 	ret = adv7180_set_power(state, on);
519e246c333SLars-Peter Clausen 	if (ret == 0)
520e246c333SLars-Peter Clausen 		state->powered = on;
521e246c333SLars-Peter Clausen 
522e246c333SLars-Peter Clausen 	mutex_unlock(&state->mutex);
523e246c333SLars-Peter Clausen 	return ret;
524e246c333SLars-Peter Clausen }
525e246c333SLars-Peter Clausen 
526cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
527cb7a01acSMauro Carvalho Chehab {
528cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
529cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
530cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
531cb7a01acSMauro Carvalho Chehab 	int val;
532cb7a01acSMauro Carvalho Chehab 
533cb7a01acSMauro Carvalho Chehab 	if (ret)
534cb7a01acSMauro Carvalho Chehab 		return ret;
535cb7a01acSMauro Carvalho Chehab 	val = ctrl->val;
536cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
537cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
5383999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_BRI, val);
539cb7a01acSMauro Carvalho Chehab 		break;
540cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
541cb7a01acSMauro Carvalho Chehab 		/*Hue is inverted according to HSL chart */
5423999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
543cb7a01acSMauro Carvalho Chehab 		break;
544cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
5453999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_CON, val);
546cb7a01acSMauro Carvalho Chehab 		break;
547cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
548cb7a01acSMauro Carvalho Chehab 		/*
549cb7a01acSMauro Carvalho Chehab 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
550cb7a01acSMauro Carvalho Chehab 		 *Let's not confuse the user, everybody understands saturation
551cb7a01acSMauro Carvalho Chehab 		 */
5523999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
553cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
554cb7a01acSMauro Carvalho Chehab 			break;
5553999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
556cb7a01acSMauro Carvalho Chehab 		break;
55708b717c2SLars-Peter Clausen 	case V4L2_CID_ADV_FAST_SWITCH:
55808b717c2SLars-Peter Clausen 		if (ctrl->val) {
55908b717c2SLars-Peter Clausen 			/* ADI required write */
56008b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0x44);
56108b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL,
56208b717c2SLars-Peter Clausen 				ADV7180_FLCONTROL_FL_ENABLE);
56308b717c2SLars-Peter Clausen 		} else {
56408b717c2SLars-Peter Clausen 			/* ADI required write */
56508b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0xc4);
56608b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
56708b717c2SLars-Peter Clausen 		}
56808b717c2SLars-Peter Clausen 		break;
569cb7a01acSMauro Carvalho Chehab 	default:
570cb7a01acSMauro Carvalho Chehab 		ret = -EINVAL;
571cb7a01acSMauro Carvalho Chehab 	}
572cb7a01acSMauro Carvalho Chehab 
573cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
574cb7a01acSMauro Carvalho Chehab 	return ret;
575cb7a01acSMauro Carvalho Chehab }
576cb7a01acSMauro Carvalho Chehab 
577cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
578cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7180_s_ctrl,
579cb7a01acSMauro Carvalho Chehab };
580cb7a01acSMauro Carvalho Chehab 
58108b717c2SLars-Peter Clausen static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
58208b717c2SLars-Peter Clausen 	.ops = &adv7180_ctrl_ops,
58308b717c2SLars-Peter Clausen 	.id = V4L2_CID_ADV_FAST_SWITCH,
58408b717c2SLars-Peter Clausen 	.name = "Fast Switching",
58508b717c2SLars-Peter Clausen 	.type = V4L2_CTRL_TYPE_BOOLEAN,
58608b717c2SLars-Peter Clausen 	.min = 0,
58708b717c2SLars-Peter Clausen 	.max = 1,
58808b717c2SLars-Peter Clausen 	.step = 1,
58908b717c2SLars-Peter Clausen };
59008b717c2SLars-Peter Clausen 
591cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state)
592cb7a01acSMauro Carvalho Chehab {
593cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
594cb7a01acSMauro Carvalho Chehab 
595cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
596cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
597cb7a01acSMauro Carvalho Chehab 			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
598cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
599cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
600cb7a01acSMauro Carvalho Chehab 			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
601cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
602cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
603cb7a01acSMauro Carvalho Chehab 			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
604cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
605cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
606cb7a01acSMauro Carvalho Chehab 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
60708b717c2SLars-Peter Clausen 	v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
60808b717c2SLars-Peter Clausen 
609cb7a01acSMauro Carvalho Chehab 	state->sd.ctrl_handler = &state->ctrl_hdl;
610cb7a01acSMauro Carvalho Chehab 	if (state->ctrl_hdl.error) {
611cb7a01acSMauro Carvalho Chehab 		int err = state->ctrl_hdl.error;
612cb7a01acSMauro Carvalho Chehab 
613cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->ctrl_hdl);
614cb7a01acSMauro Carvalho Chehab 		return err;
615cb7a01acSMauro Carvalho Chehab 	}
616cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
617cb7a01acSMauro Carvalho Chehab 
618cb7a01acSMauro Carvalho Chehab 	return 0;
619cb7a01acSMauro Carvalho Chehab }
620cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state)
621cb7a01acSMauro Carvalho Chehab {
622cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
623cb7a01acSMauro Carvalho Chehab }
624cb7a01acSMauro Carvalho Chehab 
625d5d51a82SLars-Peter Clausen static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
626f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
627d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_mbus_code_enum *code)
628cccb83f7SVladimir Barinov {
629d5d51a82SLars-Peter Clausen 	if (code->index != 0)
630cccb83f7SVladimir Barinov 		return -EINVAL;
631cccb83f7SVladimir Barinov 
6326de690ddSNiklas Söderlund 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
633cccb83f7SVladimir Barinov 
634cccb83f7SVladimir Barinov 	return 0;
635cccb83f7SVladimir Barinov }
636cccb83f7SVladimir Barinov 
637cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
638cccb83f7SVladimir Barinov 			    struct v4l2_mbus_framefmt *fmt)
639cccb83f7SVladimir Barinov {
640cccb83f7SVladimir Barinov 	struct adv7180_state *state = to_state(sd);
641cccb83f7SVladimir Barinov 
6426de690ddSNiklas Söderlund 	fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
643cccb83f7SVladimir Barinov 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
644cccb83f7SVladimir Barinov 	fmt->width = 720;
645cccb83f7SVladimir Barinov 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
646cccb83f7SVladimir Barinov 
647cccb83f7SVladimir Barinov 	return 0;
648cccb83f7SVladimir Barinov }
649cccb83f7SVladimir Barinov 
650851a54efSLars-Peter Clausen static int adv7180_set_field_mode(struct adv7180_state *state)
651851a54efSLars-Peter Clausen {
652851a54efSLars-Peter Clausen 	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
653851a54efSLars-Peter Clausen 		return 0;
654851a54efSLars-Peter Clausen 
655851a54efSLars-Peter Clausen 	if (state->field == V4L2_FIELD_NONE) {
656851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
657851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x20);
658851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x28);
659851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x38);
660851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x30);
661851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x30);
662851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x80);
663851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x70);
664851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x50);
665851a54efSLars-Peter Clausen 		}
666851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x00);
667851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x00);
668851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x80);
669851a54efSLars-Peter Clausen 	} else {
670851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
671851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x18);
672851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x18);
673851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x30);
674851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x20);
675851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x28);
676851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x40);
677851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x58);
678851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x30);
679851a54efSLars-Peter Clausen 		}
680851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x70);
681851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x80);
682851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x00);
683851a54efSLars-Peter Clausen 	}
684851a54efSLars-Peter Clausen 
685851a54efSLars-Peter Clausen 	return 0;
686851a54efSLars-Peter Clausen }
687851a54efSLars-Peter Clausen 
688d5d51a82SLars-Peter Clausen static int adv7180_get_pad_format(struct v4l2_subdev *sd,
689f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
690d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
691d5d51a82SLars-Peter Clausen {
692851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
693851a54efSLars-Peter Clausen 
694851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
695f7234138SHans Verkuil 		format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
696851a54efSLars-Peter Clausen 	} else {
697851a54efSLars-Peter Clausen 		adv7180_mbus_fmt(sd, &format->format);
698851a54efSLars-Peter Clausen 		format->format.field = state->field;
699851a54efSLars-Peter Clausen 	}
700851a54efSLars-Peter Clausen 
701851a54efSLars-Peter Clausen 	return 0;
702d5d51a82SLars-Peter Clausen }
703d5d51a82SLars-Peter Clausen 
704d5d51a82SLars-Peter Clausen static int adv7180_set_pad_format(struct v4l2_subdev *sd,
705f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
706d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
707d5d51a82SLars-Peter Clausen {
708851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
709851a54efSLars-Peter Clausen 	struct v4l2_mbus_framefmt *framefmt;
710e0ad7a9bSNiklas Söderlund 	int ret;
711851a54efSLars-Peter Clausen 
712851a54efSLars-Peter Clausen 	switch (format->format.field) {
713851a54efSLars-Peter Clausen 	case V4L2_FIELD_NONE:
714851a54efSLars-Peter Clausen 		if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
715851a54efSLars-Peter Clausen 			format->format.field = V4L2_FIELD_INTERLACED;
716851a54efSLars-Peter Clausen 		break;
717851a54efSLars-Peter Clausen 	default:
718851a54efSLars-Peter Clausen 		format->format.field = V4L2_FIELD_INTERLACED;
719851a54efSLars-Peter Clausen 		break;
720851a54efSLars-Peter Clausen 	}
721851a54efSLars-Peter Clausen 
722e0ad7a9bSNiklas Söderlund 	ret = adv7180_mbus_fmt(sd,  &format->format);
723e0ad7a9bSNiklas Söderlund 
724851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
725851a54efSLars-Peter Clausen 		if (state->field != format->format.field) {
726851a54efSLars-Peter Clausen 			state->field = format->format.field;
727851a54efSLars-Peter Clausen 			adv7180_set_power(state, false);
728851a54efSLars-Peter Clausen 			adv7180_set_field_mode(state);
729851a54efSLars-Peter Clausen 			adv7180_set_power(state, true);
730851a54efSLars-Peter Clausen 		}
731851a54efSLars-Peter Clausen 	} else {
732f7234138SHans Verkuil 		framefmt = v4l2_subdev_get_try_format(sd, cfg, 0);
733851a54efSLars-Peter Clausen 		*framefmt = format->format;
734851a54efSLars-Peter Clausen 	}
735851a54efSLars-Peter Clausen 
736e0ad7a9bSNiklas Söderlund 	return ret;
737d5d51a82SLars-Peter Clausen }
738d5d51a82SLars-Peter Clausen 
739cccb83f7SVladimir Barinov static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
740cccb83f7SVladimir Barinov 				 struct v4l2_mbus_config *cfg)
741cccb83f7SVladimir Barinov {
742b37135e3SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
743b37135e3SLars-Peter Clausen 
744b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
745b37135e3SLars-Peter Clausen 		cfg->type = V4L2_MBUS_CSI2;
746b37135e3SLars-Peter Clausen 		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
747b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CHANNEL_0 |
748b37135e3SLars-Peter Clausen 				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
749b37135e3SLars-Peter Clausen 	} else {
750cccb83f7SVladimir Barinov 		/*
751cccb83f7SVladimir Barinov 		 * The ADV7180 sensor supports BT.601/656 output modes.
752cccb83f7SVladimir Barinov 		 * The BT.656 is default and not yet configurable by s/w.
753cccb83f7SVladimir Barinov 		 */
754cccb83f7SVladimir Barinov 		cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
755cccb83f7SVladimir Barinov 				 V4L2_MBUS_DATA_ACTIVE_HIGH;
756cccb83f7SVladimir Barinov 		cfg->type = V4L2_MBUS_BT656;
757b37135e3SLars-Peter Clausen 	}
758cccb83f7SVladimir Barinov 
759cccb83f7SVladimir Barinov 	return 0;
760cccb83f7SVladimir Barinov }
761cccb83f7SVladimir Barinov 
762ecf37493SHans Verkuil static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect)
76364b3df92SNiklas Söderlund {
76464b3df92SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
76564b3df92SNiklas Söderlund 
76664b3df92SNiklas Söderlund 	if (state->curr_norm & V4L2_STD_525_60) {
767ecf37493SHans Verkuil 		aspect->numerator = 11;
768ecf37493SHans Verkuil 		aspect->denominator = 10;
76964b3df92SNiklas Söderlund 	} else {
770ecf37493SHans Verkuil 		aspect->numerator = 54;
771ecf37493SHans Verkuil 		aspect->denominator = 59;
77264b3df92SNiklas Söderlund 	}
77364b3df92SNiklas Söderlund 
77464b3df92SNiklas Söderlund 	return 0;
77564b3df92SNiklas Söderlund }
77664b3df92SNiklas Söderlund 
777bae4c757SNiklas Söderlund static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
778bae4c757SNiklas Söderlund {
779bae4c757SNiklas Söderlund 	*norm = V4L2_STD_ALL;
780bae4c757SNiklas Söderlund 	return 0;
781bae4c757SNiklas Söderlund }
782bae4c757SNiklas Söderlund 
783937feeedSHans Verkuil static int adv7180_s_stream(struct v4l2_subdev *sd, int enable)
784937feeedSHans Verkuil {
785937feeedSHans Verkuil 	struct adv7180_state *state = to_state(sd);
786937feeedSHans Verkuil 	int ret;
787937feeedSHans Verkuil 
788937feeedSHans Verkuil 	/* It's always safe to stop streaming, no need to take the lock */
789937feeedSHans Verkuil 	if (!enable) {
790937feeedSHans Verkuil 		state->streaming = enable;
791937feeedSHans Verkuil 		return 0;
792937feeedSHans Verkuil 	}
793937feeedSHans Verkuil 
794937feeedSHans Verkuil 	/* Must wait until querystd released the lock */
795937feeedSHans Verkuil 	ret = mutex_lock_interruptible(&state->mutex);
796937feeedSHans Verkuil 	if (ret)
797937feeedSHans Verkuil 		return ret;
798937feeedSHans Verkuil 	state->streaming = enable;
799937feeedSHans Verkuil 	mutex_unlock(&state->mutex);
800937feeedSHans Verkuil 	return 0;
801937feeedSHans Verkuil }
802937feeedSHans Verkuil 
803937feeedSHans Verkuil static int adv7180_subscribe_event(struct v4l2_subdev *sd,
804937feeedSHans Verkuil 				   struct v4l2_fh *fh,
805937feeedSHans Verkuil 				   struct v4l2_event_subscription *sub)
806937feeedSHans Verkuil {
807937feeedSHans Verkuil 	switch (sub->type) {
808937feeedSHans Verkuil 	case V4L2_EVENT_SOURCE_CHANGE:
809937feeedSHans Verkuil 		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
810937feeedSHans Verkuil 	case V4L2_EVENT_CTRL:
811937feeedSHans Verkuil 		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
812937feeedSHans Verkuil 	default:
813937feeedSHans Verkuil 		return -EINVAL;
814937feeedSHans Verkuil 	}
815937feeedSHans Verkuil }
816937feeedSHans Verkuil 
817cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = {
8188774bed9SLaurent Pinchart 	.s_std = adv7180_s_std,
819d0fadc86SNiklas Söderlund 	.g_std = adv7180_g_std,
820cb7a01acSMauro Carvalho Chehab 	.querystd = adv7180_querystd,
821cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7180_g_input_status,
822cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7180_s_routing,
823cccb83f7SVladimir Barinov 	.g_mbus_config = adv7180_g_mbus_config,
824ecf37493SHans Verkuil 	.g_pixelaspect = adv7180_g_pixelaspect,
825bae4c757SNiklas Söderlund 	.g_tvnorms = adv7180_g_tvnorms,
826937feeedSHans Verkuil 	.s_stream = adv7180_s_stream,
827cb7a01acSMauro Carvalho Chehab };
828cb7a01acSMauro Carvalho Chehab 
829cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = {
830e246c333SLars-Peter Clausen 	.s_power = adv7180_s_power,
831937feeedSHans Verkuil 	.subscribe_event = adv7180_subscribe_event,
832937feeedSHans Verkuil 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
833cb7a01acSMauro Carvalho Chehab };
834cb7a01acSMauro Carvalho Chehab 
835d5d51a82SLars-Peter Clausen static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
836d5d51a82SLars-Peter Clausen 	.enum_mbus_code = adv7180_enum_mbus_code,
837d5d51a82SLars-Peter Clausen 	.set_fmt = adv7180_set_pad_format,
838d5d51a82SLars-Peter Clausen 	.get_fmt = adv7180_get_pad_format,
839d5d51a82SLars-Peter Clausen };
840d5d51a82SLars-Peter Clausen 
841cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = {
842cb7a01acSMauro Carvalho Chehab 	.core = &adv7180_core_ops,
843cb7a01acSMauro Carvalho Chehab 	.video = &adv7180_video_ops,
844d5d51a82SLars-Peter Clausen 	.pad = &adv7180_pad_ops,
845cb7a01acSMauro Carvalho Chehab };
846cb7a01acSMauro Carvalho Chehab 
8470c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid)
848cb7a01acSMauro Carvalho Chehab {
8490c25534dSLars-Peter Clausen 	struct adv7180_state *state = devid;
850cb7a01acSMauro Carvalho Chehab 	u8 isr3;
851cb7a01acSMauro Carvalho Chehab 
852cb7a01acSMauro Carvalho Chehab 	mutex_lock(&state->mutex);
8533999e5d0SLars-Peter Clausen 	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
854cb7a01acSMauro Carvalho Chehab 	/* clear */
8553999e5d0SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_ICR3, isr3);
856cb7a01acSMauro Carvalho Chehab 
857937feeedSHans Verkuil 	if (isr3 & ADV7180_IRQ3_AD_CHANGE) {
858937feeedSHans Verkuil 		static const struct v4l2_event src_ch = {
859937feeedSHans Verkuil 			.type = V4L2_EVENT_SOURCE_CHANGE,
860937feeedSHans Verkuil 			.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
861937feeedSHans Verkuil 		};
862937feeedSHans Verkuil 
863937feeedSHans Verkuil 		v4l2_subdev_notify_event(&state->sd, &src_ch);
864937feeedSHans Verkuil 	}
865cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
866cb7a01acSMauro Carvalho Chehab 
867cb7a01acSMauro Carvalho Chehab 	return IRQ_HANDLED;
868cb7a01acSMauro Carvalho Chehab }
869cb7a01acSMauro Carvalho Chehab 
870f5dde49bSLars-Peter Clausen static int adv7180_init(struct adv7180_state *state)
871f5dde49bSLars-Peter Clausen {
872f5dde49bSLars-Peter Clausen 	int ret;
873f5dde49bSLars-Peter Clausen 
874f5dde49bSLars-Peter Clausen 	/* ITU-R BT.656-4 compatible */
875f5dde49bSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
876f5dde49bSLars-Peter Clausen 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
877f5dde49bSLars-Peter Clausen 	if (ret < 0)
878f5dde49bSLars-Peter Clausen 		return ret;
879f5dde49bSLars-Peter Clausen 
880f5dde49bSLars-Peter Clausen 	/* Manually set V bit end position in NTSC mode */
881f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
882f5dde49bSLars-Peter Clausen 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
883f5dde49bSLars-Peter Clausen }
884f5dde49bSLars-Peter Clausen 
885f5dde49bSLars-Peter Clausen static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
886f5dde49bSLars-Peter Clausen {
887f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
888f5dde49bSLars-Peter Clausen 		(std << 4) | state->input);
889f5dde49bSLars-Peter Clausen }
890f5dde49bSLars-Peter Clausen 
891f5dde49bSLars-Peter Clausen static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
892f5dde49bSLars-Peter Clausen {
893f5dde49bSLars-Peter Clausen 	int ret;
894f5dde49bSLars-Peter Clausen 
895f5dde49bSLars-Peter Clausen 	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
896f5dde49bSLars-Peter Clausen 	if (ret < 0)
897f5dde49bSLars-Peter Clausen 		return ret;
898f5dde49bSLars-Peter Clausen 
899f5dde49bSLars-Peter Clausen 	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
900f5dde49bSLars-Peter Clausen 	ret |= input;
901f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
902f5dde49bSLars-Peter Clausen }
903f5dde49bSLars-Peter Clausen 
904c5ef8f8cSLars-Peter Clausen static int adv7182_init(struct adv7180_state *state)
905c5ef8f8cSLars-Peter Clausen {
906b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
907b37135e3SLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
908b37135e3SLars-Peter Clausen 			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
909b37135e3SLars-Peter Clausen 
910851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
911851a54efSLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
912851a54efSLars-Peter Clausen 			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
913851a54efSLars-Peter Clausen 
914bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2) {
915bf7dcb80SLars-Peter Clausen 		/* ADI recommended writes for improved video quality */
916bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0080, 0x51);
917bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0081, 0x51);
918bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0082, 0x68);
919bf7dcb80SLars-Peter Clausen 	}
920bf7dcb80SLars-Peter Clausen 
921c5ef8f8cSLars-Peter Clausen 	/* ADI required writes */
922b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
923ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x4e);
924ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57);
925ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0);
926b37135e3SLars-Peter Clausen 	} else {
927b37135e3SLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_V2)
928ce5d6290SSteve Longerbeam 			adv7180_write(state,
929ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
930ce5d6290SSteve Longerbeam 				      0x17);
931b37135e3SLars-Peter Clausen 		else
932ce5d6290SSteve Longerbeam 			adv7180_write(state,
933ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
934ce5d6290SSteve Longerbeam 				      0x07);
935ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x0c);
936ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0x40);
937b37135e3SLars-Peter Clausen 	}
938b37135e3SLars-Peter Clausen 
939b37135e3SLars-Peter Clausen 	adv7180_write(state, 0x0013, 0x00);
940c5ef8f8cSLars-Peter Clausen 
941c5ef8f8cSLars-Peter Clausen 	return 0;
942c5ef8f8cSLars-Peter Clausen }
943c5ef8f8cSLars-Peter Clausen 
944c5ef8f8cSLars-Peter Clausen static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
945c5ef8f8cSLars-Peter Clausen {
946c5ef8f8cSLars-Peter Clausen 	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
947c5ef8f8cSLars-Peter Clausen }
948c5ef8f8cSLars-Peter Clausen 
949c5ef8f8cSLars-Peter Clausen enum adv7182_input_type {
950c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_CVBS,
951c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_DIFF_CVBS,
952c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_SVIDEO,
953c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_YPBPR,
954c5ef8f8cSLars-Peter Clausen };
955c5ef8f8cSLars-Peter Clausen 
956c5ef8f8cSLars-Peter Clausen static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
957c5ef8f8cSLars-Peter Clausen {
958c5ef8f8cSLars-Peter Clausen 	switch (input) {
959c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN1:
960c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN2:
961c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN3:
962c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN4:
963c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN5:
964c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN6:
965c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN7:
966c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN8:
967c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_CVBS;
968c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
969c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
970c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
971c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
972c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_SVIDEO;
973c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
974c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
975c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_YPBPR;
976c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
977c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
978c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
979c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
980c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_DIFF_CVBS;
981c5ef8f8cSLars-Peter Clausen 	default: /* Will never happen */
982c5ef8f8cSLars-Peter Clausen 		return 0;
983c5ef8f8cSLars-Peter Clausen 	}
984c5ef8f8cSLars-Peter Clausen }
985c5ef8f8cSLars-Peter Clausen 
986c5ef8f8cSLars-Peter Clausen /* ADI recommended writes to registers 0x52, 0x53, 0x54 */
987c5ef8f8cSLars-Peter Clausen static unsigned int adv7182_lbias_settings[][3] = {
988c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
989c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
990c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
991c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
992c5ef8f8cSLars-Peter Clausen };
993c5ef8f8cSLars-Peter Clausen 
994bf7dcb80SLars-Peter Clausen static unsigned int adv7280_lbias_settings[][3] = {
995bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
996bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
997bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
998bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
999bf7dcb80SLars-Peter Clausen };
1000bf7dcb80SLars-Peter Clausen 
1001c5ef8f8cSLars-Peter Clausen static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
1002c5ef8f8cSLars-Peter Clausen {
1003c5ef8f8cSLars-Peter Clausen 	enum adv7182_input_type input_type;
1004c5ef8f8cSLars-Peter Clausen 	unsigned int *lbias;
1005c5ef8f8cSLars-Peter Clausen 	unsigned int i;
1006c5ef8f8cSLars-Peter Clausen 	int ret;
1007c5ef8f8cSLars-Peter Clausen 
1008c5ef8f8cSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
1009c5ef8f8cSLars-Peter Clausen 	if (ret)
1010c5ef8f8cSLars-Peter Clausen 		return ret;
1011c5ef8f8cSLars-Peter Clausen 
1012c5ef8f8cSLars-Peter Clausen 	/* Reset clamp circuitry - ADI recommended writes */
1013ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0x00);
1014ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0xff);
1015c5ef8f8cSLars-Peter Clausen 
1016c5ef8f8cSLars-Peter Clausen 	input_type = adv7182_get_input_type(input);
1017c5ef8f8cSLars-Peter Clausen 
1018c5ef8f8cSLars-Peter Clausen 	switch (input_type) {
1019c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_CVBS:
1020c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_DIFF_CVBS:
1021c5ef8f8cSLars-Peter Clausen 		/* ADI recommends to use the SH1 filter */
1022ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x41);
1023c5ef8f8cSLars-Peter Clausen 		break;
1024c5ef8f8cSLars-Peter Clausen 	default:
1025ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x01);
1026c5ef8f8cSLars-Peter Clausen 		break;
1027c5ef8f8cSLars-Peter Clausen 	}
1028c5ef8f8cSLars-Peter Clausen 
1029bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2)
1030bf7dcb80SLars-Peter Clausen 		lbias = adv7280_lbias_settings[input_type];
1031bf7dcb80SLars-Peter Clausen 	else
1032c5ef8f8cSLars-Peter Clausen 		lbias = adv7182_lbias_settings[input_type];
1033c5ef8f8cSLars-Peter Clausen 
1034c5ef8f8cSLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
1035ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CVBS_TRIM + i, lbias[i]);
1036c5ef8f8cSLars-Peter Clausen 
1037c5ef8f8cSLars-Peter Clausen 	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
1038c5ef8f8cSLars-Peter Clausen 		/* ADI required writes to make differential CVBS work */
1039ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xa8);
1040ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0x90);
1041ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0xb0);
1042ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x08);
1043ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0xa0);
1044c5ef8f8cSLars-Peter Clausen 	} else {
1045ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xf0);
1046ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0xd0);
1047ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0x10);
1048ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x9c);
1049ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0x00);
1050c5ef8f8cSLars-Peter Clausen 	}
1051c5ef8f8cSLars-Peter Clausen 
1052c5ef8f8cSLars-Peter Clausen 	return 0;
1053c5ef8f8cSLars-Peter Clausen }
1054c5ef8f8cSLars-Peter Clausen 
1055f5dde49bSLars-Peter Clausen static const struct adv7180_chip_info adv7180_info = {
1056f5dde49bSLars-Peter Clausen 	.flags = ADV7180_FLAG_RESET_POWERED,
1057f5dde49bSLars-Peter Clausen 	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
1058f5dde49bSLars-Peter Clausen 	 * all inputs and let the card driver take care of validation
1059f5dde49bSLars-Peter Clausen 	 */
1060f5dde49bSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
1061f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN2) |
1062f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN3) |
1063f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN4) |
1064f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN5) |
1065f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN6) |
1066f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
1067f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
1068f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
1069f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1070f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
1071f5dde49bSLars-Peter Clausen 	.init = adv7180_init,
1072f5dde49bSLars-Peter Clausen 	.set_std = adv7180_set_std,
1073f5dde49bSLars-Peter Clausen 	.select_input = adv7180_select_input,
1074f5dde49bSLars-Peter Clausen };
1075f5dde49bSLars-Peter Clausen 
1076c5ef8f8cSLars-Peter Clausen static const struct adv7180_chip_info adv7182_info = {
1077c5ef8f8cSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1078c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1079c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1080c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1081c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1082c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1083c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1084c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1085c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
1086c5ef8f8cSLars-Peter Clausen 	.init = adv7182_init,
1087c5ef8f8cSLars-Peter Clausen 	.set_std = adv7182_set_std,
1088c5ef8f8cSLars-Peter Clausen 	.select_input = adv7182_select_input,
1089c5ef8f8cSLars-Peter Clausen };
1090c5ef8f8cSLars-Peter Clausen 
1091bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7280_info = {
1092851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1093bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1094bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1095bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1096bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1097bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1098bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1099bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
1100bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1101bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1102bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1103bf7dcb80SLars-Peter Clausen };
1104bf7dcb80SLars-Peter Clausen 
1105b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7280_m_info = {
1106851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1107b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1108b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1109b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1110b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1111b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1112b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1113b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1114b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1115b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1116b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1117b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1118b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1119b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1120b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
1121b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1122b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1123b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1124b37135e3SLars-Peter Clausen };
1125b37135e3SLars-Peter Clausen 
1126bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7281_info = {
1127b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1128bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1129bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1130bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1131bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1132bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1133bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1134bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1135bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1136bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1137bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1138bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1139bf7dcb80SLars-Peter Clausen };
1140bf7dcb80SLars-Peter Clausen 
1141b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_m_info = {
1142b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1143b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1144b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1145b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1146b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1147b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1148b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1149b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1150b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1151b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1152b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1153b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1154b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1155b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1156b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1157b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1158b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1159b37135e3SLars-Peter Clausen };
1160b37135e3SLars-Peter Clausen 
1161b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_ma_info = {
1162b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1163b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1164b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1165b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1166b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1167b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1168b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1169b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1170b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1171b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1172b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1173b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1174b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1175b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1176b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
1177b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1178b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1179b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
1180b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1181b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1182b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1183b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1184b37135e3SLars-Peter Clausen };
1185b37135e3SLars-Peter Clausen 
1186851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_info = {
1187851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1188851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1189851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1190851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1191851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1192851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1193851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1194851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1195851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1196851a54efSLars-Peter Clausen 	.init = adv7182_init,
1197851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1198851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1199851a54efSLars-Peter Clausen };
1200851a54efSLars-Peter Clausen 
1201851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_m_info = {
1202851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1203851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1204851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1205851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1206851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1207851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1208851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1209851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1210851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1211851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1212851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1213851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1214851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1215851a54efSLars-Peter Clausen 	.init = adv7182_init,
1216851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1217851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1218851a54efSLars-Peter Clausen };
1219851a54efSLars-Peter Clausen 
12203999e5d0SLars-Peter Clausen static int init_device(struct adv7180_state *state)
1221cb7a01acSMauro Carvalho Chehab {
1222cb7a01acSMauro Carvalho Chehab 	int ret;
1223cb7a01acSMauro Carvalho Chehab 
12243999e5d0SLars-Peter Clausen 	mutex_lock(&state->mutex);
12253999e5d0SLars-Peter Clausen 
122665d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, true);
122765d9e14aSSteve Longerbeam 
1228c18818e9SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
122916dfe72fSUlrich Hecht 	usleep_range(5000, 10000);
1230c18818e9SLars-Peter Clausen 
1231f5dde49bSLars-Peter Clausen 	ret = state->chip_info->init(state);
12323e35e33cSLars-Peter Clausen 	if (ret)
12333999e5d0SLars-Peter Clausen 		goto out_unlock;
1234cb7a01acSMauro Carvalho Chehab 
1235f5dde49bSLars-Peter Clausen 	ret = adv7180_program_std(state);
1236f5dde49bSLars-Peter Clausen 	if (ret)
12373999e5d0SLars-Peter Clausen 		goto out_unlock;
1238cb7a01acSMauro Carvalho Chehab 
1239851a54efSLars-Peter Clausen 	adv7180_set_field_mode(state);
1240851a54efSLars-Peter Clausen 
1241cb7a01acSMauro Carvalho Chehab 	/* register for interrupts */
1242cb7a01acSMauro Carvalho Chehab 	if (state->irq > 0) {
1243cb7a01acSMauro Carvalho Chehab 		/* config the Interrupt pin to be active low */
12443999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_ICONF1,
1245cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_ACTIVE_LOW |
1246cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_PSYNC_ONLY);
1247cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12483999e5d0SLars-Peter Clausen 			goto out_unlock;
1249cb7a01acSMauro Carvalho Chehab 
12503999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
1251cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12523999e5d0SLars-Peter Clausen 			goto out_unlock;
1253cb7a01acSMauro Carvalho Chehab 
12543999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
1255cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12563999e5d0SLars-Peter Clausen 			goto out_unlock;
1257cb7a01acSMauro Carvalho Chehab 
1258cb7a01acSMauro Carvalho Chehab 		/* enable AD change interrupts interrupts */
12593999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR3,
1260cb7a01acSMauro Carvalho Chehab 						ADV7180_IRQ3_AD_CHANGE);
1261cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12623999e5d0SLars-Peter Clausen 			goto out_unlock;
1263cb7a01acSMauro Carvalho Chehab 
12643999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
1265cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
12663999e5d0SLars-Peter Clausen 			goto out_unlock;
1267cb7a01acSMauro Carvalho Chehab 	}
1268cb7a01acSMauro Carvalho Chehab 
12693999e5d0SLars-Peter Clausen out_unlock:
12703999e5d0SLars-Peter Clausen 	mutex_unlock(&state->mutex);
1271df065b37SAlexey Khoroshilov 
1272df065b37SAlexey Khoroshilov 	return ret;
1273cb7a01acSMauro Carvalho Chehab }
1274cb7a01acSMauro Carvalho Chehab 
12754c62e976SGreg Kroah-Hartman static int adv7180_probe(struct i2c_client *client,
1276cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1277cb7a01acSMauro Carvalho Chehab {
1278cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state;
1279cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1280cb7a01acSMauro Carvalho Chehab 	int ret;
1281cb7a01acSMauro Carvalho Chehab 
1282cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1283cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
1284cb7a01acSMauro Carvalho Chehab 		return -EIO;
1285cb7a01acSMauro Carvalho Chehab 
1286cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1287cb7a01acSMauro Carvalho Chehab 		 client->addr, client->adapter->name);
1288cb7a01acSMauro Carvalho Chehab 
1289c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
12907657e064SFabio Estevam 	if (state == NULL)
12917657e064SFabio Estevam 		return -ENOMEM;
1292cb7a01acSMauro Carvalho Chehab 
12933999e5d0SLars-Peter Clausen 	state->client = client;
1294851a54efSLars-Peter Clausen 	state->field = V4L2_FIELD_INTERLACED;
1295f5dde49bSLars-Peter Clausen 	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
12963999e5d0SLars-Peter Clausen 
129765d9e14aSSteve Longerbeam 	state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
129865d9e14aSSteve Longerbeam 						   GPIOD_OUT_HIGH);
129965d9e14aSSteve Longerbeam 	if (IS_ERR(state->pwdn_gpio)) {
130065d9e14aSSteve Longerbeam 		ret = PTR_ERR(state->pwdn_gpio);
130165d9e14aSSteve Longerbeam 		v4l_err(client, "request for power pin failed: %d\n", ret);
130265d9e14aSSteve Longerbeam 		return ret;
130365d9e14aSSteve Longerbeam 	}
130465d9e14aSSteve Longerbeam 
1305b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
1306b37135e3SLars-Peter Clausen 		state->csi_client = i2c_new_dummy(client->adapter,
1307b37135e3SLars-Peter Clausen 				ADV7180_DEFAULT_CSI_I2C_ADDR);
1308b37135e3SLars-Peter Clausen 		if (!state->csi_client)
1309b37135e3SLars-Peter Clausen 			return -ENOMEM;
1310b37135e3SLars-Peter Clausen 	}
1311b37135e3SLars-Peter Clausen 
1312851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
1313851a54efSLars-Peter Clausen 		state->vpp_client = i2c_new_dummy(client->adapter,
1314851a54efSLars-Peter Clausen 				ADV7180_DEFAULT_VPP_I2C_ADDR);
1315851a54efSLars-Peter Clausen 		if (!state->vpp_client) {
1316851a54efSLars-Peter Clausen 			ret = -ENOMEM;
1317851a54efSLars-Peter Clausen 			goto err_unregister_csi_client;
1318851a54efSLars-Peter Clausen 		}
1319851a54efSLars-Peter Clausen 	}
1320851a54efSLars-Peter Clausen 
1321cb7a01acSMauro Carvalho Chehab 	state->irq = client->irq;
1322cb7a01acSMauro Carvalho Chehab 	mutex_init(&state->mutex);
1323937feeedSHans Verkuil 	state->curr_norm = V4L2_STD_NTSC;
1324f5dde49bSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
1325e246c333SLars-Peter Clausen 		state->powered = true;
1326f5dde49bSLars-Peter Clausen 	else
1327f5dde49bSLars-Peter Clausen 		state->powered = false;
1328cb7a01acSMauro Carvalho Chehab 	state->input = 0;
1329cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
1330cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
13315cc72c47SAkinobu Mita 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
1332cb7a01acSMauro Carvalho Chehab 
1333cb7a01acSMauro Carvalho Chehab 	ret = adv7180_init_controls(state);
1334cb7a01acSMauro Carvalho Chehab 	if (ret)
1335851a54efSLars-Peter Clausen 		goto err_unregister_vpp_client;
1336d5d51a82SLars-Peter Clausen 
1337d5d51a82SLars-Peter Clausen 	state->pad.flags = MEDIA_PAD_FL_SOURCE;
1338ca0fa5f0SHans Verkuil 	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
1339ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
1340cb7a01acSMauro Carvalho Chehab 	if (ret)
1341cb7a01acSMauro Carvalho Chehab 		goto err_free_ctrl;
1342fa5b7945SLars-Peter Clausen 
1343d5d51a82SLars-Peter Clausen 	ret = init_device(state);
1344d5d51a82SLars-Peter Clausen 	if (ret)
1345d5d51a82SLars-Peter Clausen 		goto err_media_entity_cleanup;
1346d5d51a82SLars-Peter Clausen 
1347fa5721d1SLars-Peter Clausen 	if (state->irq) {
1348fa5721d1SLars-Peter Clausen 		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
1349f3e991d4SLars-Peter Clausen 					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
1350f3e991d4SLars-Peter Clausen 					   KBUILD_MODNAME, state);
1351fa5721d1SLars-Peter Clausen 		if (ret)
1352d5d51a82SLars-Peter Clausen 			goto err_media_entity_cleanup;
1353fa5721d1SLars-Peter Clausen 	}
1354fa5721d1SLars-Peter Clausen 
1355fa5b7945SLars-Peter Clausen 	ret = v4l2_async_register_subdev(sd);
1356fa5b7945SLars-Peter Clausen 	if (ret)
1357fa5b7945SLars-Peter Clausen 		goto err_free_irq;
1358fa5b7945SLars-Peter Clausen 
1359cb7a01acSMauro Carvalho Chehab 	return 0;
1360cb7a01acSMauro Carvalho Chehab 
1361fa5b7945SLars-Peter Clausen err_free_irq:
1362fa5b7945SLars-Peter Clausen 	if (state->irq > 0)
1363fa5b7945SLars-Peter Clausen 		free_irq(client->irq, state);
1364d5d51a82SLars-Peter Clausen err_media_entity_cleanup:
1365d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1366cb7a01acSMauro Carvalho Chehab err_free_ctrl:
1367cb7a01acSMauro Carvalho Chehab 	adv7180_exit_controls(state);
1368851a54efSLars-Peter Clausen err_unregister_vpp_client:
1369851a54efSLars-Peter Clausen 	i2c_unregister_device(state->vpp_client);
1370b37135e3SLars-Peter Clausen err_unregister_csi_client:
1371b37135e3SLars-Peter Clausen 	i2c_unregister_device(state->csi_client);
1372297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1373cb7a01acSMauro Carvalho Chehab 	return ret;
1374cb7a01acSMauro Carvalho Chehab }
1375cb7a01acSMauro Carvalho Chehab 
13764c62e976SGreg Kroah-Hartman static int adv7180_remove(struct i2c_client *client)
1377cb7a01acSMauro Carvalho Chehab {
1378cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1379cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1380cb7a01acSMauro Carvalho Chehab 
1381fa5b7945SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
1382fa5b7945SLars-Peter Clausen 
13830c25534dSLars-Peter Clausen 	if (state->irq > 0)
1384cb7a01acSMauro Carvalho Chehab 		free_irq(client->irq, state);
1385cb7a01acSMauro Carvalho Chehab 
1386d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1387b13f4af2SLars-Peter Clausen 	adv7180_exit_controls(state);
1388b37135e3SLars-Peter Clausen 
1389851a54efSLars-Peter Clausen 	i2c_unregister_device(state->vpp_client);
1390b37135e3SLars-Peter Clausen 	i2c_unregister_device(state->csi_client);
1391b37135e3SLars-Peter Clausen 
139265d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, false);
139365d9e14aSSteve Longerbeam 
1394297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1395b37135e3SLars-Peter Clausen 
1396cb7a01acSMauro Carvalho Chehab 	return 0;
1397cb7a01acSMauro Carvalho Chehab }
1398cb7a01acSMauro Carvalho Chehab 
1399cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = {
1400f5dde49bSLars-Peter Clausen 	{ "adv7180", (kernel_ulong_t)&adv7180_info },
1401281ddc3cSUlrich Hecht 	{ "adv7180cp", (kernel_ulong_t)&adv7180_info },
1402281ddc3cSUlrich Hecht 	{ "adv7180st", (kernel_ulong_t)&adv7180_info },
1403c5ef8f8cSLars-Peter Clausen 	{ "adv7182", (kernel_ulong_t)&adv7182_info },
1404bf7dcb80SLars-Peter Clausen 	{ "adv7280", (kernel_ulong_t)&adv7280_info },
1405b37135e3SLars-Peter Clausen 	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
1406bf7dcb80SLars-Peter Clausen 	{ "adv7281", (kernel_ulong_t)&adv7281_info },
1407b37135e3SLars-Peter Clausen 	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
1408b37135e3SLars-Peter Clausen 	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
1409851a54efSLars-Peter Clausen 	{ "adv7282", (kernel_ulong_t)&adv7282_info },
1410851a54efSLars-Peter Clausen 	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
1411cb7a01acSMauro Carvalho Chehab 	{},
1412cb7a01acSMauro Carvalho Chehab };
1413f5dde49bSLars-Peter Clausen MODULE_DEVICE_TABLE(i2c, adv7180_id);
1414cb7a01acSMauro Carvalho Chehab 
1415cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
1416cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev)
1417cb7a01acSMauro Carvalho Chehab {
1418cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
1419e246c333SLars-Peter Clausen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1420e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
1421cb7a01acSMauro Carvalho Chehab 
14223999e5d0SLars-Peter Clausen 	return adv7180_set_power(state, false);
1423cb7a01acSMauro Carvalho Chehab }
1424cb7a01acSMauro Carvalho Chehab 
1425cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev)
1426cb7a01acSMauro Carvalho Chehab {
1427cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
1428cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1429cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1430cb7a01acSMauro Carvalho Chehab 	int ret;
1431cb7a01acSMauro Carvalho Chehab 
14323999e5d0SLars-Peter Clausen 	ret = init_device(state);
1433cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1434cb7a01acSMauro Carvalho Chehab 		return ret;
1435c18818e9SLars-Peter Clausen 
1436c18818e9SLars-Peter Clausen 	ret = adv7180_set_power(state, state->powered);
1437c18818e9SLars-Peter Clausen 	if (ret)
1438c18818e9SLars-Peter Clausen 		return ret;
1439c18818e9SLars-Peter Clausen 
1440cb7a01acSMauro Carvalho Chehab 	return 0;
1441cb7a01acSMauro Carvalho Chehab }
1442cc1088dcSLars-Peter Clausen 
1443cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
1444cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops)
1445cc1088dcSLars-Peter Clausen 
1446cc1088dcSLars-Peter Clausen #else
1447cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL
1448cb7a01acSMauro Carvalho Chehab #endif
1449cb7a01acSMauro Carvalho Chehab 
1450250121d3SBen Dooks #ifdef CONFIG_OF
1451250121d3SBen Dooks static const struct of_device_id adv7180_of_id[] = {
1452250121d3SBen Dooks 	{ .compatible = "adi,adv7180", },
1453ce1ec5c0SUlrich Hecht 	{ .compatible = "adi,adv7180cp", },
1454ce1ec5c0SUlrich Hecht 	{ .compatible = "adi,adv7180st", },
1455bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7182", },
1456bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280", },
1457bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280-m", },
1458bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281", },
1459bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-m", },
1460bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-ma", },
1461bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282", },
1462bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282-m", },
1463250121d3SBen Dooks 	{ },
1464250121d3SBen Dooks };
1465250121d3SBen Dooks 
1466250121d3SBen Dooks MODULE_DEVICE_TABLE(of, adv7180_of_id);
1467250121d3SBen Dooks #endif
1468250121d3SBen Dooks 
1469cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = {
1470cb7a01acSMauro Carvalho Chehab 	.driver = {
1471cb7a01acSMauro Carvalho Chehab 		   .name = KBUILD_MODNAME,
1472cc1088dcSLars-Peter Clausen 		   .pm = ADV7180_PM_OPS,
1473250121d3SBen Dooks 		   .of_match_table = of_match_ptr(adv7180_of_id),
1474cb7a01acSMauro Carvalho Chehab 		   },
1475cb7a01acSMauro Carvalho Chehab 	.probe = adv7180_probe,
14764c62e976SGreg Kroah-Hartman 	.remove = adv7180_remove,
1477cb7a01acSMauro Carvalho Chehab 	.id_table = adv7180_id,
1478cb7a01acSMauro Carvalho Chehab };
1479cb7a01acSMauro Carvalho Chehab 
1480cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver);
1481cb7a01acSMauro Carvalho Chehab 
1482cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
1483cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories");
1484cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1485