xref: /openbmc/linux/drivers/media/i2c/adv7180.c (revision fa5721d1)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * adv7180.c Analog Devices ADV7180 video decoder driver
3cb7a01acSMauro Carvalho Chehab  * Copyright (c) 2009 Intel Corporation
4cccb83f7SVladimir Barinov  * Copyright (C) 2013 Cogent Embedded, Inc.
5cccb83f7SVladimir Barinov  * Copyright (C) 2013 Renesas Solutions Corp.
6cb7a01acSMauro Carvalho Chehab  *
7cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
8cb7a01acSMauro Carvalho Chehab  * it under the terms of the GNU General Public License version 2 as
9cb7a01acSMauro Carvalho Chehab  * published by the Free Software Foundation.
10cb7a01acSMauro Carvalho Chehab  *
11cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
12cb7a01acSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14cb7a01acSMauro Carvalho Chehab  * GNU General Public License for more details.
15cb7a01acSMauro Carvalho Chehab  *
16cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
17cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
18cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19cb7a01acSMauro Carvalho Chehab  */
20cb7a01acSMauro Carvalho Chehab 
21cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/errno.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
25cb7a01acSMauro Carvalho Chehab #include <linux/interrupt.h>
26cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
27cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
29cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
32cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h>
33cb7a01acSMauro Carvalho Chehab 
34cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_REG			0x00
35cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM	0x00
36cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
37cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM	0x20
38cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM	0x30
39cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_NTSC_J			0x40
40cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_NTSC_M			0x50
41cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL60			0x60
42cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_NTSC_443			0x70
43cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_BG			0x80
44cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_N			0x90
45cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_M			0xa0
46cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_M_PED			0xb0
47cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_COMB_N		0xc0
48cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED		0xd0
49cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_SECAM			0xe0
50cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_PAL_SECAM_PED		0xf0
51cb7a01acSMauro Carvalho Chehab #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
52cb7a01acSMauro Carvalho Chehab 
53cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG		0x04
54cb7a01acSMauro Carvalho Chehab #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
55cb7a01acSMauro Carvalho Chehab 
56cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_ENABLE_REG			0x07
57cb7a01acSMauro Carvalho Chehab #define ADV7180_AUTODETECT_DEFAULT			0x7f
58cb7a01acSMauro Carvalho Chehab /* Contrast */
59cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_REG		0x08	/*Unsigned */
60cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MIN		0
61cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_DEF		128
62cb7a01acSMauro Carvalho Chehab #define ADV7180_CON_MAX		255
63cb7a01acSMauro Carvalho Chehab /* Brightness*/
64cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_REG		0x0a	/*Signed */
65cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MIN		-128
66cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_DEF		0
67cb7a01acSMauro Carvalho Chehab #define ADV7180_BRI_MAX		127
68cb7a01acSMauro Carvalho Chehab /* Hue */
69cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_REG		0x0b	/*Signed, inverted */
70cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MIN		-127
71cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_DEF		0
72cb7a01acSMauro Carvalho Chehab #define ADV7180_HUE_MAX		128
73cb7a01acSMauro Carvalho Chehab 
74cb7a01acSMauro Carvalho Chehab #define ADV7180_ADI_CTRL_REG				0x0e
75cb7a01acSMauro Carvalho Chehab #define ADV7180_ADI_CTRL_IRQ_SPACE			0x20
76cb7a01acSMauro Carvalho Chehab 
77cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_REG		0x0f
78cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_ON		0x04
79cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_OFF		0x24
80cb7a01acSMauro Carvalho Chehab #define ADV7180_PWR_MAN_RES		0x80
81cb7a01acSMauro Carvalho Chehab 
82cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_REG				0x10
83cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_IN_LOCK		0x01
84cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_MASK	0x70
85cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
86cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
87cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_M	0x20
88cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_60	0x30
89cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_B_G	0x40
90cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM	0x50
91cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
92cb7a01acSMauro Carvalho Chehab #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
93cb7a01acSMauro Carvalho Chehab 
94cb7a01acSMauro Carvalho Chehab #define ADV7180_IDENT_REG 0x11
95cb7a01acSMauro Carvalho Chehab #define ADV7180_ID_7180 0x18
96cb7a01acSMauro Carvalho Chehab 
97cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ADI		0x40
98cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_LOW	0x01
99cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_PSYNC_ONLY	0x10
100cb7a01acSMauro Carvalho Chehab #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
101cb7a01acSMauro Carvalho Chehab /* Saturation */
102cb7a01acSMauro Carvalho Chehab #define ADV7180_SD_SAT_CB_REG	0xe3	/*Unsigned */
103cb7a01acSMauro Carvalho Chehab #define ADV7180_SD_SAT_CR_REG	0xe4	/*Unsigned */
104cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MIN		0
105cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_DEF		128
106cb7a01acSMauro Carvalho Chehab #define ADV7180_SAT_MAX		255
107cb7a01acSMauro Carvalho Chehab 
108cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_LOCK	0x01
109cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ1_UNLOCK	0x02
110cb7a01acSMauro Carvalho Chehab #define ADV7180_ISR1_ADI	0x42
111cb7a01acSMauro Carvalho Chehab #define ADV7180_ICR1_ADI	0x43
112cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR1_ADI	0x44
113cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR2_ADI	0x48
114cb7a01acSMauro Carvalho Chehab #define ADV7180_IRQ3_AD_CHANGE	0x08
115cb7a01acSMauro Carvalho Chehab #define ADV7180_ISR3_ADI	0x4A
116cb7a01acSMauro Carvalho Chehab #define ADV7180_ICR3_ADI	0x4B
117cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR3_ADI	0x4C
118cb7a01acSMauro Carvalho Chehab #define ADV7180_IMR4_ADI	0x50
119cb7a01acSMauro Carvalho Chehab 
120cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_REG	0xE6
121cb7a01acSMauro Carvalho Chehab #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
122cb7a01acSMauro Carvalho Chehab 
123cb7a01acSMauro Carvalho Chehab struct adv7180_state {
124cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;
125cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev	sd;
126cb7a01acSMauro Carvalho Chehab 	struct mutex		mutex; /* mutual excl. when accessing chip */
127cb7a01acSMauro Carvalho Chehab 	int			irq;
128cb7a01acSMauro Carvalho Chehab 	v4l2_std_id		curr_norm;
129cb7a01acSMauro Carvalho Chehab 	bool			autodetect;
130e246c333SLars-Peter Clausen 	bool			powered;
131cb7a01acSMauro Carvalho Chehab 	u8			input;
132cb7a01acSMauro Carvalho Chehab };
133cb7a01acSMauro Carvalho Chehab #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
134cb7a01acSMauro Carvalho Chehab 					    struct adv7180_state,	\
135cb7a01acSMauro Carvalho Chehab 					    ctrl_hdl)->sd)
136cb7a01acSMauro Carvalho Chehab 
137cb7a01acSMauro Carvalho Chehab static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
138cb7a01acSMauro Carvalho Chehab {
139b294a192SVladimir Barinov 	/* in case V4L2_IN_ST_NO_SIGNAL */
140b294a192SVladimir Barinov 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
141b294a192SVladimir Barinov 		return V4L2_STD_UNKNOWN;
142b294a192SVladimir Barinov 
143cb7a01acSMauro Carvalho Chehab 	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
144cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
145cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
146cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
147cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
148cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_M:
149cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
150cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_60:
151cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_60;
152cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_B_G:
153cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
154cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM:
155cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
156cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_PAL_COMB:
157cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
158cb7a01acSMauro Carvalho Chehab 	case ADV7180_STATUS1_AUTOD_SECAM_525:
159cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
160cb7a01acSMauro Carvalho Chehab 	default:
161cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
162cb7a01acSMauro Carvalho Chehab 	}
163cb7a01acSMauro Carvalho Chehab }
164cb7a01acSMauro Carvalho Chehab 
165cb7a01acSMauro Carvalho Chehab static int v4l2_std_to_adv7180(v4l2_std_id std)
166cb7a01acSMauro Carvalho Chehab {
167cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
168cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_PAL60;
169cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_NTSC_443)
170cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_NTSC_443;
171cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_N)
172cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_PAL_N;
173cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_M)
174cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_PAL_M;
175cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_Nc)
176cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_PAL_COMB_N;
177cb7a01acSMauro Carvalho Chehab 
178cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_PAL)
179cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_PAL_BG;
180cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC)
181cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_NTSC_M;
182cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_SECAM)
183cb7a01acSMauro Carvalho Chehab 		return ADV7180_INPUT_CONTROL_PAL_SECAM;
184cb7a01acSMauro Carvalho Chehab 
185cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
186cb7a01acSMauro Carvalho Chehab }
187cb7a01acSMauro Carvalho Chehab 
188cb7a01acSMauro Carvalho Chehab static u32 adv7180_status_to_v4l2(u8 status1)
189cb7a01acSMauro Carvalho Chehab {
190cb7a01acSMauro Carvalho Chehab 	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
191cb7a01acSMauro Carvalho Chehab 		return V4L2_IN_ST_NO_SIGNAL;
192cb7a01acSMauro Carvalho Chehab 
193cb7a01acSMauro Carvalho Chehab 	return 0;
194cb7a01acSMauro Carvalho Chehab }
195cb7a01acSMauro Carvalho Chehab 
196cb7a01acSMauro Carvalho Chehab static int __adv7180_status(struct i2c_client *client, u32 *status,
197cb7a01acSMauro Carvalho Chehab 			    v4l2_std_id *std)
198cb7a01acSMauro Carvalho Chehab {
199cb7a01acSMauro Carvalho Chehab 	int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
200cb7a01acSMauro Carvalho Chehab 
201cb7a01acSMauro Carvalho Chehab 	if (status1 < 0)
202cb7a01acSMauro Carvalho Chehab 		return status1;
203cb7a01acSMauro Carvalho Chehab 
204cb7a01acSMauro Carvalho Chehab 	if (status)
205cb7a01acSMauro Carvalho Chehab 		*status = adv7180_status_to_v4l2(status1);
206cb7a01acSMauro Carvalho Chehab 	if (std)
207cb7a01acSMauro Carvalho Chehab 		*std = adv7180_std_to_v4l2(status1);
208cb7a01acSMauro Carvalho Chehab 
209cb7a01acSMauro Carvalho Chehab 	return 0;
210cb7a01acSMauro Carvalho Chehab }
211cb7a01acSMauro Carvalho Chehab 
212cb7a01acSMauro Carvalho Chehab static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
213cb7a01acSMauro Carvalho Chehab {
214cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7180_state, sd);
215cb7a01acSMauro Carvalho Chehab }
216cb7a01acSMauro Carvalho Chehab 
217cb7a01acSMauro Carvalho Chehab static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
218cb7a01acSMauro Carvalho Chehab {
219cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
220cb7a01acSMauro Carvalho Chehab 	int err = mutex_lock_interruptible(&state->mutex);
221cb7a01acSMauro Carvalho Chehab 	if (err)
222cb7a01acSMauro Carvalho Chehab 		return err;
223cb7a01acSMauro Carvalho Chehab 
224cb7a01acSMauro Carvalho Chehab 	/* when we are interrupt driven we know the state */
225cb7a01acSMauro Carvalho Chehab 	if (!state->autodetect || state->irq > 0)
226cb7a01acSMauro Carvalho Chehab 		*std = state->curr_norm;
227cb7a01acSMauro Carvalho Chehab 	else
228cb7a01acSMauro Carvalho Chehab 		err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
229cb7a01acSMauro Carvalho Chehab 
230cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
231cb7a01acSMauro Carvalho Chehab 	return err;
232cb7a01acSMauro Carvalho Chehab }
233cb7a01acSMauro Carvalho Chehab 
234cb7a01acSMauro Carvalho Chehab static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
235cb7a01acSMauro Carvalho Chehab 			     u32 output, u32 config)
236cb7a01acSMauro Carvalho Chehab {
237cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
238cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
239cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
240cb7a01acSMauro Carvalho Chehab 
241cb7a01acSMauro Carvalho Chehab 	if (ret)
242cb7a01acSMauro Carvalho Chehab 		return ret;
243cb7a01acSMauro Carvalho Chehab 
244cb7a01acSMauro Carvalho Chehab 	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
245cb7a01acSMauro Carvalho Chehab 	 * all inputs and let the card driver take care of validation
246cb7a01acSMauro Carvalho Chehab 	 */
247cb7a01acSMauro Carvalho Chehab 	if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
248cb7a01acSMauro Carvalho Chehab 		goto out;
249cb7a01acSMauro Carvalho Chehab 
250cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG);
251cb7a01acSMauro Carvalho Chehab 
252cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
253cb7a01acSMauro Carvalho Chehab 		goto out;
254cb7a01acSMauro Carvalho Chehab 
255cb7a01acSMauro Carvalho Chehab 	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
256cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_write_byte_data(client,
257cb7a01acSMauro Carvalho Chehab 					ADV7180_INPUT_CONTROL_REG, ret | input);
258cb7a01acSMauro Carvalho Chehab 	state->input = input;
259cb7a01acSMauro Carvalho Chehab out:
260cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
261cb7a01acSMauro Carvalho Chehab 	return ret;
262cb7a01acSMauro Carvalho Chehab }
263cb7a01acSMauro Carvalho Chehab 
264cb7a01acSMauro Carvalho Chehab static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
265cb7a01acSMauro Carvalho Chehab {
266cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
267cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
268cb7a01acSMauro Carvalho Chehab 	if (ret)
269cb7a01acSMauro Carvalho Chehab 		return ret;
270cb7a01acSMauro Carvalho Chehab 
271cb7a01acSMauro Carvalho Chehab 	ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
272cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
273cb7a01acSMauro Carvalho Chehab 	return ret;
274cb7a01acSMauro Carvalho Chehab }
275cb7a01acSMauro Carvalho Chehab 
276cb7a01acSMauro Carvalho Chehab static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
277cb7a01acSMauro Carvalho Chehab {
278cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
279cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
280cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
281cb7a01acSMauro Carvalho Chehab 	if (ret)
282cb7a01acSMauro Carvalho Chehab 		return ret;
283cb7a01acSMauro Carvalho Chehab 
284cb7a01acSMauro Carvalho Chehab 	/* all standards -> autodetect */
285cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_ALL) {
286cb7a01acSMauro Carvalho Chehab 		ret =
287cb7a01acSMauro Carvalho Chehab 		    i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
288cb7a01acSMauro Carvalho Chehab 				ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
289cb7a01acSMauro Carvalho Chehab 					      | state->input);
290cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
291cb7a01acSMauro Carvalho Chehab 			goto out;
292cb7a01acSMauro Carvalho Chehab 
293cb7a01acSMauro Carvalho Chehab 		__adv7180_status(client, NULL, &state->curr_norm);
294cb7a01acSMauro Carvalho Chehab 		state->autodetect = true;
295cb7a01acSMauro Carvalho Chehab 	} else {
296cb7a01acSMauro Carvalho Chehab 		ret = v4l2_std_to_adv7180(std);
297cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
298cb7a01acSMauro Carvalho Chehab 			goto out;
299cb7a01acSMauro Carvalho Chehab 
300cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client,
301cb7a01acSMauro Carvalho Chehab 						ADV7180_INPUT_CONTROL_REG,
302cb7a01acSMauro Carvalho Chehab 						ret | state->input);
303cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
304cb7a01acSMauro Carvalho Chehab 			goto out;
305cb7a01acSMauro Carvalho Chehab 
306cb7a01acSMauro Carvalho Chehab 		state->curr_norm = std;
307cb7a01acSMauro Carvalho Chehab 		state->autodetect = false;
308cb7a01acSMauro Carvalho Chehab 	}
309cb7a01acSMauro Carvalho Chehab 	ret = 0;
310cb7a01acSMauro Carvalho Chehab out:
311cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
312cb7a01acSMauro Carvalho Chehab 	return ret;
313cb7a01acSMauro Carvalho Chehab }
314cb7a01acSMauro Carvalho Chehab 
315e246c333SLars-Peter Clausen static int adv7180_set_power(struct adv7180_state *state,
316e246c333SLars-Peter Clausen 	struct i2c_client *client, bool on)
317e246c333SLars-Peter Clausen {
318e246c333SLars-Peter Clausen 	u8 val;
319e246c333SLars-Peter Clausen 
320e246c333SLars-Peter Clausen 	if (on)
321e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_ON;
322e246c333SLars-Peter Clausen 	else
323e246c333SLars-Peter Clausen 		val = ADV7180_PWR_MAN_OFF;
324e246c333SLars-Peter Clausen 
325e246c333SLars-Peter Clausen 	return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val);
326e246c333SLars-Peter Clausen }
327e246c333SLars-Peter Clausen 
328e246c333SLars-Peter Clausen static int adv7180_s_power(struct v4l2_subdev *sd, int on)
329e246c333SLars-Peter Clausen {
330e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
331e246c333SLars-Peter Clausen 	struct i2c_client *client = v4l2_get_subdevdata(sd);
332e246c333SLars-Peter Clausen 	int ret;
333e246c333SLars-Peter Clausen 
334e246c333SLars-Peter Clausen 	ret = mutex_lock_interruptible(&state->mutex);
335e246c333SLars-Peter Clausen 	if (ret)
336e246c333SLars-Peter Clausen 		return ret;
337e246c333SLars-Peter Clausen 
338e246c333SLars-Peter Clausen 	ret = adv7180_set_power(state, client, on);
339e246c333SLars-Peter Clausen 	if (ret == 0)
340e246c333SLars-Peter Clausen 		state->powered = on;
341e246c333SLars-Peter Clausen 
342e246c333SLars-Peter Clausen 	mutex_unlock(&state->mutex);
343e246c333SLars-Peter Clausen 	return ret;
344e246c333SLars-Peter Clausen }
345e246c333SLars-Peter Clausen 
346cb7a01acSMauro Carvalho Chehab static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
347cb7a01acSMauro Carvalho Chehab {
348cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
349cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
350cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
351cb7a01acSMauro Carvalho Chehab 	int ret = mutex_lock_interruptible(&state->mutex);
352cb7a01acSMauro Carvalho Chehab 	int val;
353cb7a01acSMauro Carvalho Chehab 
354cb7a01acSMauro Carvalho Chehab 	if (ret)
355cb7a01acSMauro Carvalho Chehab 		return ret;
356cb7a01acSMauro Carvalho Chehab 	val = ctrl->val;
357cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
358cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
359cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
360cb7a01acSMauro Carvalho Chehab 		break;
361cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
362cb7a01acSMauro Carvalho Chehab 		/*Hue is inverted according to HSL chart */
363cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
364cb7a01acSMauro Carvalho Chehab 		break;
365cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
366cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
367cb7a01acSMauro Carvalho Chehab 		break;
368cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
369cb7a01acSMauro Carvalho Chehab 		/*
370cb7a01acSMauro Carvalho Chehab 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
371cb7a01acSMauro Carvalho Chehab 		 *Let's not confuse the user, everybody understands saturation
372cb7a01acSMauro Carvalho Chehab 		 */
373cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
374cb7a01acSMauro Carvalho Chehab 						val);
375cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
376cb7a01acSMauro Carvalho Chehab 			break;
377cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
378cb7a01acSMauro Carvalho Chehab 						val);
379cb7a01acSMauro Carvalho Chehab 		break;
380cb7a01acSMauro Carvalho Chehab 	default:
381cb7a01acSMauro Carvalho Chehab 		ret = -EINVAL;
382cb7a01acSMauro Carvalho Chehab 	}
383cb7a01acSMauro Carvalho Chehab 
384cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
385cb7a01acSMauro Carvalho Chehab 	return ret;
386cb7a01acSMauro Carvalho Chehab }
387cb7a01acSMauro Carvalho Chehab 
388cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
389cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7180_s_ctrl,
390cb7a01acSMauro Carvalho Chehab };
391cb7a01acSMauro Carvalho Chehab 
392cb7a01acSMauro Carvalho Chehab static int adv7180_init_controls(struct adv7180_state *state)
393cb7a01acSMauro Carvalho Chehab {
394cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
395cb7a01acSMauro Carvalho Chehab 
396cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
397cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
398cb7a01acSMauro Carvalho Chehab 			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
399cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
400cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
401cb7a01acSMauro Carvalho Chehab 			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
402cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
403cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
404cb7a01acSMauro Carvalho Chehab 			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
405cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
406cb7a01acSMauro Carvalho Chehab 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
407cb7a01acSMauro Carvalho Chehab 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
408cb7a01acSMauro Carvalho Chehab 	state->sd.ctrl_handler = &state->ctrl_hdl;
409cb7a01acSMauro Carvalho Chehab 	if (state->ctrl_hdl.error) {
410cb7a01acSMauro Carvalho Chehab 		int err = state->ctrl_hdl.error;
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->ctrl_hdl);
413cb7a01acSMauro Carvalho Chehab 		return err;
414cb7a01acSMauro Carvalho Chehab 	}
415cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
416cb7a01acSMauro Carvalho Chehab 
417cb7a01acSMauro Carvalho Chehab 	return 0;
418cb7a01acSMauro Carvalho Chehab }
419cb7a01acSMauro Carvalho Chehab static void adv7180_exit_controls(struct adv7180_state *state)
420cb7a01acSMauro Carvalho Chehab {
421cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
422cb7a01acSMauro Carvalho Chehab }
423cb7a01acSMauro Carvalho Chehab 
424cccb83f7SVladimir Barinov static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
425f5fe58fdSBoris BREZILLON 				 u32 *code)
426cccb83f7SVladimir Barinov {
427cccb83f7SVladimir Barinov 	if (index > 0)
428cccb83f7SVladimir Barinov 		return -EINVAL;
429cccb83f7SVladimir Barinov 
430f5fe58fdSBoris BREZILLON 	*code = MEDIA_BUS_FMT_YUYV8_2X8;
431cccb83f7SVladimir Barinov 
432cccb83f7SVladimir Barinov 	return 0;
433cccb83f7SVladimir Barinov }
434cccb83f7SVladimir Barinov 
435cccb83f7SVladimir Barinov static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
436cccb83f7SVladimir Barinov 			    struct v4l2_mbus_framefmt *fmt)
437cccb83f7SVladimir Barinov {
438cccb83f7SVladimir Barinov 	struct adv7180_state *state = to_state(sd);
439cccb83f7SVladimir Barinov 
440f5fe58fdSBoris BREZILLON 	fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
441cccb83f7SVladimir Barinov 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
442cccb83f7SVladimir Barinov 	fmt->field = V4L2_FIELD_INTERLACED;
443cccb83f7SVladimir Barinov 	fmt->width = 720;
444cccb83f7SVladimir Barinov 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
445cccb83f7SVladimir Barinov 
446cccb83f7SVladimir Barinov 	return 0;
447cccb83f7SVladimir Barinov }
448cccb83f7SVladimir Barinov 
449cccb83f7SVladimir Barinov static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
450cccb83f7SVladimir Barinov 				 struct v4l2_mbus_config *cfg)
451cccb83f7SVladimir Barinov {
452cccb83f7SVladimir Barinov 	/*
453cccb83f7SVladimir Barinov 	 * The ADV7180 sensor supports BT.601/656 output modes.
454cccb83f7SVladimir Barinov 	 * The BT.656 is default and not yet configurable by s/w.
455cccb83f7SVladimir Barinov 	 */
456cccb83f7SVladimir Barinov 	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
457cccb83f7SVladimir Barinov 		     V4L2_MBUS_DATA_ACTIVE_HIGH;
458cccb83f7SVladimir Barinov 	cfg->type = V4L2_MBUS_BT656;
459cccb83f7SVladimir Barinov 
460cccb83f7SVladimir Barinov 	return 0;
461cccb83f7SVladimir Barinov }
462cccb83f7SVladimir Barinov 
463cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7180_video_ops = {
4648774bed9SLaurent Pinchart 	.s_std = adv7180_s_std,
465cb7a01acSMauro Carvalho Chehab 	.querystd = adv7180_querystd,
466cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7180_g_input_status,
467cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7180_s_routing,
468cccb83f7SVladimir Barinov 	.enum_mbus_fmt = adv7180_enum_mbus_fmt,
469cccb83f7SVladimir Barinov 	.try_mbus_fmt = adv7180_mbus_fmt,
470cccb83f7SVladimir Barinov 	.g_mbus_fmt = adv7180_mbus_fmt,
471cccb83f7SVladimir Barinov 	.s_mbus_fmt = adv7180_mbus_fmt,
472cccb83f7SVladimir Barinov 	.g_mbus_config = adv7180_g_mbus_config,
473cb7a01acSMauro Carvalho Chehab };
474cb7a01acSMauro Carvalho Chehab 
475cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7180_core_ops = {
476e246c333SLars-Peter Clausen 	.s_power = adv7180_s_power,
477cb7a01acSMauro Carvalho Chehab };
478cb7a01acSMauro Carvalho Chehab 
479cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7180_ops = {
480cb7a01acSMauro Carvalho Chehab 	.core = &adv7180_core_ops,
481cb7a01acSMauro Carvalho Chehab 	.video = &adv7180_video_ops,
482cb7a01acSMauro Carvalho Chehab };
483cb7a01acSMauro Carvalho Chehab 
4840c25534dSLars-Peter Clausen static irqreturn_t adv7180_irq(int irq, void *devid)
485cb7a01acSMauro Carvalho Chehab {
4860c25534dSLars-Peter Clausen 	struct adv7180_state *state = devid;
487cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
488cb7a01acSMauro Carvalho Chehab 	u8 isr3;
489cb7a01acSMauro Carvalho Chehab 
490cb7a01acSMauro Carvalho Chehab 	mutex_lock(&state->mutex);
491cb7a01acSMauro Carvalho Chehab 	i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
492cb7a01acSMauro Carvalho Chehab 				  ADV7180_ADI_CTRL_IRQ_SPACE);
493cb7a01acSMauro Carvalho Chehab 	isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
494cb7a01acSMauro Carvalho Chehab 	/* clear */
495cb7a01acSMauro Carvalho Chehab 	i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
496cb7a01acSMauro Carvalho Chehab 	i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
497cb7a01acSMauro Carvalho Chehab 
498cb7a01acSMauro Carvalho Chehab 	if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
499cb7a01acSMauro Carvalho Chehab 		__adv7180_status(client, NULL, &state->curr_norm);
500cb7a01acSMauro Carvalho Chehab 	mutex_unlock(&state->mutex);
501cb7a01acSMauro Carvalho Chehab 
502cb7a01acSMauro Carvalho Chehab 	return IRQ_HANDLED;
503cb7a01acSMauro Carvalho Chehab }
504cb7a01acSMauro Carvalho Chehab 
505cb7a01acSMauro Carvalho Chehab static int init_device(struct i2c_client *client, struct adv7180_state *state)
506cb7a01acSMauro Carvalho Chehab {
507cb7a01acSMauro Carvalho Chehab 	int ret;
508cb7a01acSMauro Carvalho Chehab 
509cb7a01acSMauro Carvalho Chehab 	/* Initialize adv7180 */
510cb7a01acSMauro Carvalho Chehab 	/* Enable autodetection */
511cb7a01acSMauro Carvalho Chehab 	if (state->autodetect) {
512cb7a01acSMauro Carvalho Chehab 		ret =
513cb7a01acSMauro Carvalho Chehab 		    i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
514cb7a01acSMauro Carvalho Chehab 				ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
515cb7a01acSMauro Carvalho Chehab 					      | state->input);
516cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
517cb7a01acSMauro Carvalho Chehab 			return ret;
518cb7a01acSMauro Carvalho Chehab 
519cb7a01acSMauro Carvalho Chehab 		ret =
520cb7a01acSMauro Carvalho Chehab 		    i2c_smbus_write_byte_data(client,
521cb7a01acSMauro Carvalho Chehab 					      ADV7180_AUTODETECT_ENABLE_REG,
522cb7a01acSMauro Carvalho Chehab 					      ADV7180_AUTODETECT_DEFAULT);
523cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
524cb7a01acSMauro Carvalho Chehab 			return ret;
525cb7a01acSMauro Carvalho Chehab 	} else {
526cb7a01acSMauro Carvalho Chehab 		ret = v4l2_std_to_adv7180(state->curr_norm);
527cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
528cb7a01acSMauro Carvalho Chehab 			return ret;
529cb7a01acSMauro Carvalho Chehab 
530cb7a01acSMauro Carvalho Chehab 		ret =
531cb7a01acSMauro Carvalho Chehab 		    i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
532cb7a01acSMauro Carvalho Chehab 					      ret | state->input);
533cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
534cb7a01acSMauro Carvalho Chehab 			return ret;
535cb7a01acSMauro Carvalho Chehab 
536cb7a01acSMauro Carvalho Chehab 	}
537cb7a01acSMauro Carvalho Chehab 	/* ITU-R BT.656-4 compatible */
538cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_write_byte_data(client,
539cb7a01acSMauro Carvalho Chehab 			ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
540cb7a01acSMauro Carvalho Chehab 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
541cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
542cb7a01acSMauro Carvalho Chehab 		return ret;
543cb7a01acSMauro Carvalho Chehab 
544cb7a01acSMauro Carvalho Chehab 	/* Manually set V bit end position in NTSC mode */
545cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_write_byte_data(client,
546cb7a01acSMauro Carvalho Chehab 					ADV7180_NTSC_V_BIT_END_REG,
547cb7a01acSMauro Carvalho Chehab 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
548cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
549cb7a01acSMauro Carvalho Chehab 		return ret;
550cb7a01acSMauro Carvalho Chehab 
551cb7a01acSMauro Carvalho Chehab 	/* read current norm */
552cb7a01acSMauro Carvalho Chehab 	__adv7180_status(client, NULL, &state->curr_norm);
553cb7a01acSMauro Carvalho Chehab 
554cb7a01acSMauro Carvalho Chehab 	/* register for interrupts */
555cb7a01acSMauro Carvalho Chehab 	if (state->irq > 0) {
556cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
557cb7a01acSMauro Carvalho Chehab 						ADV7180_ADI_CTRL_IRQ_SPACE);
558cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
559df065b37SAlexey Khoroshilov 			goto err;
560cb7a01acSMauro Carvalho Chehab 
561cb7a01acSMauro Carvalho Chehab 		/* config the Interrupt pin to be active low */
562cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
563cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_ACTIVE_LOW |
564cb7a01acSMauro Carvalho Chehab 						ADV7180_ICONF1_PSYNC_ONLY);
565cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
566df065b37SAlexey Khoroshilov 			goto err;
567cb7a01acSMauro Carvalho Chehab 
568cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
569cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
570df065b37SAlexey Khoroshilov 			goto err;
571cb7a01acSMauro Carvalho Chehab 
572cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
573cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
574df065b37SAlexey Khoroshilov 			goto err;
575cb7a01acSMauro Carvalho Chehab 
576cb7a01acSMauro Carvalho Chehab 		/* enable AD change interrupts interrupts */
577cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
578cb7a01acSMauro Carvalho Chehab 						ADV7180_IRQ3_AD_CHANGE);
579cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
580df065b37SAlexey Khoroshilov 			goto err;
581cb7a01acSMauro Carvalho Chehab 
582cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
583cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
584df065b37SAlexey Khoroshilov 			goto err;
585cb7a01acSMauro Carvalho Chehab 
586cb7a01acSMauro Carvalho Chehab 		ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
587cb7a01acSMauro Carvalho Chehab 						0);
588cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
589df065b37SAlexey Khoroshilov 			goto err;
590cb7a01acSMauro Carvalho Chehab 	}
591cb7a01acSMauro Carvalho Chehab 
592cb7a01acSMauro Carvalho Chehab 	return 0;
593df065b37SAlexey Khoroshilov 
594df065b37SAlexey Khoroshilov err:
595df065b37SAlexey Khoroshilov 	return ret;
596cb7a01acSMauro Carvalho Chehab }
597cb7a01acSMauro Carvalho Chehab 
5984c62e976SGreg Kroah-Hartman static int adv7180_probe(struct i2c_client *client,
599cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
600cb7a01acSMauro Carvalho Chehab {
601cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state;
602cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
603cb7a01acSMauro Carvalho Chehab 	int ret;
604cb7a01acSMauro Carvalho Chehab 
605cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
606cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
607cb7a01acSMauro Carvalho Chehab 		return -EIO;
608cb7a01acSMauro Carvalho Chehab 
609cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
610cb7a01acSMauro Carvalho Chehab 		 client->addr, client->adapter->name);
611cb7a01acSMauro Carvalho Chehab 
612c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
6137657e064SFabio Estevam 	if (state == NULL)
6147657e064SFabio Estevam 		return -ENOMEM;
615cb7a01acSMauro Carvalho Chehab 
616cb7a01acSMauro Carvalho Chehab 	state->irq = client->irq;
617cb7a01acSMauro Carvalho Chehab 	mutex_init(&state->mutex);
618cb7a01acSMauro Carvalho Chehab 	state->autodetect = true;
619e246c333SLars-Peter Clausen 	state->powered = true;
620cb7a01acSMauro Carvalho Chehab 	state->input = 0;
621cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
622cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
623cb7a01acSMauro Carvalho Chehab 
624cb7a01acSMauro Carvalho Chehab 	ret = adv7180_init_controls(state);
625cb7a01acSMauro Carvalho Chehab 	if (ret)
626cb7a01acSMauro Carvalho Chehab 		goto err_unreg_subdev;
627cb7a01acSMauro Carvalho Chehab 	ret = init_device(client, state);
628cb7a01acSMauro Carvalho Chehab 	if (ret)
629cb7a01acSMauro Carvalho Chehab 		goto err_free_ctrl;
630fa5b7945SLars-Peter Clausen 
631fa5721d1SLars-Peter Clausen 	if (state->irq) {
632fa5721d1SLars-Peter Clausen 		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
633fa5721d1SLars-Peter Clausen 					   IRQF_ONESHOT, KBUILD_MODNAME, state);
634fa5721d1SLars-Peter Clausen 		if (ret)
635fa5721d1SLars-Peter Clausen 			goto err_free_ctrl;
636fa5721d1SLars-Peter Clausen 	}
637fa5721d1SLars-Peter Clausen 
638fa5b7945SLars-Peter Clausen 	ret = v4l2_async_register_subdev(sd);
639fa5b7945SLars-Peter Clausen 	if (ret)
640fa5b7945SLars-Peter Clausen 		goto err_free_irq;
641fa5b7945SLars-Peter Clausen 
642cb7a01acSMauro Carvalho Chehab 	return 0;
643cb7a01acSMauro Carvalho Chehab 
644fa5b7945SLars-Peter Clausen err_free_irq:
645fa5b7945SLars-Peter Clausen 	if (state->irq > 0)
646fa5b7945SLars-Peter Clausen 		free_irq(client->irq, state);
647cb7a01acSMauro Carvalho Chehab err_free_ctrl:
648cb7a01acSMauro Carvalho Chehab 	adv7180_exit_controls(state);
649cb7a01acSMauro Carvalho Chehab err_unreg_subdev:
650297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
651cb7a01acSMauro Carvalho Chehab 	return ret;
652cb7a01acSMauro Carvalho Chehab }
653cb7a01acSMauro Carvalho Chehab 
6544c62e976SGreg Kroah-Hartman static int adv7180_remove(struct i2c_client *client)
655cb7a01acSMauro Carvalho Chehab {
656cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
657cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
658cb7a01acSMauro Carvalho Chehab 
659fa5b7945SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
660fa5b7945SLars-Peter Clausen 
6610c25534dSLars-Peter Clausen 	if (state->irq > 0)
662cb7a01acSMauro Carvalho Chehab 		free_irq(client->irq, state);
663cb7a01acSMauro Carvalho Chehab 
664b13f4af2SLars-Peter Clausen 	adv7180_exit_controls(state);
665297a0ae3SLars-Peter Clausen 	mutex_destroy(&state->mutex);
666cb7a01acSMauro Carvalho Chehab 	return 0;
667cb7a01acSMauro Carvalho Chehab }
668cb7a01acSMauro Carvalho Chehab 
669cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7180_id[] = {
670cb7a01acSMauro Carvalho Chehab 	{KBUILD_MODNAME, 0},
671cb7a01acSMauro Carvalho Chehab 	{},
672cb7a01acSMauro Carvalho Chehab };
673cb7a01acSMauro Carvalho Chehab 
674cc1088dcSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
675cc1088dcSLars-Peter Clausen static int adv7180_suspend(struct device *dev)
676cb7a01acSMauro Carvalho Chehab {
677cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
678e246c333SLars-Peter Clausen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
679e246c333SLars-Peter Clausen 	struct adv7180_state *state = to_state(sd);
680cb7a01acSMauro Carvalho Chehab 
681e246c333SLars-Peter Clausen 	return adv7180_set_power(state, client, false);
682cb7a01acSMauro Carvalho Chehab }
683cb7a01acSMauro Carvalho Chehab 
684cc1088dcSLars-Peter Clausen static int adv7180_resume(struct device *dev)
685cb7a01acSMauro Carvalho Chehab {
686cc1088dcSLars-Peter Clausen 	struct i2c_client *client = to_i2c_client(dev);
687cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
688cb7a01acSMauro Carvalho Chehab 	struct adv7180_state *state = to_state(sd);
689cb7a01acSMauro Carvalho Chehab 	int ret;
690cb7a01acSMauro Carvalho Chehab 
691e246c333SLars-Peter Clausen 	if (state->powered) {
692e246c333SLars-Peter Clausen 		ret = adv7180_set_power(state, client, true);
693e246c333SLars-Peter Clausen 		if (ret)
694cb7a01acSMauro Carvalho Chehab 			return ret;
695e246c333SLars-Peter Clausen 	}
696cb7a01acSMauro Carvalho Chehab 	ret = init_device(client, state);
697cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
698cb7a01acSMauro Carvalho Chehab 		return ret;
699cb7a01acSMauro Carvalho Chehab 	return 0;
700cb7a01acSMauro Carvalho Chehab }
701cc1088dcSLars-Peter Clausen 
702cc1088dcSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
703cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS (&adv7180_pm_ops)
704cc1088dcSLars-Peter Clausen 
705cc1088dcSLars-Peter Clausen #else
706cc1088dcSLars-Peter Clausen #define ADV7180_PM_OPS NULL
707cb7a01acSMauro Carvalho Chehab #endif
708cb7a01acSMauro Carvalho Chehab 
709cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adv7180_id);
710cb7a01acSMauro Carvalho Chehab 
711cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7180_driver = {
712cb7a01acSMauro Carvalho Chehab 	.driver = {
713cb7a01acSMauro Carvalho Chehab 		   .owner = THIS_MODULE,
714cb7a01acSMauro Carvalho Chehab 		   .name = KBUILD_MODNAME,
715cc1088dcSLars-Peter Clausen 		   .pm = ADV7180_PM_OPS,
716cb7a01acSMauro Carvalho Chehab 		   },
717cb7a01acSMauro Carvalho Chehab 	.probe = adv7180_probe,
7184c62e976SGreg Kroah-Hartman 	.remove = adv7180_remove,
719cb7a01acSMauro Carvalho Chehab 	.id_table = adv7180_id,
720cb7a01acSMauro Carvalho Chehab };
721cb7a01acSMauro Carvalho Chehab 
722cb7a01acSMauro Carvalho Chehab module_i2c_driver(adv7180_driver);
723cb7a01acSMauro Carvalho Chehab 
724cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
725cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mocean Laboratories");
726cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
727