xref: /openbmc/linux/drivers/media/i2c/adv7180.c (revision aaeb31c0)
1661521a8SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * adv7180.c Analog Devices ADV7180 video decoder driver
4cb7a01acSMauro Carvalho Chehab  * Copyright (c) 2009 Intel Corporation
5cccb83f7SVladimir Barinov  * Copyright (C) 2013 Cogent Embedded, Inc.
6cccb83f7SVladimir Barinov  * Copyright (C) 2013 Renesas Solutions Corp.
7cb7a01acSMauro Carvalho Chehab  */
8cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
9cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
10cb7a01acSMauro Carvalho Chehab #include <linux/errno.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/interrupt.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
15250121d3SBen Dooks #include <linux/of.h>
1665d9e14aSSteve Longerbeam #include <linux/gpio/consumer.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
18937feeedSHans Verkuil #include <media/v4l2-ioctl.h>
19937feeedSHans Verkuil #include <media/v4l2-event.h>
20cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
21cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h>
23c18818e9SLars-Peter Clausen #include <linux/delay.h>
24cb7a01acSMauro Carvalho Chehab 
25f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
26f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
27f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
28f5dde49bSLars-Peter Clausen #define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
29f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_J				0x4
30f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_M				0x5
31f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL60				0x6
32f5dde49bSLars-Peter Clausen #define ADV7180_STD_NTSC_443				0x7
33f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_BG				0x8
34f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_N				0x9
35f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M				0xa
36f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_M_PED				0xb
37f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N				0xc
38f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_COMB_N_PED			0xd
39f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM				0xe
40f5dde49bSLars-Peter Clausen #define ADV7180_STD_PAL_SECAM_PED			0xf
41f5dde49bSLars-Peter Clausen 
423999e5d0SLars-Peter Clausen #define ADV7180_REG_INPUT_CONTROL			0x0000
43cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
44cb7a01acSMauro Carvalho Chehab 
45c5ef8f8cSLars-Peter Clausen #define ADV7182_REG_INPUT_VIDSEL			0x0002
46db9edaafSBenjamin Marty #define ADV7182_REG_INPUT_RESERVED			BIT(2)
47c5ef8f8cSLars-Peter Clausen 
48ce5d6290SSteve Longerbeam #define ADV7180_REG_OUTPUT_CONTROL			0x0003
493999e5d0SLars-Peter Clausen #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
50cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
51cb7a01acSMauro Carvalho Chehab 
52ce5d6290SSteve Longerbeam #define ADV7180_REG_AUTODETECT_ENABLE			0x0007
53cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_DEFAULT			0x7f
54cb7a01acSMauro Carvalho Chehab /* Contrast */
553999e5d0SLars-Peter Clausen #define ADV7180_REG_CON		0x0008	/*Unsigned */
56cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MIN		0
57cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_DEF		128
58cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MAX		255
59cb7a01acSMauro Carvalho Chehab /* Brightness*/
603999e5d0SLars-Peter Clausen #define ADV7180_REG_BRI		0x000a	/*Signed */
61cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MIN		-128
62cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_DEF		0
63cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MAX		127
64cb7a01acSMauro Carvalho Chehab /* Hue */
653999e5d0SLars-Peter Clausen #define ADV7180_REG_HUE		0x000b	/*Signed, inverted */
66cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MIN		-127
67cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_DEF		0
68cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MAX		128
69cb7a01acSMauro Carvalho Chehab 
70a76c86f4SFabio Estevam #define ADV7180_REG_DEF_VALUE_Y	0x000c
71a76c86f4SFabio Estevam #define ADV7180_DEF_VAL_EN		0x1
72a76c86f4SFabio Estevam #define ADV7180_DEF_VAL_AUTO_EN	0x2
733999e5d0SLars-Peter Clausen #define ADV7180_REG_CTRL		0x000e
74029d6177SLars-Peter Clausen #define ADV7180_CTRL_IRQ_SPACE		0x20
75cb7a01acSMauro Carvalho Chehab 
76029d6177SLars-Peter Clausen #define ADV7180_REG_PWR_MAN		0x0f
77cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_ON		0x04
78cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_OFF		0x24
79cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_RES		0x80
80cb7a01acSMauro Carvalho Chehab 
813999e5d0SLars-Peter Clausen #define ADV7180_REG_STATUS1		0x0010
82cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_IN_LOCK		0x01
83cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_MASK	0x70
84cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
85cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
86cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_M	0x20
87cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_60	0x30
88cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_B_G	0x40
89cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM	0x50
90cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
91cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
92cb7a01acSMauro Carvalho Chehab 
933999e5d0SLars-Peter Clausen #define ADV7180_REG_IDENT 0x0011
94cb7a01acSMauro Carvalho Chehab #define ADV7180_ID_7180 0x18
95cb7a01acSMauro Carvalho Chehab 
96ce5d6290SSteve Longerbeam #define ADV7180_REG_STATUS3		0x0013
97ce5d6290SSteve Longerbeam #define ADV7180_REG_ANALOG_CLAMP_CTL	0x0014
98ce5d6290SSteve Longerbeam #define ADV7180_REG_SHAP_FILTER_CTL_1	0x0017
99ce5d6290SSteve Longerbeam #define ADV7180_REG_CTRL_2		0x001d
100ce5d6290SSteve Longerbeam #define ADV7180_REG_VSYNC_FIELD_CTL_1	0x0031
101ed771d75SMatthew Michilot #define ADV7180_VSYNC_FIELD_CTL_1_NEWAV 0x12
102ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_1	0x003d
103ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_2	0x003e
104ce5d6290SSteve Longerbeam #define ADV7180_REG_MANUAL_WIN_CTL_3	0x003f
105ce5d6290SSteve Longerbeam #define ADV7180_REG_LOCK_CNT		0x0051
106ce5d6290SSteve Longerbeam #define ADV7180_REG_CVBS_TRIM		0x0052
107ce5d6290SSteve Longerbeam #define ADV7180_REG_CLAMP_ADJ		0x005a
108ce5d6290SSteve Longerbeam #define ADV7180_REG_RES_CIR		0x005f
109ce5d6290SSteve Longerbeam #define ADV7180_REG_DIFF_MODE		0x0060
110ce5d6290SSteve Longerbeam 
11152e37f0aSSteve Longerbeam #define ADV7180_REG_ICONF1		0x2040
112cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW	0x01
113cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY	0x10
114cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
115cb7a01acSMauro Carvalho Chehab /* Saturation */
1163999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
1173999e5d0SLars-Peter Clausen #define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
118cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN		0
119cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF		128
120cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX		255
121cb7a01acSMauro Carvalho Chehab 
122cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK	0x01
123cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK	0x02
12452e37f0aSSteve Longerbeam #define ADV7180_REG_ISR1	0x2042
12552e37f0aSSteve Longerbeam #define ADV7180_REG_ICR1	0x2043
12652e37f0aSSteve Longerbeam #define ADV7180_REG_IMR1	0x2044
12752e37f0aSSteve Longerbeam #define ADV7180_REG_IMR2	0x2048
128cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE	0x08
12952e37f0aSSteve Longerbeam #define ADV7180_REG_ISR3	0x204A
13052e37f0aSSteve Longerbeam #define ADV7180_REG_ICR3	0x204B
13152e37f0aSSteve Longerbeam #define ADV7180_REG_IMR3	0x204C
13252e37f0aSSteve Longerbeam #define ADV7180_REG_IMR4	0x2050
133cb7a01acSMauro Carvalho Chehab 
1343999e5d0SLars-Peter Clausen #define ADV7180_REG_NTSC_V_BIT_END	0x00E6
135cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
136cb7a01acSMauro Carvalho Chehab 
137851a54efSLars-Peter Clausen #define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
138b37135e3SLars-Peter Clausen #define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
139b37135e3SLars-Peter Clausen 
140ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL1		0x4080
141ce5d6290SSteve Longerbeam #define ADV7180_REG_ACE_CTRL5		0x4084
14208b717c2SLars-Peter Clausen #define ADV7180_REG_FLCONTROL		0x40e0
14308b717c2SLars-Peter Clausen #define ADV7180_FLCONTROL_FL_ENABLE 0x1
14408b717c2SLars-Peter Clausen 
145ce5d6290SSteve Longerbeam #define ADV7180_REG_RST_CLAMP	0x809c
146ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ1	0x80b6
147ce5d6290SSteve Longerbeam #define ADV7180_REG_AGC_ADJ2	0x80c0
148ce5d6290SSteve Longerbeam 
149b37135e3SLars-Peter Clausen #define ADV7180_CSI_REG_PWRDN	0x00
150b37135e3SLars-Peter Clausen #define ADV7180_CSI_PWRDN	0x80
151b37135e3SLars-Peter Clausen 
152f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN1 0x00
153f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN2 0x01
154f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN3 0x02
155f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN4 0x03
156f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN5 0x04
157f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_CVBS_AIN6 0x05
158f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
159f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
160f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
161f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
162f5dde49bSLars-Peter Clausen #define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
163f5dde49bSLars-Peter Clausen 
164c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN1 0x00
165c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN2 0x01
166c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN3 0x02
167c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN4 0x03
168c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN5 0x04
169c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN6 0x05
170c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN7 0x06
171c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_CVBS_AIN8 0x07
172c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
173c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
174c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
175c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
176c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
177c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
178c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
179c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
180c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
181c5ef8f8cSLars-Peter Clausen #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
182c5ef8f8cSLars-Peter Clausen 
183b37135e3SLars-Peter Clausen #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
184851a54efSLars-Peter Clausen #define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
185b37135e3SLars-Peter Clausen 
18608b717c2SLars-Peter Clausen #define V4L2_CID_ADV_FAST_SWITCH	(V4L2_CID_USER_ADV7180_BASE + 0x00)
18708b717c2SLars-Peter Clausen 
1889483a3f8STim Harvey /* Initial number of frames to skip to avoid possible garbage */
1899483a3f8STim Harvey #define ADV7180_NUM_OF_SKIP_FRAMES       2
1909483a3f8STim Harvey 
191f5dde49bSLars-Peter Clausen struct adv7180_state;
192f5dde49bSLars-Peter Clausen 
193f5dde49bSLars-Peter Clausen #define ADV7180_FLAG_RESET_POWERED	BIT(0)
194bf7dcb80SLars-Peter Clausen #define ADV7180_FLAG_V2			BIT(1)
195b37135e3SLars-Peter Clausen #define ADV7180_FLAG_MIPI_CSI2		BIT(2)
196851a54efSLars-Peter Clausen #define ADV7180_FLAG_I2P		BIT(3)
197f5dde49bSLars-Peter Clausen 
198f5dde49bSLars-Peter Clausen struct adv7180_chip_info {
199f5dde49bSLars-Peter Clausen 	unsigned int flags;
200f5dde49bSLars-Peter Clausen 	unsigned int valid_input_mask;
201f5dde49bSLars-Peter Clausen 	int (*set_std)(struct adv7180_state *st, unsigned int std);
202f5dde49bSLars-Peter Clausen 	int (*select_input)(struct adv7180_state *st, unsigned int input);
203f5dde49bSLars-Peter Clausen 	int (*init)(struct adv7180_state *state);
204f5dde49bSLars-Peter Clausen };
205f5dde49bSLars-Peter Clausen 
206cb7a01acSMauro Carvalho Chehab struct adv7180_state {
207cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;
208cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev	sd;
209d5d51a82SLars-Peter Clausen 	struct media_pad	pad;
210cb7a01acSMauro Carvalho Chehab 	struct mutex		mutex; /* mutual excl. when accessing chip */
211cb7a01acSMauro Carvalho Chehab 	int			irq;
21265d9e14aSSteve Longerbeam 	struct gpio_desc	*pwdn_gpio;
213abb7c7c2SFrieder Schrempf 	struct gpio_desc	*rst_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;
225ed771d75SMatthew Michilot 	bool			force_bt656_4;
226cb7a01acSMauro Carvalho Chehab };
227cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
228cb7a01acSMauro Carvalho Chehab 					    struct adv7180_state,	\
229cb7a01acSMauro Carvalho Chehab 					    ctrl_hdl)->sd)
230cb7a01acSMauro Carvalho Chehab 
adv7180_select_page(struct adv7180_state * state,unsigned int page)2313999e5d0SLars-Peter Clausen static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
2323999e5d0SLars-Peter Clausen {
2333999e5d0SLars-Peter Clausen 	if (state->register_page != page) {
2343999e5d0SLars-Peter Clausen 		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
2353999e5d0SLars-Peter Clausen 			page);
2363999e5d0SLars-Peter Clausen 		state->register_page = page;
2373999e5d0SLars-Peter Clausen 	}
2383999e5d0SLars-Peter Clausen 
2393999e5d0SLars-Peter Clausen 	return 0;
2403999e5d0SLars-Peter Clausen }
2413999e5d0SLars-Peter Clausen 
adv7180_write(struct adv7180_state * state,unsigned int reg,unsigned int value)2423999e5d0SLars-Peter Clausen static int adv7180_write(struct adv7180_state *state, unsigned int reg,
2433999e5d0SLars-Peter Clausen 	unsigned int value)
2443999e5d0SLars-Peter Clausen {
2453999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2463999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2473999e5d0SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
2483999e5d0SLars-Peter Clausen }
2493999e5d0SLars-Peter Clausen 
adv7180_read(struct adv7180_state * state,unsigned int reg)2503999e5d0SLars-Peter Clausen static int adv7180_read(struct adv7180_state *state, unsigned int reg)
2513999e5d0SLars-Peter Clausen {
2523999e5d0SLars-Peter Clausen 	lockdep_assert_held(&state->mutex);
2533999e5d0SLars-Peter Clausen 	adv7180_select_page(state, reg >> 8);
2543999e5d0SLars-Peter Clausen 	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
2553999e5d0SLars-Peter Clausen }
2563999e5d0SLars-Peter Clausen 
adv7180_csi_write(struct adv7180_state * state,unsigned int reg,unsigned int value)257b37135e3SLars-Peter Clausen static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
258b37135e3SLars-Peter Clausen 	unsigned int value)
259b37135e3SLars-Peter Clausen {
260b37135e3SLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
261b37135e3SLars-Peter Clausen }
262b37135e3SLars-Peter Clausen 
adv7180_set_video_standard(struct adv7180_state * state,unsigned int std)263f5dde49bSLars-Peter Clausen static int adv7180_set_video_standard(struct adv7180_state *state,
264f5dde49bSLars-Peter Clausen 	unsigned int std)
265f5dde49bSLars-Peter Clausen {
266f5dde49bSLars-Peter Clausen 	return state->chip_info->set_std(state, std);
267f5dde49bSLars-Peter Clausen }
2683999e5d0SLars-Peter Clausen 
adv7180_vpp_write(struct adv7180_state * state,unsigned int reg,unsigned int value)269851a54efSLars-Peter Clausen static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
270851a54efSLars-Peter Clausen 	unsigned int value)
271851a54efSLars-Peter Clausen {
272851a54efSLars-Peter Clausen 	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
273851a54efSLars-Peter Clausen }
274851a54efSLars-Peter Clausen 
adv7180_std_to_v4l2(u8 status1)275cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
276cb7a01acSMauro Carvalho Chehab {
277b294a192SVladimir Barinov 	/* in case V4L2_IN_ST_NO_SIGNAL */
278b294a192SVladimir Barinov 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
279b294a192SVladimir Barinov 		return V4L2_STD_UNKNOWN;
280b294a192SVladimir Barinov 
281cb7a01acSMauro Carvalho Chehab 	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
282cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
283cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
284cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
285cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
286cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_M:
287cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
288cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_60:
289cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_60;
290cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_B_G:
291cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
292cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM:
293cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
294cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_COMB:
295cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
296cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM_525:
297cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
298cb7a01acSMauro Carvalho Chehab 	default:
299cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
300cb7a01acSMauro Carvalho Chehab 	}
301cb7a01acSMauro Carvalho Chehab }
302cb7a01acSMauro Carvalho Chehab 
v4l2_std_to_adv7180(v4l2_std_id std)303cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std)
304cb7a01acSMauro Carvalho Chehab {
305cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
306f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL60;
307cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_NTSC_443)
308f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_443;
309cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_N)
310f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_N;
311cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_M)
312f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_M;
313cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_Nc)
314f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_COMB_N;
315cb7a01acSMauro Carvalho Chehab 
316cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_PAL)
317f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_BG;
318cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC)
319f5dde49bSLars-Peter Clausen 		return ADV7180_STD_NTSC_M;
320cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_SECAM)
321f5dde49bSLars-Peter Clausen 		return ADV7180_STD_PAL_SECAM;
322cb7a01acSMauro Carvalho Chehab 
323cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
324cb7a01acSMauro Carvalho Chehab }
325cb7a01acSMauro Carvalho Chehab 
adv7180_status_to_v4l2(u8 status1)326cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1)
327cb7a01acSMauro Carvalho Chehab {
328cb7a01acSMauro Carvalho Chehab 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
329cb7a01acSMauro Carvalho Chehab 		return V4L2_IN_ST_NO_SIGNAL;
330cb7a01acSMauro Carvalho Chehab 
331cb7a01acSMauro Carvalho Chehab 	return 0;
332cb7a01acSMauro Carvalho Chehab }
333cb7a01acSMauro Carvalho Chehab 
__adv7180_status(struct adv7180_state * state,u32 * status,v4l2_std_id * std)3343999e5d0SLars-Peter Clausen static int __adv7180_status(struct adv7180_state *state, u32 *status,
335cb7a01acSMauro Carvalho Chehab 			    v4l2_std_id *std)
336cb7a01acSMauro Carvalho Chehab {
3373999e5d0SLars-Peter Clausen 	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
338cb7a01acSMauro Carvalho Chehab 
339cb7a01acSMauro Carvalho Chehab 	if (status1 < 0)
340cb7a01acSMauro Carvalho Chehab 		return status1;
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab 	if (status)
343cb7a01acSMauro Carvalho Chehab 		*status = adv7180_status_to_v4l2(status1);
344cb7a01acSMauro Carvalho Chehab 	if (std)
345cb7a01acSMauro Carvalho Chehab 		*std = adv7180_std_to_v4l2(status1);
346cb7a01acSMauro Carvalho Chehab 
347cb7a01acSMauro Carvalho Chehab 	return 0;
348cb7a01acSMauro Carvalho Chehab }
349cb7a01acSMauro Carvalho Chehab 
to_state(struct v4l2_subdev * sd)350cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
351cb7a01acSMauro Carvalho Chehab {
352cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7180_state, sd);
353cb7a01acSMauro Carvalho Chehab }
354cb7a01acSMauro Carvalho Chehab 
adv7180_querystd(struct v4l2_subdev * sd,v4l2_std_id * std)355cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
356cb7a01acSMauro Carvalho Chehab {
357cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
358cb7a01acSMauro Carvalho Chehab 	int err = mutex_lock_interruptible(&state->mutex);
359cb7a01acSMauro Carvalho Chehab 	if (err)
360cb7a01acSMauro Carvalho Chehab 		return err;
361cb7a01acSMauro Carvalho Chehab 
362937feeedSHans Verkuil 	if (state->streaming) {
363937feeedSHans Verkuil 		err = -EBUSY;
364937feeedSHans Verkuil 		goto unlock;
365937feeedSHans Verkuil 	}
366cb7a01acSMauro Carvalho Chehab 
367937feeedSHans Verkuil 	err = adv7180_set_video_standard(state,
368937feeedSHans Verkuil 			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
369937feeedSHans Verkuil 	if (err)
370937feeedSHans Verkuil 		goto unlock;
371937feeedSHans Verkuil 
372937feeedSHans Verkuil 	msleep(100);
373937feeedSHans Verkuil 	__adv7180_status(state, NULL, std);
374937feeedSHans Verkuil 
375937feeedSHans Verkuil 	err = v4l2_std_to_adv7180(state->curr_norm);
376937feeedSHans Verkuil 	if (err < 0)
377937feeedSHans Verkuil 		goto unlock;
378937feeedSHans Verkuil 
379937feeedSHans Verkuil 	err = adv7180_set_video_standard(state, err);
380937feeedSHans Verkuil 
381937feeedSHans Verkuil unlock:
382cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
383cb7a01acSMauro Carvalho Chehab 	return err;
384cb7a01acSMauro Carvalho Chehab }
385cb7a01acSMauro Carvalho Chehab 
adv7180_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)386cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
387cb7a01acSMauro Carvalho Chehab 			     u32 output, u32 config)
388cb7a01acSMauro Carvalho Chehab {
389cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
390cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
391cb7a01acSMauro Carvalho Chehab 
392cb7a01acSMauro Carvalho Chehab 	if (ret)
393cb7a01acSMauro Carvalho Chehab 		return ret;
394cb7a01acSMauro Carvalho Chehab 
395f5dde49bSLars-Peter Clausen 	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
396f5dde49bSLars-Peter Clausen 		ret = -EINVAL;
397cb7a01acSMauro Carvalho Chehab 		goto out;
398f5dde49bSLars-Peter Clausen 	}
399cb7a01acSMauro Carvalho Chehab 
400f5dde49bSLars-Peter Clausen 	ret = state->chip_info->select_input(state, input);
401cb7a01acSMauro Carvalho Chehab 
402f5dde49bSLars-Peter Clausen 	if (ret == 0)
403cb7a01acSMauro Carvalho Chehab 		state->input = input;
404cb7a01acSMauro Carvalho Chehab out:
405cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
406cb7a01acSMauro Carvalho Chehab 	return ret;
407cb7a01acSMauro Carvalho Chehab }
408cb7a01acSMauro Carvalho Chehab 
adv7180_g_input_status(struct v4l2_subdev * sd,u32 * status)409cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
410cb7a01acSMauro Carvalho Chehab {
411cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
412cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
413cb7a01acSMauro Carvalho Chehab 	if (ret)
414cb7a01acSMauro Carvalho Chehab 		return ret;
415cb7a01acSMauro Carvalho Chehab 
4163999e5d0SLars-Peter Clausen 	ret = __adv7180_status(state, status, NULL);
417cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
418cb7a01acSMauro Carvalho Chehab 	return ret;
419cb7a01acSMauro Carvalho Chehab }
420cb7a01acSMauro Carvalho Chehab 
adv7180_program_std(struct adv7180_state * state)4213e35e33cSLars-Peter Clausen static int adv7180_program_std(struct adv7180_state *state)
4223e35e33cSLars-Peter Clausen {
4233e35e33cSLars-Peter Clausen 	int ret;
4243e35e33cSLars-Peter Clausen 
4253e35e33cSLars-Peter Clausen 	ret = v4l2_std_to_adv7180(state->curr_norm);
4263e35e33cSLars-Peter Clausen 	if (ret < 0)
4273e35e33cSLars-Peter Clausen 		return ret;
4283e35e33cSLars-Peter Clausen 
429f5dde49bSLars-Peter Clausen 	ret = adv7180_set_video_standard(state, ret);
4303e35e33cSLars-Peter Clausen 	if (ret < 0)
4313e35e33cSLars-Peter Clausen 		return ret;
4323e35e33cSLars-Peter Clausen 	return 0;
4333e35e33cSLars-Peter Clausen }
4343e35e33cSLars-Peter Clausen 
adv7180_s_std(struct v4l2_subdev * sd,v4l2_std_id std)435cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
436cb7a01acSMauro Carvalho Chehab {
437cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
438cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
4393e35e33cSLars-Peter Clausen 
440cb7a01acSMauro Carvalho Chehab 	if (ret)
441cb7a01acSMauro Carvalho Chehab 		return ret;
442cb7a01acSMauro Carvalho Chehab 
4433e35e33cSLars-Peter Clausen 	/* Make sure we can support this std */
444cb7a01acSMauro Carvalho Chehab 	ret = v4l2_std_to_adv7180(std);
445cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
446cb7a01acSMauro Carvalho Chehab 		goto out;
447cb7a01acSMauro Carvalho Chehab 
448cb7a01acSMauro Carvalho Chehab 	state->curr_norm = std;
4493e35e33cSLars-Peter Clausen 
4503e35e33cSLars-Peter Clausen 	ret = adv7180_program_std(state);
451cb7a01acSMauro Carvalho Chehab out:
452cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
453cb7a01acSMauro Carvalho Chehab 	return ret;
454cb7a01acSMauro Carvalho Chehab }
455cb7a01acSMauro Carvalho Chehab 
adv7180_g_std(struct v4l2_subdev * sd,v4l2_std_id * norm)456d0fadc86SNiklas Söderlund static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
457d0fadc86SNiklas Söderlund {
458d0fadc86SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
459d0fadc86SNiklas Söderlund 
460d0fadc86SNiklas Söderlund 	*norm = state->curr_norm;
461d0fadc86SNiklas Söderlund 
462d0fadc86SNiklas Söderlund 	return 0;
463d0fadc86SNiklas Söderlund }
464d0fadc86SNiklas Söderlund 
adv7180_g_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_frame_interval * fi)46538566d28SNiklas Söderlund static int adv7180_g_frame_interval(struct v4l2_subdev *sd,
46638566d28SNiklas Söderlund 				    struct v4l2_subdev_frame_interval *fi)
46738566d28SNiklas Söderlund {
46838566d28SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
46938566d28SNiklas Söderlund 
47038566d28SNiklas Söderlund 	if (state->curr_norm & V4L2_STD_525_60) {
47138566d28SNiklas Söderlund 		fi->interval.numerator = 1001;
47238566d28SNiklas Söderlund 		fi->interval.denominator = 30000;
47338566d28SNiklas Söderlund 	} else {
47438566d28SNiklas Söderlund 		fi->interval.numerator = 1;
47538566d28SNiklas Söderlund 		fi->interval.denominator = 25;
47638566d28SNiklas Söderlund 	}
47738566d28SNiklas Söderlund 
47838566d28SNiklas Söderlund 	return 0;
47938566d28SNiklas Söderlund }
48038566d28SNiklas Söderlund 
adv7180_set_power_pin(struct adv7180_state * state,bool on)48165d9e14aSSteve Longerbeam static void adv7180_set_power_pin(struct adv7180_state *state, bool on)
48265d9e14aSSteve Longerbeam {
48365d9e14aSSteve Longerbeam 	if (!state->pwdn_gpio)
48465d9e14aSSteve Longerbeam 		return;
48565d9e14aSSteve Longerbeam 
48665d9e14aSSteve Longerbeam 	if (on) {
48765d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 0);
48865d9e14aSSteve Longerbeam 		usleep_range(5000, 10000);
48965d9e14aSSteve Longerbeam 	} else {
49065d9e14aSSteve Longerbeam 		gpiod_set_value_cansleep(state->pwdn_gpio, 1);
49165d9e14aSSteve Longerbeam 	}
49265d9e14aSSteve Longerbeam }
49365d9e14aSSteve Longerbeam 
adv7180_set_reset_pin(struct adv7180_state * state,bool on)494abb7c7c2SFrieder Schrempf static void adv7180_set_reset_pin(struct adv7180_state *state, bool on)
495abb7c7c2SFrieder Schrempf {
496abb7c7c2SFrieder Schrempf 	if (!state->rst_gpio)
497abb7c7c2SFrieder Schrempf 		return;
498abb7c7c2SFrieder Schrempf 
499abb7c7c2SFrieder Schrempf 	if (on) {
500abb7c7c2SFrieder Schrempf 		gpiod_set_value_cansleep(state->rst_gpio, 1);
501abb7c7c2SFrieder Schrempf 	} else {
502abb7c7c2SFrieder Schrempf 		gpiod_set_value_cansleep(state->rst_gpio, 0);
503abb7c7c2SFrieder Schrempf 		usleep_range(5000, 10000);
504abb7c7c2SFrieder Schrempf 	}
505abb7c7c2SFrieder Schrempf }
506abb7c7c2SFrieder Schrempf 
adv7180_set_power(struct adv7180_state * state,bool on)5073999e5d0SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state, bool on)
508e246c333SLars-Peter Clausen {
509e246c333SLars-Peter Clausen 	u8 val;
510b37135e3SLars-Peter Clausen 	int ret;
511e246c333SLars-Peter Clausen 
512e246c333SLars-Peter Clausen 	if (on)
513e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_ON;
514e246c333SLars-Peter Clausen 	else
515e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_OFF;
516e246c333SLars-Peter Clausen 
517b37135e3SLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
518b37135e3SLars-Peter Clausen 	if (ret)
519b37135e3SLars-Peter Clausen 		return ret;
520b37135e3SLars-Peter Clausen 
521b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
522b37135e3SLars-Peter Clausen 		if (on) {
523b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xDE, 0x02);
524b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD2, 0xF7);
525b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xD8, 0x65);
526b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0xE0, 0x09);
527b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x2C, 0x00);
528851a54efSLars-Peter Clausen 			if (state->field == V4L2_FIELD_NONE)
529851a54efSLars-Peter Clausen 				adv7180_csi_write(state, 0x1D, 0x80);
530b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x00);
531b37135e3SLars-Peter Clausen 		} else {
532b37135e3SLars-Peter Clausen 			adv7180_csi_write(state, 0x00, 0x80);
533b37135e3SLars-Peter Clausen 		}
534b37135e3SLars-Peter Clausen 	}
535b37135e3SLars-Peter Clausen 
536b37135e3SLars-Peter Clausen 	return 0;
537e246c333SLars-Peter Clausen }
538e246c333SLars-Peter Clausen 
adv7180_s_power(struct v4l2_subdev * sd,int on)539e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on)
540e246c333SLars-Peter Clausen {
541e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
542e246c333SLars-Peter Clausen 	int ret;
543e246c333SLars-Peter Clausen 
544e246c333SLars-Peter Clausen 	ret = mutex_lock_interruptible(&state->mutex);
545e246c333SLars-Peter Clausen 	if (ret)
546e246c333SLars-Peter Clausen 		return ret;
547e246c333SLars-Peter Clausen 
5483999e5d0SLars-Peter Clausen 	ret = adv7180_set_power(state, on);
549e246c333SLars-Peter Clausen 	if (ret == 0)
550e246c333SLars-Peter Clausen 		state->powered = on;
551e246c333SLars-Peter Clausen 
552e246c333SLars-Peter Clausen 	mutex_unlock(&state->mutex);
553e246c333SLars-Peter Clausen 	return ret;
554e246c333SLars-Peter Clausen }
555e246c333SLars-Peter Clausen 
556a76c86f4SFabio Estevam static const char * const test_pattern_menu[] = {
557a76c86f4SFabio Estevam 	"Single color",
558a76c86f4SFabio Estevam 	"Color bars",
559a76c86f4SFabio Estevam 	"Luma ramp",
560a76c86f4SFabio Estevam 	"Boundary box",
561a76c86f4SFabio Estevam 	"Disable",
562a76c86f4SFabio Estevam };
563a76c86f4SFabio Estevam 
adv7180_test_pattern(struct adv7180_state * state,int value)564a76c86f4SFabio Estevam static int adv7180_test_pattern(struct adv7180_state *state, int value)
565a76c86f4SFabio Estevam {
566a76c86f4SFabio Estevam 	unsigned int reg = 0;
567a76c86f4SFabio Estevam 
568a76c86f4SFabio Estevam 	/* Map menu value into register value */
569a76c86f4SFabio Estevam 	if (value < 3)
570a76c86f4SFabio Estevam 		reg = value;
571a76c86f4SFabio Estevam 	if (value == 3)
572a76c86f4SFabio Estevam 		reg = 5;
573a76c86f4SFabio Estevam 
574a76c86f4SFabio Estevam 	adv7180_write(state, ADV7180_REG_ANALOG_CLAMP_CTL, reg);
575a76c86f4SFabio Estevam 
576a76c86f4SFabio Estevam 	if (value == ARRAY_SIZE(test_pattern_menu) - 1) {
577a76c86f4SFabio Estevam 		reg = adv7180_read(state, ADV7180_REG_DEF_VALUE_Y);
578a76c86f4SFabio Estevam 		reg &= ~ADV7180_DEF_VAL_EN;
579a76c86f4SFabio Estevam 		adv7180_write(state, ADV7180_REG_DEF_VALUE_Y, reg);
580a76c86f4SFabio Estevam 		return 0;
581a76c86f4SFabio Estevam 	}
582a76c86f4SFabio Estevam 
583a76c86f4SFabio Estevam 	reg = adv7180_read(state, ADV7180_REG_DEF_VALUE_Y);
584a76c86f4SFabio Estevam 	reg |= ADV7180_DEF_VAL_EN | ADV7180_DEF_VAL_AUTO_EN;
585a76c86f4SFabio Estevam 	adv7180_write(state, ADV7180_REG_DEF_VALUE_Y, reg);
586a76c86f4SFabio Estevam 
587a76c86f4SFabio Estevam 	return 0;
588a76c86f4SFabio Estevam }
589a76c86f4SFabio Estevam 
adv7180_s_ctrl(struct v4l2_ctrl * ctrl)590cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
591cb7a01acSMauro Carvalho Chehab {
592cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
593cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
594cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
595cb7a01acSMauro Carvalho Chehab 	int val;
596cb7a01acSMauro Carvalho Chehab 
597cb7a01acSMauro Carvalho Chehab 	if (ret)
598cb7a01acSMauro Carvalho Chehab 		return ret;
599cb7a01acSMauro Carvalho Chehab 	val = ctrl->val;
600cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
601cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
6023999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_BRI, val);
603cb7a01acSMauro Carvalho Chehab 		break;
604cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
605cb7a01acSMauro Carvalho Chehab 		/*Hue is inverted according to HSL chart */
6063999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
607cb7a01acSMauro Carvalho Chehab 		break;
608cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
6093999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_CON, val);
610cb7a01acSMauro Carvalho Chehab 		break;
611cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
612cb7a01acSMauro Carvalho Chehab 		/*
613cb7a01acSMauro Carvalho Chehab 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
614cb7a01acSMauro Carvalho Chehab 		 *Let's not confuse the user, everybody understands saturation
615cb7a01acSMauro Carvalho Chehab 		 */
6163999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
617cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
618cb7a01acSMauro Carvalho Chehab 			break;
6193999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
620cb7a01acSMauro Carvalho Chehab 		break;
62108b717c2SLars-Peter Clausen 	case V4L2_CID_ADV_FAST_SWITCH:
62208b717c2SLars-Peter Clausen 		if (ctrl->val) {
62308b717c2SLars-Peter Clausen 			/* ADI required write */
62408b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0x44);
62508b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL,
62608b717c2SLars-Peter Clausen 				ADV7180_FLCONTROL_FL_ENABLE);
62708b717c2SLars-Peter Clausen 		} else {
62808b717c2SLars-Peter Clausen 			/* ADI required write */
62908b717c2SLars-Peter Clausen 			adv7180_write(state, 0x80d9, 0xc4);
63008b717c2SLars-Peter Clausen 			adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
63108b717c2SLars-Peter Clausen 		}
63208b717c2SLars-Peter Clausen 		break;
633a76c86f4SFabio Estevam 	case V4L2_CID_TEST_PATTERN:
634a76c86f4SFabio Estevam 		ret = adv7180_test_pattern(state, val);
635a76c86f4SFabio Estevam 		break;
636cb7a01acSMauro Carvalho Chehab 	default:
637cb7a01acSMauro Carvalho Chehab 		ret = -EINVAL;
638cb7a01acSMauro Carvalho Chehab 	}
639cb7a01acSMauro Carvalho Chehab 
640cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
641cb7a01acSMauro Carvalho Chehab 	return ret;
642cb7a01acSMauro Carvalho Chehab }
643cb7a01acSMauro Carvalho Chehab 
644cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
645cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7180_s_ctrl,
646cb7a01acSMauro Carvalho Chehab };
647cb7a01acSMauro Carvalho Chehab 
64808b717c2SLars-Peter Clausen static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
64908b717c2SLars-Peter Clausen 	.ops = &adv7180_ctrl_ops,
65008b717c2SLars-Peter Clausen 	.id = V4L2_CID_ADV_FAST_SWITCH,
65108b717c2SLars-Peter Clausen 	.name = "Fast Switching",
65208b717c2SLars-Peter Clausen 	.type = V4L2_CTRL_TYPE_BOOLEAN,
65308b717c2SLars-Peter Clausen 	.min = 0,
65408b717c2SLars-Peter Clausen 	.max = 1,
65508b717c2SLars-Peter Clausen 	.step = 1,
65608b717c2SLars-Peter Clausen };
65708b717c2SLars-Peter Clausen 
adv7180_init_controls(struct adv7180_state * state)658cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state)
659cb7a01acSMauro Carvalho Chehab {
660cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
661cb7a01acSMauro Carvalho Chehab 
662cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
663cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
664cb7a01acSMauro Carvalho Chehab 			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
665cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
666cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
667cb7a01acSMauro Carvalho Chehab 			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
668cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
669cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
670cb7a01acSMauro Carvalho Chehab 			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
671cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
672cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
673cb7a01acSMauro Carvalho Chehab 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
67408b717c2SLars-Peter Clausen 	v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
67508b717c2SLars-Peter Clausen 
676a76c86f4SFabio Estevam 	v4l2_ctrl_new_std_menu_items(&state->ctrl_hdl, &adv7180_ctrl_ops,
677a76c86f4SFabio Estevam 				      V4L2_CID_TEST_PATTERN,
678a76c86f4SFabio Estevam 				      ARRAY_SIZE(test_pattern_menu) - 1,
679a76c86f4SFabio Estevam 				      0, ARRAY_SIZE(test_pattern_menu) - 1,
680a76c86f4SFabio Estevam 				      test_pattern_menu);
681a76c86f4SFabio Estevam 
682cb7a01acSMauro Carvalho Chehab 	state->sd.ctrl_handler = &state->ctrl_hdl;
683cb7a01acSMauro Carvalho Chehab 	if (state->ctrl_hdl.error) {
684cb7a01acSMauro Carvalho Chehab 		int err = state->ctrl_hdl.error;
685cb7a01acSMauro Carvalho Chehab 
686cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->ctrl_hdl);
687cb7a01acSMauro Carvalho Chehab 		return err;
688cb7a01acSMauro Carvalho Chehab 	}
689cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
690cb7a01acSMauro Carvalho Chehab 
691cb7a01acSMauro Carvalho Chehab 	return 0;
692cb7a01acSMauro Carvalho Chehab }
adv7180_exit_controls(struct adv7180_state * state)693cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state)
694cb7a01acSMauro Carvalho Chehab {
695cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
696cb7a01acSMauro Carvalho Chehab }
697cb7a01acSMauro Carvalho Chehab 
adv7180_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)698d5d51a82SLars-Peter Clausen static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
6990d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
700d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_mbus_code_enum *code)
701cccb83f7SVladimir Barinov {
702d5d51a82SLars-Peter Clausen 	if (code->index != 0)
703cccb83f7SVladimir Barinov 		return -EINVAL;
704cccb83f7SVladimir Barinov 
7056de690ddSNiklas Söderlund 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
706cccb83f7SVladimir Barinov 
707cccb83f7SVladimir Barinov 	return 0;
708cccb83f7SVladimir Barinov }
709cccb83f7SVladimir Barinov 
adv7180_mbus_fmt(struct v4l2_subdev * sd,struct v4l2_mbus_framefmt * fmt)710cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
711cccb83f7SVladimir Barinov 			    struct v4l2_mbus_framefmt *fmt)
712cccb83f7SVladimir Barinov {
713cccb83f7SVladimir Barinov 	struct adv7180_state *state = to_state(sd);
714cccb83f7SVladimir Barinov 
7156de690ddSNiklas Söderlund 	fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
716cccb83f7SVladimir Barinov 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
717cccb83f7SVladimir Barinov 	fmt->width = 720;
718cccb83f7SVladimir Barinov 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
719cccb83f7SVladimir Barinov 
7206457b626SNiklas Söderlund 	if (state->field == V4L2_FIELD_ALTERNATE)
7216457b626SNiklas Söderlund 		fmt->height /= 2;
7226457b626SNiklas Söderlund 
723cccb83f7SVladimir Barinov 	return 0;
724cccb83f7SVladimir Barinov }
725cccb83f7SVladimir Barinov 
adv7180_set_field_mode(struct adv7180_state * state)726851a54efSLars-Peter Clausen static int adv7180_set_field_mode(struct adv7180_state *state)
727851a54efSLars-Peter Clausen {
728851a54efSLars-Peter Clausen 	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
729851a54efSLars-Peter Clausen 		return 0;
730851a54efSLars-Peter Clausen 
731851a54efSLars-Peter Clausen 	if (state->field == V4L2_FIELD_NONE) {
732851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
733851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x20);
734851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x28);
735851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x38);
736851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x30);
737851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x30);
738851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x80);
739851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x70);
740851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x50);
741851a54efSLars-Peter Clausen 		}
742851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x00);
743851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x00);
744851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x80);
745851a54efSLars-Peter Clausen 	} else {
746851a54efSLars-Peter Clausen 		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
747851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x01, 0x18);
748851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x02, 0x18);
749851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x03, 0x30);
750851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x04, 0x20);
751851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x05, 0x28);
752851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x06, 0x40);
753851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x07, 0x58);
754851a54efSLars-Peter Clausen 			adv7180_csi_write(state, 0x08, 0x30);
755851a54efSLars-Peter Clausen 		}
756851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0xa3, 0x70);
757851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x5b, 0x80);
758851a54efSLars-Peter Clausen 		adv7180_vpp_write(state, 0x55, 0x00);
759851a54efSLars-Peter Clausen 	}
760851a54efSLars-Peter Clausen 
761851a54efSLars-Peter Clausen 	return 0;
762851a54efSLars-Peter Clausen }
763851a54efSLars-Peter Clausen 
adv7180_get_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)764d5d51a82SLars-Peter Clausen static int adv7180_get_pad_format(struct v4l2_subdev *sd,
7650d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
766d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
767d5d51a82SLars-Peter Clausen {
768851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
769851a54efSLars-Peter Clausen 
770851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
7710d346d2aSTomi Valkeinen 		format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0);
772851a54efSLars-Peter Clausen 	} else {
773851a54efSLars-Peter Clausen 		adv7180_mbus_fmt(sd, &format->format);
774851a54efSLars-Peter Clausen 		format->format.field = state->field;
775851a54efSLars-Peter Clausen 	}
776851a54efSLars-Peter Clausen 
777851a54efSLars-Peter Clausen 	return 0;
778d5d51a82SLars-Peter Clausen }
779d5d51a82SLars-Peter Clausen 
adv7180_set_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)780d5d51a82SLars-Peter Clausen static int adv7180_set_pad_format(struct v4l2_subdev *sd,
7810d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
782d5d51a82SLars-Peter Clausen 				  struct v4l2_subdev_format *format)
783d5d51a82SLars-Peter Clausen {
784851a54efSLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
785851a54efSLars-Peter Clausen 	struct v4l2_mbus_framefmt *framefmt;
786e0ad7a9bSNiklas Söderlund 	int ret;
787851a54efSLars-Peter Clausen 
788851a54efSLars-Peter Clausen 	switch (format->format.field) {
789851a54efSLars-Peter Clausen 	case V4L2_FIELD_NONE:
7906457b626SNiklas Söderlund 		if (state->chip_info->flags & ADV7180_FLAG_I2P)
791851a54efSLars-Peter Clausen 			break;
7921771e9fbSGustavo A. R. Silva 		fallthrough;
793851a54efSLars-Peter Clausen 	default:
7946457b626SNiklas Söderlund 		format->format.field = V4L2_FIELD_ALTERNATE;
795851a54efSLars-Peter Clausen 		break;
796851a54efSLars-Peter Clausen 	}
797851a54efSLars-Peter Clausen 
798e0ad7a9bSNiklas Söderlund 	ret = adv7180_mbus_fmt(sd,  &format->format);
799e0ad7a9bSNiklas Söderlund 
800851a54efSLars-Peter Clausen 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
801851a54efSLars-Peter Clausen 		if (state->field != format->format.field) {
802851a54efSLars-Peter Clausen 			state->field = format->format.field;
803851a54efSLars-Peter Clausen 			adv7180_set_power(state, false);
804851a54efSLars-Peter Clausen 			adv7180_set_field_mode(state);
805851a54efSLars-Peter Clausen 			adv7180_set_power(state, true);
806851a54efSLars-Peter Clausen 		}
807851a54efSLars-Peter Clausen 	} else {
8080d346d2aSTomi Valkeinen 		framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0);
809851a54efSLars-Peter Clausen 		*framefmt = format->format;
810851a54efSLars-Peter Clausen 	}
811851a54efSLars-Peter Clausen 
812e0ad7a9bSNiklas Söderlund 	return ret;
813d5d51a82SLars-Peter Clausen }
814d5d51a82SLars-Peter Clausen 
adv7180_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)81523c72dd9SNiklas Söderlund static int adv7180_init_cfg(struct v4l2_subdev *sd,
8160d346d2aSTomi Valkeinen 			    struct v4l2_subdev_state *sd_state)
81723c72dd9SNiklas Söderlund {
81823c72dd9SNiklas Söderlund 	struct v4l2_subdev_format fmt = {
8190d346d2aSTomi Valkeinen 		.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY
82023c72dd9SNiklas Söderlund 		: V4L2_SUBDEV_FORMAT_ACTIVE,
82123c72dd9SNiklas Söderlund 	};
82223c72dd9SNiklas Söderlund 
8230d346d2aSTomi Valkeinen 	return adv7180_set_pad_format(sd, sd_state, &fmt);
82423c72dd9SNiklas Söderlund }
82523c72dd9SNiklas Söderlund 
adv7180_get_mbus_config(struct v4l2_subdev * sd,unsigned int pad,struct v4l2_mbus_config * cfg)8260c3da525SJacopo Mondi static int adv7180_get_mbus_config(struct v4l2_subdev *sd,
8270c3da525SJacopo Mondi 				   unsigned int pad,
828cccb83f7SVladimir Barinov 				   struct v4l2_mbus_config *cfg)
829cccb83f7SVladimir Barinov {
830b37135e3SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
831b37135e3SLars-Peter Clausen 
832b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
8332d95e7edSSakari Ailus 		cfg->type = V4L2_MBUS_CSI2_DPHY;
8346a7bdd89SLaurent Pinchart 		cfg->bus.mipi_csi2.num_data_lanes = 1;
835b9f7caa7SLaurent Pinchart 		cfg->bus.mipi_csi2.flags = 0;
836b37135e3SLars-Peter Clausen 	} else {
837cccb83f7SVladimir Barinov 		/*
838cccb83f7SVladimir Barinov 		 * The ADV7180 sensor supports BT.601/656 output modes.
839cccb83f7SVladimir Barinov 		 * The BT.656 is default and not yet configurable by s/w.
840cccb83f7SVladimir Barinov 		 */
8416a7bdd89SLaurent Pinchart 		cfg->bus.parallel.flags = V4L2_MBUS_MASTER |
8426a7bdd89SLaurent Pinchart 					  V4L2_MBUS_PCLK_SAMPLE_RISING |
843cccb83f7SVladimir Barinov 					  V4L2_MBUS_DATA_ACTIVE_HIGH;
844cccb83f7SVladimir Barinov 		cfg->type = V4L2_MBUS_BT656;
845b37135e3SLars-Peter Clausen 	}
846cccb83f7SVladimir Barinov 
847cccb83f7SVladimir Barinov 	return 0;
848cccb83f7SVladimir Barinov }
849cccb83f7SVladimir Barinov 
adv7180_get_skip_frames(struct v4l2_subdev * sd,u32 * frames)8509483a3f8STim Harvey static int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames)
8519483a3f8STim Harvey {
8529483a3f8STim Harvey 	*frames = ADV7180_NUM_OF_SKIP_FRAMES;
8539483a3f8STim Harvey 
8549483a3f8STim Harvey 	return 0;
8559483a3f8STim Harvey }
8569483a3f8STim Harvey 
adv7180_g_pixelaspect(struct v4l2_subdev * sd,struct v4l2_fract * aspect)857ecf37493SHans Verkuil static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect)
85864b3df92SNiklas Söderlund {
85964b3df92SNiklas Söderlund 	struct adv7180_state *state = to_state(sd);
86064b3df92SNiklas Söderlund 
86164b3df92SNiklas Söderlund 	if (state->curr_norm & V4L2_STD_525_60) {
862ecf37493SHans Verkuil 		aspect->numerator = 11;
863ecf37493SHans Verkuil 		aspect->denominator = 10;
86464b3df92SNiklas Söderlund 	} else {
865ecf37493SHans Verkuil 		aspect->numerator = 54;
866ecf37493SHans Verkuil 		aspect->denominator = 59;
86764b3df92SNiklas Söderlund 	}
86864b3df92SNiklas Söderlund 
86964b3df92SNiklas Söderlund 	return 0;
87064b3df92SNiklas Söderlund }
87164b3df92SNiklas Söderlund 
adv7180_g_tvnorms(struct v4l2_subdev * sd,v4l2_std_id * norm)872bae4c757SNiklas Söderlund static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
873bae4c757SNiklas Söderlund {
874bae4c757SNiklas Söderlund 	*norm = V4L2_STD_ALL;
875bae4c757SNiklas Söderlund 	return 0;
876bae4c757SNiklas Söderlund }
877bae4c757SNiklas Söderlund 
adv7180_s_stream(struct v4l2_subdev * sd,int enable)878937feeedSHans Verkuil static int adv7180_s_stream(struct v4l2_subdev *sd, int enable)
879937feeedSHans Verkuil {
880937feeedSHans Verkuil 	struct adv7180_state *state = to_state(sd);
881937feeedSHans Verkuil 	int ret;
882937feeedSHans Verkuil 
883937feeedSHans Verkuil 	/* It's always safe to stop streaming, no need to take the lock */
884937feeedSHans Verkuil 	if (!enable) {
885937feeedSHans Verkuil 		state->streaming = enable;
886937feeedSHans Verkuil 		return 0;
887937feeedSHans Verkuil 	}
888937feeedSHans Verkuil 
889937feeedSHans Verkuil 	/* Must wait until querystd released the lock */
890937feeedSHans Verkuil 	ret = mutex_lock_interruptible(&state->mutex);
891937feeedSHans Verkuil 	if (ret)
892937feeedSHans Verkuil 		return ret;
893937feeedSHans Verkuil 	state->streaming = enable;
894937feeedSHans Verkuil 	mutex_unlock(&state->mutex);
895937feeedSHans Verkuil 	return 0;
896937feeedSHans Verkuil }
897937feeedSHans Verkuil 
adv7180_subscribe_event(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)898937feeedSHans Verkuil static int adv7180_subscribe_event(struct v4l2_subdev *sd,
899937feeedSHans Verkuil 				   struct v4l2_fh *fh,
900937feeedSHans Verkuil 				   struct v4l2_event_subscription *sub)
901937feeedSHans Verkuil {
902937feeedSHans Verkuil 	switch (sub->type) {
903937feeedSHans Verkuil 	case V4L2_EVENT_SOURCE_CHANGE:
904937feeedSHans Verkuil 		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
905937feeedSHans Verkuil 	case V4L2_EVENT_CTRL:
906937feeedSHans Verkuil 		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
907937feeedSHans Verkuil 	default:
908937feeedSHans Verkuil 		return -EINVAL;
909937feeedSHans Verkuil 	}
910937feeedSHans Verkuil }
911937feeedSHans Verkuil 
912cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = {
9138774bed9SLaurent Pinchart 	.s_std = adv7180_s_std,
914d0fadc86SNiklas Söderlund 	.g_std = adv7180_g_std,
91538566d28SNiklas Söderlund 	.g_frame_interval = adv7180_g_frame_interval,
916cb7a01acSMauro Carvalho Chehab 	.querystd = adv7180_querystd,
917cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7180_g_input_status,
918cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7180_s_routing,
919ecf37493SHans Verkuil 	.g_pixelaspect = adv7180_g_pixelaspect,
920bae4c757SNiklas Söderlund 	.g_tvnorms = adv7180_g_tvnorms,
921937feeedSHans Verkuil 	.s_stream = adv7180_s_stream,
922cb7a01acSMauro Carvalho Chehab };
923cb7a01acSMauro Carvalho Chehab 
924cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = {
925e246c333SLars-Peter Clausen 	.s_power = adv7180_s_power,
926937feeedSHans Verkuil 	.subscribe_event = adv7180_subscribe_event,
927937feeedSHans Verkuil 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
928cb7a01acSMauro Carvalho Chehab };
929cb7a01acSMauro Carvalho Chehab 
930d5d51a82SLars-Peter Clausen static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
93123c72dd9SNiklas Söderlund 	.init_cfg = adv7180_init_cfg,
932d5d51a82SLars-Peter Clausen 	.enum_mbus_code = adv7180_enum_mbus_code,
933d5d51a82SLars-Peter Clausen 	.set_fmt = adv7180_set_pad_format,
934d5d51a82SLars-Peter Clausen 	.get_fmt = adv7180_get_pad_format,
9350c3da525SJacopo Mondi 	.get_mbus_config = adv7180_get_mbus_config,
936d5d51a82SLars-Peter Clausen };
937d5d51a82SLars-Peter Clausen 
9389483a3f8STim Harvey static const struct v4l2_subdev_sensor_ops adv7180_sensor_ops = {
9399483a3f8STim Harvey 	.g_skip_frames = adv7180_get_skip_frames,
9409483a3f8STim Harvey };
9419483a3f8STim Harvey 
942cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = {
943cb7a01acSMauro Carvalho Chehab 	.core = &adv7180_core_ops,
944cb7a01acSMauro Carvalho Chehab 	.video = &adv7180_video_ops,
945d5d51a82SLars-Peter Clausen 	.pad = &adv7180_pad_ops,
9469483a3f8STim Harvey 	.sensor = &adv7180_sensor_ops,
947cb7a01acSMauro Carvalho Chehab };
948cb7a01acSMauro Carvalho Chehab 
adv7180_irq(int irq,void * devid)9490c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid)
950cb7a01acSMauro Carvalho Chehab {
9510c25534dSLars-Peter Clausen 	struct adv7180_state *state = devid;
952cb7a01acSMauro Carvalho Chehab 	u8 isr3;
953cb7a01acSMauro Carvalho Chehab 
954cb7a01acSMauro Carvalho Chehab 	mutex_lock(&state->mutex);
9553999e5d0SLars-Peter Clausen 	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
956cb7a01acSMauro Carvalho Chehab 	/* clear */
9573999e5d0SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_ICR3, isr3);
958cb7a01acSMauro Carvalho Chehab 
959937feeedSHans Verkuil 	if (isr3 & ADV7180_IRQ3_AD_CHANGE) {
960937feeedSHans Verkuil 		static const struct v4l2_event src_ch = {
961937feeedSHans Verkuil 			.type = V4L2_EVENT_SOURCE_CHANGE,
962937feeedSHans Verkuil 			.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
963937feeedSHans Verkuil 		};
964937feeedSHans Verkuil 
965937feeedSHans Verkuil 		v4l2_subdev_notify_event(&state->sd, &src_ch);
966937feeedSHans Verkuil 	}
967cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
968cb7a01acSMauro Carvalho Chehab 
969cb7a01acSMauro Carvalho Chehab 	return IRQ_HANDLED;
970cb7a01acSMauro Carvalho Chehab }
971cb7a01acSMauro Carvalho Chehab 
adv7180_init(struct adv7180_state * state)972f5dde49bSLars-Peter Clausen static int adv7180_init(struct adv7180_state *state)
973f5dde49bSLars-Peter Clausen {
974f5dde49bSLars-Peter Clausen 	int ret;
975f5dde49bSLars-Peter Clausen 
976f5dde49bSLars-Peter Clausen 	/* ITU-R BT.656-4 compatible */
977f5dde49bSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
978f5dde49bSLars-Peter Clausen 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
979f5dde49bSLars-Peter Clausen 	if (ret < 0)
980f5dde49bSLars-Peter Clausen 		return ret;
981f5dde49bSLars-Peter Clausen 
982f5dde49bSLars-Peter Clausen 	/* Manually set V bit end position in NTSC mode */
983f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
984f5dde49bSLars-Peter Clausen 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
985f5dde49bSLars-Peter Clausen }
986f5dde49bSLars-Peter Clausen 
adv7180_set_std(struct adv7180_state * state,unsigned int std)987f5dde49bSLars-Peter Clausen static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
988f5dde49bSLars-Peter Clausen {
989f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
990f5dde49bSLars-Peter Clausen 		(std << 4) | state->input);
991f5dde49bSLars-Peter Clausen }
992f5dde49bSLars-Peter Clausen 
adv7180_select_input(struct adv7180_state * state,unsigned int input)993f5dde49bSLars-Peter Clausen static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
994f5dde49bSLars-Peter Clausen {
995f5dde49bSLars-Peter Clausen 	int ret;
996f5dde49bSLars-Peter Clausen 
997f5dde49bSLars-Peter Clausen 	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
998f5dde49bSLars-Peter Clausen 	if (ret < 0)
999f5dde49bSLars-Peter Clausen 		return ret;
1000f5dde49bSLars-Peter Clausen 
1001f5dde49bSLars-Peter Clausen 	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
1002f5dde49bSLars-Peter Clausen 	ret |= input;
1003f5dde49bSLars-Peter Clausen 	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
1004f5dde49bSLars-Peter Clausen }
1005f5dde49bSLars-Peter Clausen 
adv7182_init(struct adv7180_state * state)1006c5ef8f8cSLars-Peter Clausen static int adv7182_init(struct adv7180_state *state)
1007c5ef8f8cSLars-Peter Clausen {
1008b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
1009b37135e3SLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
1010b37135e3SLars-Peter Clausen 			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
1011b37135e3SLars-Peter Clausen 
1012851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P)
1013851a54efSLars-Peter Clausen 		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
1014851a54efSLars-Peter Clausen 			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
1015851a54efSLars-Peter Clausen 
1016bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2) {
1017bf7dcb80SLars-Peter Clausen 		/* ADI recommended writes for improved video quality */
1018bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0080, 0x51);
1019bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0081, 0x51);
1020bf7dcb80SLars-Peter Clausen 		adv7180_write(state, 0x0082, 0x68);
1021bf7dcb80SLars-Peter Clausen 	}
1022bf7dcb80SLars-Peter Clausen 
1023c5ef8f8cSLars-Peter Clausen 	/* ADI required writes */
1024b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
1025ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x4e);
1026ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57);
1027ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0);
1028b37135e3SLars-Peter Clausen 	} else {
1029ed771d75SMatthew Michilot 		if (state->chip_info->flags & ADV7180_FLAG_V2) {
1030ed771d75SMatthew Michilot 			if (state->force_bt656_4) {
1031ed771d75SMatthew Michilot 				/* ITU-R BT.656-4 compatible */
1032ed771d75SMatthew Michilot 				adv7180_write(state,
1033ed771d75SMatthew Michilot 					      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
1034ed771d75SMatthew Michilot 					      ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
1035ed771d75SMatthew Michilot 				/* Manually set NEWAVMODE */
1036ed771d75SMatthew Michilot 				adv7180_write(state,
1037ed771d75SMatthew Michilot 					      ADV7180_REG_VSYNC_FIELD_CTL_1,
1038ed771d75SMatthew Michilot 					      ADV7180_VSYNC_FIELD_CTL_1_NEWAV);
1039ed771d75SMatthew Michilot 				/* Manually set V bit end position in NTSC mode */
1040ed771d75SMatthew Michilot 				adv7180_write(state,
1041ed771d75SMatthew Michilot 					      ADV7180_REG_NTSC_V_BIT_END,
1042ed771d75SMatthew Michilot 					      ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
1043ed771d75SMatthew Michilot 			} else {
1044ce5d6290SSteve Longerbeam 				adv7180_write(state,
1045ce5d6290SSteve Longerbeam 					      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
1046ce5d6290SSteve Longerbeam 					      0x17);
1047ed771d75SMatthew Michilot 			}
1048ed771d75SMatthew Michilot 		}
1049b37135e3SLars-Peter Clausen 		else
1050ce5d6290SSteve Longerbeam 			adv7180_write(state,
1051ce5d6290SSteve Longerbeam 				      ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
1052ce5d6290SSteve Longerbeam 				      0x07);
1053ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x0c);
1054ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CTRL_2, 0x40);
1055b37135e3SLars-Peter Clausen 	}
1056b37135e3SLars-Peter Clausen 
1057b37135e3SLars-Peter Clausen 	adv7180_write(state, 0x0013, 0x00);
1058c5ef8f8cSLars-Peter Clausen 
1059c5ef8f8cSLars-Peter Clausen 	return 0;
1060c5ef8f8cSLars-Peter Clausen }
1061c5ef8f8cSLars-Peter Clausen 
adv7182_set_std(struct adv7180_state * state,unsigned int std)1062c5ef8f8cSLars-Peter Clausen static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
1063c5ef8f8cSLars-Peter Clausen {
1064db9edaafSBenjamin Marty 	/* Failing to set the reserved bit can result in increased video noise */
1065db9edaafSBenjamin Marty 	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL,
1066db9edaafSBenjamin Marty 			     (std << 4) | ADV7182_REG_INPUT_RESERVED);
1067c5ef8f8cSLars-Peter Clausen }
1068c5ef8f8cSLars-Peter Clausen 
1069c5ef8f8cSLars-Peter Clausen enum adv7182_input_type {
1070c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_CVBS,
1071c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_DIFF_CVBS,
1072c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_SVIDEO,
1073c5ef8f8cSLars-Peter Clausen 	ADV7182_INPUT_TYPE_YPBPR,
1074c5ef8f8cSLars-Peter Clausen };
1075c5ef8f8cSLars-Peter Clausen 
adv7182_get_input_type(unsigned int input)1076c5ef8f8cSLars-Peter Clausen static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
1077c5ef8f8cSLars-Peter Clausen {
1078c5ef8f8cSLars-Peter Clausen 	switch (input) {
1079c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN1:
1080c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN2:
1081c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN3:
1082c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN4:
1083c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN5:
1084c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN6:
1085c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN7:
1086c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_CVBS_AIN8:
1087c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_CVBS;
1088c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
1089c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
1090c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
1091c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
1092c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_SVIDEO;
1093c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
1094c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
1095c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_YPBPR;
1096c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
1097c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
1098c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
1099c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
1100c5ef8f8cSLars-Peter Clausen 		return ADV7182_INPUT_TYPE_DIFF_CVBS;
1101c5ef8f8cSLars-Peter Clausen 	default: /* Will never happen */
1102c5ef8f8cSLars-Peter Clausen 		return 0;
1103c5ef8f8cSLars-Peter Clausen 	}
1104c5ef8f8cSLars-Peter Clausen }
1105c5ef8f8cSLars-Peter Clausen 
1106c5ef8f8cSLars-Peter Clausen /* ADI recommended writes to registers 0x52, 0x53, 0x54 */
1107c5ef8f8cSLars-Peter Clausen static unsigned int adv7182_lbias_settings[][3] = {
1108c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
1109c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
1110c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
1111c5ef8f8cSLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
1112c5ef8f8cSLars-Peter Clausen };
1113c5ef8f8cSLars-Peter Clausen 
1114bf7dcb80SLars-Peter Clausen static unsigned int adv7280_lbias_settings[][3] = {
1115bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
1116bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
1117bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
1118bf7dcb80SLars-Peter Clausen 	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
1119bf7dcb80SLars-Peter Clausen };
1120bf7dcb80SLars-Peter Clausen 
adv7182_select_input(struct adv7180_state * state,unsigned int input)1121c5ef8f8cSLars-Peter Clausen static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
1122c5ef8f8cSLars-Peter Clausen {
1123c5ef8f8cSLars-Peter Clausen 	enum adv7182_input_type input_type;
1124c5ef8f8cSLars-Peter Clausen 	unsigned int *lbias;
1125c5ef8f8cSLars-Peter Clausen 	unsigned int i;
1126c5ef8f8cSLars-Peter Clausen 	int ret;
1127c5ef8f8cSLars-Peter Clausen 
1128c5ef8f8cSLars-Peter Clausen 	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
1129c5ef8f8cSLars-Peter Clausen 	if (ret)
1130c5ef8f8cSLars-Peter Clausen 		return ret;
1131c5ef8f8cSLars-Peter Clausen 
1132c5ef8f8cSLars-Peter Clausen 	/* Reset clamp circuitry - ADI recommended writes */
1133ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0x00);
1134ce5d6290SSteve Longerbeam 	adv7180_write(state, ADV7180_REG_RST_CLAMP, 0xff);
1135c5ef8f8cSLars-Peter Clausen 
1136c5ef8f8cSLars-Peter Clausen 	input_type = adv7182_get_input_type(input);
1137c5ef8f8cSLars-Peter Clausen 
1138c5ef8f8cSLars-Peter Clausen 	switch (input_type) {
1139c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_CVBS:
1140c5ef8f8cSLars-Peter Clausen 	case ADV7182_INPUT_TYPE_DIFF_CVBS:
1141c5ef8f8cSLars-Peter Clausen 		/* ADI recommends to use the SH1 filter */
1142ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x41);
1143c5ef8f8cSLars-Peter Clausen 		break;
1144c5ef8f8cSLars-Peter Clausen 	default:
1145ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x01);
1146c5ef8f8cSLars-Peter Clausen 		break;
1147c5ef8f8cSLars-Peter Clausen 	}
1148c5ef8f8cSLars-Peter Clausen 
1149bf7dcb80SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_V2)
1150bf7dcb80SLars-Peter Clausen 		lbias = adv7280_lbias_settings[input_type];
1151bf7dcb80SLars-Peter Clausen 	else
1152c5ef8f8cSLars-Peter Clausen 		lbias = adv7182_lbias_settings[input_type];
1153c5ef8f8cSLars-Peter Clausen 
1154c5ef8f8cSLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
1155ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CVBS_TRIM + i, lbias[i]);
1156c5ef8f8cSLars-Peter Clausen 
1157c5ef8f8cSLars-Peter Clausen 	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
1158c5ef8f8cSLars-Peter Clausen 		/* ADI required writes to make differential CVBS work */
1159ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xa8);
1160ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0x90);
1161ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0xb0);
1162ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x08);
1163ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0xa0);
1164c5ef8f8cSLars-Peter Clausen 	} else {
1165ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_RES_CIR, 0xf0);
1166ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0xd0);
1167ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_DIFF_MODE, 0x10);
1168ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x9c);
1169ce5d6290SSteve Longerbeam 		adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0x00);
1170c5ef8f8cSLars-Peter Clausen 	}
1171c5ef8f8cSLars-Peter Clausen 
1172c5ef8f8cSLars-Peter Clausen 	return 0;
1173c5ef8f8cSLars-Peter Clausen }
1174c5ef8f8cSLars-Peter Clausen 
1175f5dde49bSLars-Peter Clausen static const struct adv7180_chip_info adv7180_info = {
1176f5dde49bSLars-Peter Clausen 	.flags = ADV7180_FLAG_RESET_POWERED,
1177f5dde49bSLars-Peter Clausen 	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
1178f5dde49bSLars-Peter Clausen 	 * all inputs and let the card driver take care of validation
1179f5dde49bSLars-Peter Clausen 	 */
1180f5dde49bSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
1181f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN2) |
1182f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN3) |
1183f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN4) |
1184f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN5) |
1185f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_CVBS_AIN6) |
1186f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
1187f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
1188f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
1189f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1190f5dde49bSLars-Peter Clausen 		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
1191f5dde49bSLars-Peter Clausen 	.init = adv7180_init,
1192f5dde49bSLars-Peter Clausen 	.set_std = adv7180_set_std,
1193f5dde49bSLars-Peter Clausen 	.select_input = adv7180_select_input,
1194f5dde49bSLars-Peter Clausen };
1195f5dde49bSLars-Peter Clausen 
1196c5ef8f8cSLars-Peter Clausen static const struct adv7180_chip_info adv7182_info = {
1197c5ef8f8cSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1198c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1199c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1200c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1201c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1202c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1203c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1204c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1205c5ef8f8cSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
1206c5ef8f8cSLars-Peter Clausen 	.init = adv7182_init,
1207c5ef8f8cSLars-Peter Clausen 	.set_std = adv7182_set_std,
1208c5ef8f8cSLars-Peter Clausen 	.select_input = adv7182_select_input,
1209c5ef8f8cSLars-Peter Clausen };
1210c5ef8f8cSLars-Peter Clausen 
1211bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7280_info = {
1212851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1213bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1214bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1215bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1216bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1217bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1218bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1219bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
1220bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1221bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1222bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1223bf7dcb80SLars-Peter Clausen };
1224bf7dcb80SLars-Peter Clausen 
1225b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7280_m_info = {
1226851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1227b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1228b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1229b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1230b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1231b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1232b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1233b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1234b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1235b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1236b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1237b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1238b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1239b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1240b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
1241b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1242b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1243b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1244b37135e3SLars-Peter Clausen };
1245b37135e3SLars-Peter Clausen 
1246bf7dcb80SLars-Peter Clausen static const struct adv7180_chip_info adv7281_info = {
1247b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1248bf7dcb80SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1249bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1250bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1251bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1252bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1253bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1254bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1255bf7dcb80SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1256bf7dcb80SLars-Peter Clausen 	.init = adv7182_init,
1257bf7dcb80SLars-Peter Clausen 	.set_std = adv7182_set_std,
1258bf7dcb80SLars-Peter Clausen 	.select_input = adv7182_select_input,
1259bf7dcb80SLars-Peter Clausen };
1260bf7dcb80SLars-Peter Clausen 
1261b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_m_info = {
1262b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1263b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1264b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1265b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1266b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1267b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1268b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1269b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1270b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1271b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1272b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1273b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1274b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1275b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1276b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1277b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1278b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1279b37135e3SLars-Peter Clausen };
1280b37135e3SLars-Peter Clausen 
1281b37135e3SLars-Peter Clausen static const struct adv7180_chip_info adv7281_ma_info = {
1282b37135e3SLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
1283b37135e3SLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1284b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1285b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1286b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1287b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN5) |
1288b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN6) |
1289b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1290b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1291b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1292b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1293b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
1294b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1295b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
1296b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
1297b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1298b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1299b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
1300b37135e3SLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1301b37135e3SLars-Peter Clausen 	.init = adv7182_init,
1302b37135e3SLars-Peter Clausen 	.set_std = adv7182_set_std,
1303b37135e3SLars-Peter Clausen 	.select_input = adv7182_select_input,
1304b37135e3SLars-Peter Clausen };
1305b37135e3SLars-Peter Clausen 
1306851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_info = {
1307851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
1308851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1309851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1310851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1311851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1312851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1313851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1314851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1315851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1316851a54efSLars-Peter Clausen 	.init = adv7182_init,
1317851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1318851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1319851a54efSLars-Peter Clausen };
1320851a54efSLars-Peter Clausen 
1321851a54efSLars-Peter Clausen static const struct adv7180_chip_info adv7282_m_info = {
1322851a54efSLars-Peter Clausen 	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
1323851a54efSLars-Peter Clausen 	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
1324851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN2) |
1325851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN3) |
1326851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN4) |
1327851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN7) |
1328851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_CVBS_AIN8) |
1329851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
1330851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
1331851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
1332851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
1333851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
1334851a54efSLars-Peter Clausen 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
1335851a54efSLars-Peter Clausen 	.init = adv7182_init,
1336851a54efSLars-Peter Clausen 	.set_std = adv7182_set_std,
1337851a54efSLars-Peter Clausen 	.select_input = adv7182_select_input,
1338851a54efSLars-Peter Clausen };
1339851a54efSLars-Peter Clausen 
init_device(struct adv7180_state * state)13403999e5d0SLars-Peter Clausen static int init_device(struct adv7180_state *state)
1341cb7a01acSMauro Carvalho Chehab {
1342cb7a01acSMauro Carvalho Chehab 	int ret;
1343cb7a01acSMauro Carvalho Chehab 
13443999e5d0SLars-Peter Clausen 	mutex_lock(&state->mutex);
13453999e5d0SLars-Peter Clausen 
134665d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, true);
1347abb7c7c2SFrieder Schrempf 	adv7180_set_reset_pin(state, false);
134865d9e14aSSteve Longerbeam 
1349c18818e9SLars-Peter Clausen 	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
135016dfe72fSUlrich Hecht 	usleep_range(5000, 10000);
1351c18818e9SLars-Peter Clausen 
1352f5dde49bSLars-Peter Clausen 	ret = state->chip_info->init(state);
13533e35e33cSLars-Peter Clausen 	if (ret)
13543999e5d0SLars-Peter Clausen 		goto out_unlock;
1355cb7a01acSMauro Carvalho Chehab 
1356f5dde49bSLars-Peter Clausen 	ret = adv7180_program_std(state);
1357f5dde49bSLars-Peter Clausen 	if (ret)
13583999e5d0SLars-Peter Clausen 		goto out_unlock;
1359cb7a01acSMauro Carvalho Chehab 
1360851a54efSLars-Peter Clausen 	adv7180_set_field_mode(state);
1361851a54efSLars-Peter Clausen 
1362cb7a01acSMauro Carvalho Chehab 	/* register for interrupts */
1363cb7a01acSMauro Carvalho Chehab 	if (state->irq > 0) {
1364cb7a01acSMauro Carvalho Chehab 		/* config the Interrupt pin to be active low */
13653999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_ICONF1,
1366cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_ACTIVE_LOW |
1367cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_PSYNC_ONLY);
1368cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13693999e5d0SLars-Peter Clausen 			goto out_unlock;
1370cb7a01acSMauro Carvalho Chehab 
13713999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
1372cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13733999e5d0SLars-Peter Clausen 			goto out_unlock;
1374cb7a01acSMauro Carvalho Chehab 
13753999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
1376cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13773999e5d0SLars-Peter Clausen 			goto out_unlock;
1378cb7a01acSMauro Carvalho Chehab 
1379cb7a01acSMauro Carvalho Chehab 		/* enable AD change interrupts interrupts */
13803999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR3,
1381cb7a01acSMauro Carvalho Chehab 						ADV7180_IRQ3_AD_CHANGE);
1382cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13833999e5d0SLars-Peter Clausen 			goto out_unlock;
1384cb7a01acSMauro Carvalho Chehab 
13853999e5d0SLars-Peter Clausen 		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
1386cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
13873999e5d0SLars-Peter Clausen 			goto out_unlock;
1388cb7a01acSMauro Carvalho Chehab 	}
1389cb7a01acSMauro Carvalho Chehab 
13903999e5d0SLars-Peter Clausen out_unlock:
13913999e5d0SLars-Peter Clausen 	mutex_unlock(&state->mutex);
1392df065b37SAlexey Khoroshilov 
1393df065b37SAlexey Khoroshilov 	return ret;
1394cb7a01acSMauro Carvalho Chehab }
1395cb7a01acSMauro Carvalho Chehab 
adv7180_probe(struct i2c_client * client)1396f2478d6eSUwe Kleine-König static int adv7180_probe(struct i2c_client *client)
1397cb7a01acSMauro Carvalho Chehab {
1398f2478d6eSUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
1399ed771d75SMatthew Michilot 	struct device_node *np = client->dev.of_node;
1400cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state;
1401cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1402cb7a01acSMauro Carvalho Chehab 	int ret;
1403cb7a01acSMauro Carvalho Chehab 
1404cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1405cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
1406cb7a01acSMauro Carvalho Chehab 		return -EIO;
1407cb7a01acSMauro Carvalho Chehab 
1408c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
14097657e064SFabio Estevam 	if (state == NULL)
14107657e064SFabio Estevam 		return -ENOMEM;
1411cb7a01acSMauro Carvalho Chehab 
14123999e5d0SLars-Peter Clausen 	state->client = client;
14136457b626SNiklas Söderlund 	state->field = V4L2_FIELD_ALTERNATE;
1414f5dde49bSLars-Peter Clausen 	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
14153999e5d0SLars-Peter Clausen 
141665d9e14aSSteve Longerbeam 	state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
141765d9e14aSSteve Longerbeam 						   GPIOD_OUT_HIGH);
141865d9e14aSSteve Longerbeam 	if (IS_ERR(state->pwdn_gpio)) {
141965d9e14aSSteve Longerbeam 		ret = PTR_ERR(state->pwdn_gpio);
142065d9e14aSSteve Longerbeam 		v4l_err(client, "request for power pin failed: %d\n", ret);
142165d9e14aSSteve Longerbeam 		return ret;
142265d9e14aSSteve Longerbeam 	}
142365d9e14aSSteve Longerbeam 
1424abb7c7c2SFrieder Schrempf 	state->rst_gpio = devm_gpiod_get_optional(&client->dev, "reset",
1425abb7c7c2SFrieder Schrempf 						  GPIOD_OUT_HIGH);
1426abb7c7c2SFrieder Schrempf 	if (IS_ERR(state->rst_gpio)) {
1427abb7c7c2SFrieder Schrempf 		ret = PTR_ERR(state->rst_gpio);
1428abb7c7c2SFrieder Schrempf 		v4l_err(client, "request for reset pin failed: %d\n", ret);
1429abb7c7c2SFrieder Schrempf 		return ret;
1430abb7c7c2SFrieder Schrempf 	}
1431abb7c7c2SFrieder Schrempf 
1432ed771d75SMatthew Michilot 	if (of_property_read_bool(np, "adv,force-bt656-4"))
1433ed771d75SMatthew Michilot 		state->force_bt656_4 = true;
1434ed771d75SMatthew Michilot 
1435b37135e3SLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
143631b9754cSWolfram Sang 		state->csi_client = i2c_new_dummy_device(client->adapter,
1437b37135e3SLars-Peter Clausen 				ADV7180_DEFAULT_CSI_I2C_ADDR);
143831b9754cSWolfram Sang 		if (IS_ERR(state->csi_client))
143931b9754cSWolfram Sang 			return PTR_ERR(state->csi_client);
1440b37135e3SLars-Peter Clausen 	}
1441b37135e3SLars-Peter Clausen 
1442851a54efSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
144331b9754cSWolfram Sang 		state->vpp_client = i2c_new_dummy_device(client->adapter,
1444851a54efSLars-Peter Clausen 				ADV7180_DEFAULT_VPP_I2C_ADDR);
144531b9754cSWolfram Sang 		if (IS_ERR(state->vpp_client)) {
144631b9754cSWolfram Sang 			ret = PTR_ERR(state->vpp_client);
1447851a54efSLars-Peter Clausen 			goto err_unregister_csi_client;
1448851a54efSLars-Peter Clausen 		}
1449851a54efSLars-Peter Clausen 	}
1450851a54efSLars-Peter Clausen 
1451cb7a01acSMauro Carvalho Chehab 	state->irq = client->irq;
1452cb7a01acSMauro Carvalho Chehab 	mutex_init(&state->mutex);
1453937feeedSHans Verkuil 	state->curr_norm = V4L2_STD_NTSC;
1454f5dde49bSLars-Peter Clausen 	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
1455e246c333SLars-Peter Clausen 		state->powered = true;
1456f5dde49bSLars-Peter Clausen 	else
1457f5dde49bSLars-Peter Clausen 		state->powered = false;
1458cb7a01acSMauro Carvalho Chehab 	state->input = 0;
1459cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
1460cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
14615cc72c47SAkinobu Mita 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
1462cb7a01acSMauro Carvalho Chehab 
1463cb7a01acSMauro Carvalho Chehab 	ret = adv7180_init_controls(state);
1464cb7a01acSMauro Carvalho Chehab 	if (ret)
1465851a54efSLars-Peter Clausen 		goto err_unregister_vpp_client;
1466d5d51a82SLars-Peter Clausen 
1467d5d51a82SLars-Peter Clausen 	state->pad.flags = MEDIA_PAD_FL_SOURCE;
1468ca0fa5f0SHans Verkuil 	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
1469ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
1470cb7a01acSMauro Carvalho Chehab 	if (ret)
1471cb7a01acSMauro Carvalho Chehab 		goto err_free_ctrl;
1472fa5b7945SLars-Peter Clausen 
1473d5d51a82SLars-Peter Clausen 	ret = init_device(state);
1474d5d51a82SLars-Peter Clausen 	if (ret)
1475d5d51a82SLars-Peter Clausen 		goto err_media_entity_cleanup;
1476d5d51a82SLars-Peter Clausen 
1477fa5721d1SLars-Peter Clausen 	if (state->irq) {
1478fa5721d1SLars-Peter Clausen 		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
1479f3e991d4SLars-Peter Clausen 					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
1480f3e991d4SLars-Peter Clausen 					   KBUILD_MODNAME, state);
1481fa5721d1SLars-Peter Clausen 		if (ret)
1482d5d51a82SLars-Peter Clausen 			goto err_media_entity_cleanup;
1483fa5721d1SLars-Peter Clausen 	}
1484fa5721d1SLars-Peter Clausen 
1485fa5b7945SLars-Peter Clausen 	ret = v4l2_async_register_subdev(sd);
1486fa5b7945SLars-Peter Clausen 	if (ret)
1487fa5b7945SLars-Peter Clausen 		goto err_free_irq;
1488fa5b7945SLars-Peter Clausen 
1489f7b96a9fSFabio Estevam 	mutex_lock(&state->mutex);
1490f7b96a9fSFabio Estevam 	ret = adv7180_read(state, ADV7180_REG_IDENT);
1491f7b96a9fSFabio Estevam 	mutex_unlock(&state->mutex);
1492f7b96a9fSFabio Estevam 	if (ret < 0)
1493f7b96a9fSFabio Estevam 		goto err_v4l2_async_unregister;
1494f7b96a9fSFabio Estevam 
1495f7b96a9fSFabio Estevam 	v4l_info(client, "chip id 0x%x found @ 0x%02x (%s)\n",
1496f7b96a9fSFabio Estevam 		 ret, client->addr, client->adapter->name);
1497b19c25f4SFabio Estevam 
1498cb7a01acSMauro Carvalho Chehab 	return 0;
1499cb7a01acSMauro Carvalho Chehab 
1500f7b96a9fSFabio Estevam err_v4l2_async_unregister:
1501f7b96a9fSFabio Estevam 	v4l2_async_unregister_subdev(sd);
1502fa5b7945SLars-Peter Clausen err_free_irq:
1503fa5b7945SLars-Peter Clausen 	if (state->irq > 0)
1504fa5b7945SLars-Peter Clausen 		free_irq(client->irq, state);
1505d5d51a82SLars-Peter Clausen err_media_entity_cleanup:
1506d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1507cb7a01acSMauro Carvalho Chehab err_free_ctrl:
1508cb7a01acSMauro Carvalho Chehab 	adv7180_exit_controls(state);
1509851a54efSLars-Peter Clausen err_unregister_vpp_client:
1510851a54efSLars-Peter Clausen 	i2c_unregister_device(state->vpp_client);
1511b37135e3SLars-Peter Clausen err_unregister_csi_client:
1512b37135e3SLars-Peter Clausen 	i2c_unregister_device(state->csi_client);
1513297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1514cb7a01acSMauro Carvalho Chehab 	return ret;
1515cb7a01acSMauro Carvalho Chehab }
1516cb7a01acSMauro Carvalho Chehab 
adv7180_remove(struct i2c_client * client)1517ed5c2f5fSUwe Kleine-König static void adv7180_remove(struct i2c_client *client)
1518cb7a01acSMauro Carvalho Chehab {
1519cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1520cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1521cb7a01acSMauro Carvalho Chehab 
1522fa5b7945SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
1523fa5b7945SLars-Peter Clausen 
15240c25534dSLars-Peter Clausen 	if (state->irq > 0)
1525cb7a01acSMauro Carvalho Chehab 		free_irq(client->irq, state);
1526cb7a01acSMauro Carvalho Chehab 
1527d5d51a82SLars-Peter Clausen 	media_entity_cleanup(&sd->entity);
1528b13f4af2SLars-Peter Clausen 	adv7180_exit_controls(state);
1529b37135e3SLars-Peter Clausen 
1530851a54efSLars-Peter Clausen 	i2c_unregister_device(state->vpp_client);
1531b37135e3SLars-Peter Clausen 	i2c_unregister_device(state->csi_client);
1532b37135e3SLars-Peter Clausen 
1533abb7c7c2SFrieder Schrempf 	adv7180_set_reset_pin(state, true);
153465d9e14aSSteve Longerbeam 	adv7180_set_power_pin(state, false);
153565d9e14aSSteve Longerbeam 
1536297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
1537cb7a01acSMauro Carvalho Chehab }
1538cb7a01acSMauro Carvalho Chehab 
1539cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = {
1540f5dde49bSLars-Peter Clausen 	{ "adv7180", (kernel_ulong_t)&adv7180_info },
1541281ddc3cSUlrich Hecht 	{ "adv7180cp", (kernel_ulong_t)&adv7180_info },
1542281ddc3cSUlrich Hecht 	{ "adv7180st", (kernel_ulong_t)&adv7180_info },
1543c5ef8f8cSLars-Peter Clausen 	{ "adv7182", (kernel_ulong_t)&adv7182_info },
1544bf7dcb80SLars-Peter Clausen 	{ "adv7280", (kernel_ulong_t)&adv7280_info },
1545b37135e3SLars-Peter Clausen 	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
1546bf7dcb80SLars-Peter Clausen 	{ "adv7281", (kernel_ulong_t)&adv7281_info },
1547b37135e3SLars-Peter Clausen 	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
1548b37135e3SLars-Peter Clausen 	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
1549851a54efSLars-Peter Clausen 	{ "adv7282", (kernel_ulong_t)&adv7282_info },
1550851a54efSLars-Peter Clausen 	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
1551cb7a01acSMauro Carvalho Chehab 	{},
1552cb7a01acSMauro Carvalho Chehab };
1553f5dde49bSLars-Peter Clausen MODULE_DEVICE_TABLE(i2c, adv7180_id);
1554cb7a01acSMauro Carvalho Chehab 
1555cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
adv7180_suspend(struct device * dev)1556cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev)
1557cb7a01acSMauro Carvalho Chehab {
155817ed3c90SKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
1559e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
1560cb7a01acSMauro Carvalho Chehab 
15613999e5d0SLars-Peter Clausen 	return adv7180_set_power(state, false);
1562cb7a01acSMauro Carvalho Chehab }
1563cb7a01acSMauro Carvalho Chehab 
adv7180_resume(struct device * dev)1564cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev)
1565cb7a01acSMauro Carvalho Chehab {
156617ed3c90SKrzysztof Kozlowski 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
1567cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
1568cb7a01acSMauro Carvalho Chehab 	int ret;
1569cb7a01acSMauro Carvalho Chehab 
15703999e5d0SLars-Peter Clausen 	ret = init_device(state);
1571cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
1572cb7a01acSMauro Carvalho Chehab 		return ret;
1573c18818e9SLars-Peter Clausen 
1574c18818e9SLars-Peter Clausen 	ret = adv7180_set_power(state, state->powered);
1575c18818e9SLars-Peter Clausen 	if (ret)
1576c18818e9SLars-Peter Clausen 		return ret;
1577c18818e9SLars-Peter Clausen 
1578cb7a01acSMauro Carvalho Chehab 	return 0;
1579cb7a01acSMauro Carvalho Chehab }
1580cc1088dcSLars-Peter Clausen 
1581cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
1582cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops)
1583cc1088dcSLars-Peter Clausen 
1584cc1088dcSLars-Peter Clausen #else
1585cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL
1586cb7a01acSMauro Carvalho Chehab #endif
1587cb7a01acSMauro Carvalho Chehab 
1588250121d3SBen Dooks #ifdef CONFIG_OF
1589250121d3SBen Dooks static const struct of_device_id adv7180_of_id[] = {
1590250121d3SBen Dooks 	{ .compatible = "adi,adv7180", },
1591ce1ec5c0SUlrich Hecht 	{ .compatible = "adi,adv7180cp", },
1592ce1ec5c0SUlrich Hecht 	{ .compatible = "adi,adv7180st", },
1593bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7182", },
1594bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280", },
1595bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7280-m", },
1596bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281", },
1597bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-m", },
1598bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7281-ma", },
1599bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282", },
1600bf14e74cSJulian Scheel 	{ .compatible = "adi,adv7282-m", },
1601250121d3SBen Dooks 	{ },
1602250121d3SBen Dooks };
1603250121d3SBen Dooks 
1604250121d3SBen Dooks MODULE_DEVICE_TABLE(of, adv7180_of_id);
1605250121d3SBen Dooks #endif
1606250121d3SBen Dooks 
1607cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = {
1608cb7a01acSMauro Carvalho Chehab 	.driver = {
1609cb7a01acSMauro Carvalho Chehab 		   .name = KBUILD_MODNAME,
1610cc1088dcSLars-Peter Clausen 		   .pm = ADV7180_PM_OPS,
1611250121d3SBen Dooks 		   .of_match_table = of_match_ptr(adv7180_of_id),
1612cb7a01acSMauro Carvalho Chehab 		   },
1613*aaeb31c0SUwe Kleine-König 	.probe = adv7180_probe,
16144c62e976SGreg Kroah-Hartman 	.remove = adv7180_remove,
1615cb7a01acSMauro Carvalho Chehab 	.id_table = adv7180_id,
1616cb7a01acSMauro Carvalho Chehab };
1617cb7a01acSMauro Carvalho Chehab 
1618cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver);
1619cb7a01acSMauro Carvalho Chehab 
1620cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
1621cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories");
1622cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1623