xref: /openbmc/linux/drivers/media/i2c/adv7183.c (revision 4c62e976)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * adv7183.c Analog Devices ADV7183 video decoder driver
3cb7a01acSMauro Carvalho Chehab  *
4cb7a01acSMauro Carvalho Chehab  * Copyright (c) 2011 Analog Devices Inc.
5cb7a01acSMauro Carvalho Chehab  *
6cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
7cb7a01acSMauro Carvalho Chehab  * it under the terms of the GNU General Public License version 2 as
8cb7a01acSMauro Carvalho Chehab  * published by the Free Software Foundation.
9cb7a01acSMauro Carvalho Chehab  *
10cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
11cb7a01acSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13cb7a01acSMauro Carvalho Chehab  * GNU General Public License for more details.
14cb7a01acSMauro Carvalho Chehab  *
15cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
16cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
17cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18cb7a01acSMauro Carvalho Chehab  */
19cb7a01acSMauro Carvalho Chehab 
20cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/errno.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/gpio.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
25cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
26cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
27cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
28cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
29cb7a01acSMauro Carvalho Chehab 
30cb7a01acSMauro Carvalho Chehab #include <media/adv7183.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h>
32cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
33cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
34cb7a01acSMauro Carvalho Chehab 
35cb7a01acSMauro Carvalho Chehab #include "adv7183_regs.h"
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab struct adv7183 {
38cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
39cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
40cb7a01acSMauro Carvalho Chehab 
41cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std; /* Current set standard */
42cb7a01acSMauro Carvalho Chehab 	u32 input;
43cb7a01acSMauro Carvalho Chehab 	u32 output;
44cb7a01acSMauro Carvalho Chehab 	unsigned reset_pin;
45cb7a01acSMauro Carvalho Chehab 	unsigned oe_pin;
46cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt fmt;
47cb7a01acSMauro Carvalho Chehab };
48cb7a01acSMauro Carvalho Chehab 
49cb7a01acSMauro Carvalho Chehab /* EXAMPLES USING 27 MHz CLOCK
50cb7a01acSMauro Carvalho Chehab  * Mode 1 CVBS Input (Composite Video on AIN5)
51cb7a01acSMauro Carvalho Chehab  * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
52cb7a01acSMauro Carvalho Chehab  */
53cb7a01acSMauro Carvalho Chehab static const unsigned char adv7183_init_regs[] = {
54cb7a01acSMauro Carvalho Chehab 	ADV7183_IN_CTRL, 0x04,           /* CVBS input on AIN5 */
55cb7a01acSMauro Carvalho Chehab 	ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
56cb7a01acSMauro Carvalho Chehab 	ADV7183_SHAP_FILT_CTRL, 0x41,    /* Set CSFM to SH1 */
57cb7a01acSMauro Carvalho Chehab 	ADV7183_ADC_CTRL, 0x16,          /* Power down ADC 1 and ADC 2 */
58cb7a01acSMauro Carvalho Chehab 	ADV7183_CTI_DNR_CTRL_4, 0x04,    /* Set DNR threshold to 4 for flat response */
59cb7a01acSMauro Carvalho Chehab 	/* ADI recommended programming sequence */
60cb7a01acSMauro Carvalho Chehab 	ADV7183_ADI_CTRL, 0x80,
61cb7a01acSMauro Carvalho Chehab 	ADV7183_CTI_DNR_CTRL_4, 0x20,
62cb7a01acSMauro Carvalho Chehab 	0x52, 0x18,
63cb7a01acSMauro Carvalho Chehab 	0x58, 0xED,
64cb7a01acSMauro Carvalho Chehab 	0x77, 0xC5,
65cb7a01acSMauro Carvalho Chehab 	0x7C, 0x93,
66cb7a01acSMauro Carvalho Chehab 	0x7D, 0x00,
67cb7a01acSMauro Carvalho Chehab 	0xD0, 0x48,
68cb7a01acSMauro Carvalho Chehab 	0xD5, 0xA0,
69cb7a01acSMauro Carvalho Chehab 	0xD7, 0xEA,
70cb7a01acSMauro Carvalho Chehab 	ADV7183_SD_SATURATION_CR, 0x3E,
71cb7a01acSMauro Carvalho Chehab 	ADV7183_PAL_V_END, 0x3E,
72cb7a01acSMauro Carvalho Chehab 	ADV7183_PAL_F_TOGGLE, 0x0F,
73cb7a01acSMauro Carvalho Chehab 	ADV7183_ADI_CTRL, 0x00,
74cb7a01acSMauro Carvalho Chehab };
75cb7a01acSMauro Carvalho Chehab 
76cb7a01acSMauro Carvalho Chehab static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
77cb7a01acSMauro Carvalho Chehab {
78cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct adv7183, sd);
79cb7a01acSMauro Carvalho Chehab }
80cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
81cb7a01acSMauro Carvalho Chehab {
82cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
83cb7a01acSMauro Carvalho Chehab }
84cb7a01acSMauro Carvalho Chehab 
85cb7a01acSMauro Carvalho Chehab static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
86cb7a01acSMauro Carvalho Chehab {
87cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
88cb7a01acSMauro Carvalho Chehab 
89cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_byte_data(client, reg);
90cb7a01acSMauro Carvalho Chehab }
91cb7a01acSMauro Carvalho Chehab 
92cb7a01acSMauro Carvalho Chehab static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
93cb7a01acSMauro Carvalho Chehab 				unsigned char value)
94cb7a01acSMauro Carvalho Chehab {
95cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
96cb7a01acSMauro Carvalho Chehab 
97cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, reg, value);
98cb7a01acSMauro Carvalho Chehab }
99cb7a01acSMauro Carvalho Chehab 
100cb7a01acSMauro Carvalho Chehab static int adv7183_writeregs(struct v4l2_subdev *sd,
101cb7a01acSMauro Carvalho Chehab 		const unsigned char *regs, unsigned int num)
102cb7a01acSMauro Carvalho Chehab {
103cb7a01acSMauro Carvalho Chehab 	unsigned char reg, data;
104cb7a01acSMauro Carvalho Chehab 	unsigned int cnt = 0;
105cb7a01acSMauro Carvalho Chehab 
106cb7a01acSMauro Carvalho Chehab 	if (num & 0x1) {
107cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "invalid regs array\n");
108cb7a01acSMauro Carvalho Chehab 		return -1;
109cb7a01acSMauro Carvalho Chehab 	}
110cb7a01acSMauro Carvalho Chehab 
111cb7a01acSMauro Carvalho Chehab 	while (cnt < num) {
112cb7a01acSMauro Carvalho Chehab 		reg = *regs++;
113cb7a01acSMauro Carvalho Chehab 		data = *regs++;
114cb7a01acSMauro Carvalho Chehab 		cnt += 2;
115cb7a01acSMauro Carvalho Chehab 
116cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, reg, data);
117cb7a01acSMauro Carvalho Chehab 	}
118cb7a01acSMauro Carvalho Chehab 	return 0;
119cb7a01acSMauro Carvalho Chehab }
120cb7a01acSMauro Carvalho Chehab 
121cb7a01acSMauro Carvalho Chehab static int adv7183_log_status(struct v4l2_subdev *sd)
122cb7a01acSMauro Carvalho Chehab {
123cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
124cb7a01acSMauro Carvalho Chehab 
125cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
126cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_IN_CTRL));
127cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
128cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_VD_SEL));
129cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
130cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_OUT_CTRL));
131cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
132cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
133cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
134cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_AUTO_DET_EN));
135cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
136cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_CONTRAST));
137cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
138cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_BRIGHTNESS));
139cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
140cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_HUE));
141cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
142cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_DEF_Y));
143cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
144cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_DEF_C));
145cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
146cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_ADI_CTRL));
147cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
148cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_POW_MANAGE));
149cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
150cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_STATUS_1),
151cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_STATUS_2),
152cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_STATUS_3));
153cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
154cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_IDENT));
155cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
156cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
157cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
158cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
159cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
160cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
161cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
162cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
163cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
164cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
165cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_ADI_CTRL_2));
166cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
167cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
168cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
169cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
170cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
171cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
172cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
173cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
174cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
175cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
176cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
177cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
178cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
179cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
180cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
181cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
182cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
183cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
184cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
185cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
186cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
187cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_POLARITY));
188cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
189cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_ADC_CTRL));
190cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
191cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_SD_OFFSET_CB),
192cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_SD_OFFSET_CR));
193cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
194cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_SD_SATURATION_CB),
195cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_SD_SATURATION_CR));
196cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
197cb7a01acSMauro Carvalho Chehab 			adv7183_read(sd, ADV7183_DRIVE_STR));
198cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
199cb7a01acSMauro Carvalho Chehab 	return 0;
200cb7a01acSMauro Carvalho Chehab }
201cb7a01acSMauro Carvalho Chehab 
202cb7a01acSMauro Carvalho Chehab static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
203cb7a01acSMauro Carvalho Chehab {
204cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
205cb7a01acSMauro Carvalho Chehab 
206cb7a01acSMauro Carvalho Chehab 	*std = decoder->std;
207cb7a01acSMauro Carvalho Chehab 	return 0;
208cb7a01acSMauro Carvalho Chehab }
209cb7a01acSMauro Carvalho Chehab 
210cb7a01acSMauro Carvalho Chehab static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
211cb7a01acSMauro Carvalho Chehab {
212cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
213cb7a01acSMauro Carvalho Chehab 	int reg;
214cb7a01acSMauro Carvalho Chehab 
215cb7a01acSMauro Carvalho Chehab 	reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
216cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_PAL_60)
217cb7a01acSMauro Carvalho Chehab 		reg |= 0x60;
218cb7a01acSMauro Carvalho Chehab 	else if (std == V4L2_STD_NTSC_443)
219cb7a01acSMauro Carvalho Chehab 		reg |= 0x70;
220cb7a01acSMauro Carvalho Chehab 	else if (std == V4L2_STD_PAL_N)
221cb7a01acSMauro Carvalho Chehab 		reg |= 0x90;
222cb7a01acSMauro Carvalho Chehab 	else if (std == V4L2_STD_PAL_M)
223cb7a01acSMauro Carvalho Chehab 		reg |= 0xA0;
224cb7a01acSMauro Carvalho Chehab 	else if (std == V4L2_STD_PAL_Nc)
225cb7a01acSMauro Carvalho Chehab 		reg |= 0xC0;
226cb7a01acSMauro Carvalho Chehab 	else if (std & V4L2_STD_PAL)
227cb7a01acSMauro Carvalho Chehab 		reg |= 0x80;
228cb7a01acSMauro Carvalho Chehab 	else if (std & V4L2_STD_NTSC)
229cb7a01acSMauro Carvalho Chehab 		reg |= 0x50;
230cb7a01acSMauro Carvalho Chehab 	else if (std & V4L2_STD_SECAM)
231cb7a01acSMauro Carvalho Chehab 		reg |= 0xE0;
232cb7a01acSMauro Carvalho Chehab 	else
233cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
234cb7a01acSMauro Carvalho Chehab 	adv7183_write(sd, ADV7183_IN_CTRL, reg);
235cb7a01acSMauro Carvalho Chehab 
236cb7a01acSMauro Carvalho Chehab 	decoder->std = std;
237cb7a01acSMauro Carvalho Chehab 
238cb7a01acSMauro Carvalho Chehab 	return 0;
239cb7a01acSMauro Carvalho Chehab }
240cb7a01acSMauro Carvalho Chehab 
241cb7a01acSMauro Carvalho Chehab static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
242cb7a01acSMauro Carvalho Chehab {
243cb7a01acSMauro Carvalho Chehab 	int reg;
244cb7a01acSMauro Carvalho Chehab 
245cb7a01acSMauro Carvalho Chehab 	reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
246cb7a01acSMauro Carvalho Chehab 	adv7183_write(sd, ADV7183_POW_MANAGE, reg);
247cb7a01acSMauro Carvalho Chehab 	/* wait 5ms before any further i2c writes are performed */
248cb7a01acSMauro Carvalho Chehab 	usleep_range(5000, 10000);
249cb7a01acSMauro Carvalho Chehab 	return 0;
250cb7a01acSMauro Carvalho Chehab }
251cb7a01acSMauro Carvalho Chehab 
252cb7a01acSMauro Carvalho Chehab static int adv7183_s_routing(struct v4l2_subdev *sd,
253cb7a01acSMauro Carvalho Chehab 				u32 input, u32 output, u32 config)
254cb7a01acSMauro Carvalho Chehab {
255cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
256cb7a01acSMauro Carvalho Chehab 	int reg;
257cb7a01acSMauro Carvalho Chehab 
258cb7a01acSMauro Carvalho Chehab 	if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
259cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
260cb7a01acSMauro Carvalho Chehab 
261cb7a01acSMauro Carvalho Chehab 	if (input != decoder->input) {
262cb7a01acSMauro Carvalho Chehab 		decoder->input = input;
263cb7a01acSMauro Carvalho Chehab 		reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
264cb7a01acSMauro Carvalho Chehab 		switch (input) {
265cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE1:
266cb7a01acSMauro Carvalho Chehab 			reg |= 0x1;
267cb7a01acSMauro Carvalho Chehab 			break;
268cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE2:
269cb7a01acSMauro Carvalho Chehab 			reg |= 0x2;
270cb7a01acSMauro Carvalho Chehab 			break;
271cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE3:
272cb7a01acSMauro Carvalho Chehab 			reg |= 0x3;
273cb7a01acSMauro Carvalho Chehab 			break;
274cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE4:
275cb7a01acSMauro Carvalho Chehab 			reg |= 0x4;
276cb7a01acSMauro Carvalho Chehab 			break;
277cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE5:
278cb7a01acSMauro Carvalho Chehab 			reg |= 0x5;
279cb7a01acSMauro Carvalho Chehab 			break;
280cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE6:
281cb7a01acSMauro Carvalho Chehab 			reg |= 0xB;
282cb7a01acSMauro Carvalho Chehab 			break;
283cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE7:
284cb7a01acSMauro Carvalho Chehab 			reg |= 0xC;
285cb7a01acSMauro Carvalho Chehab 			break;
286cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE8:
287cb7a01acSMauro Carvalho Chehab 			reg |= 0xD;
288cb7a01acSMauro Carvalho Chehab 			break;
289cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE9:
290cb7a01acSMauro Carvalho Chehab 			reg |= 0xE;
291cb7a01acSMauro Carvalho Chehab 			break;
292cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPOSITE10:
293cb7a01acSMauro Carvalho Chehab 			reg |= 0xF;
294cb7a01acSMauro Carvalho Chehab 			break;
295cb7a01acSMauro Carvalho Chehab 		case ADV7183_SVIDEO0:
296cb7a01acSMauro Carvalho Chehab 			reg |= 0x6;
297cb7a01acSMauro Carvalho Chehab 			break;
298cb7a01acSMauro Carvalho Chehab 		case ADV7183_SVIDEO1:
299cb7a01acSMauro Carvalho Chehab 			reg |= 0x7;
300cb7a01acSMauro Carvalho Chehab 			break;
301cb7a01acSMauro Carvalho Chehab 		case ADV7183_SVIDEO2:
302cb7a01acSMauro Carvalho Chehab 			reg |= 0x8;
303cb7a01acSMauro Carvalho Chehab 			break;
304cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPONENT0:
305cb7a01acSMauro Carvalho Chehab 			reg |= 0x9;
306cb7a01acSMauro Carvalho Chehab 			break;
307cb7a01acSMauro Carvalho Chehab 		case ADV7183_COMPONENT1:
308cb7a01acSMauro Carvalho Chehab 			reg |= 0xA;
309cb7a01acSMauro Carvalho Chehab 			break;
310cb7a01acSMauro Carvalho Chehab 		default:
311cb7a01acSMauro Carvalho Chehab 			break;
312cb7a01acSMauro Carvalho Chehab 		}
313cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_IN_CTRL, reg);
314cb7a01acSMauro Carvalho Chehab 	}
315cb7a01acSMauro Carvalho Chehab 
316cb7a01acSMauro Carvalho Chehab 	if (output != decoder->output) {
317cb7a01acSMauro Carvalho Chehab 		decoder->output = output;
318cb7a01acSMauro Carvalho Chehab 		reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
319cb7a01acSMauro Carvalho Chehab 		switch (output) {
320cb7a01acSMauro Carvalho Chehab 		case ADV7183_16BIT_OUT:
321cb7a01acSMauro Carvalho Chehab 			reg |= 0x9;
322cb7a01acSMauro Carvalho Chehab 			break;
323cb7a01acSMauro Carvalho Chehab 		default:
324cb7a01acSMauro Carvalho Chehab 			reg |= 0xC;
325cb7a01acSMauro Carvalho Chehab 			break;
326cb7a01acSMauro Carvalho Chehab 		}
327cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_OUT_CTRL, reg);
328cb7a01acSMauro Carvalho Chehab 	}
329cb7a01acSMauro Carvalho Chehab 
330cb7a01acSMauro Carvalho Chehab 	return 0;
331cb7a01acSMauro Carvalho Chehab }
332cb7a01acSMauro Carvalho Chehab 
333cb7a01acSMauro Carvalho Chehab static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
334cb7a01acSMauro Carvalho Chehab {
335cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
336cb7a01acSMauro Carvalho Chehab 	int val = ctrl->val;
337cb7a01acSMauro Carvalho Chehab 
338cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
339cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
340cb7a01acSMauro Carvalho Chehab 		if (val < 0)
341cb7a01acSMauro Carvalho Chehab 			val = 127 - val;
342cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_BRIGHTNESS, val);
343cb7a01acSMauro Carvalho Chehab 		break;
344cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
345cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_CONTRAST, val);
346cb7a01acSMauro Carvalho Chehab 		break;
347cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
348cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
349cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
350cb7a01acSMauro Carvalho Chehab 		break;
351cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
352cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
353cb7a01acSMauro Carvalho Chehab 		adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
354cb7a01acSMauro Carvalho Chehab 		break;
355cb7a01acSMauro Carvalho Chehab 	default:
356cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
357cb7a01acSMauro Carvalho Chehab 	}
358cb7a01acSMauro Carvalho Chehab 
359cb7a01acSMauro Carvalho Chehab 	return 0;
360cb7a01acSMauro Carvalho Chehab }
361cb7a01acSMauro Carvalho Chehab 
362cb7a01acSMauro Carvalho Chehab static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
363cb7a01acSMauro Carvalho Chehab {
364cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
365cb7a01acSMauro Carvalho Chehab 	int reg;
366cb7a01acSMauro Carvalho Chehab 
367cb7a01acSMauro Carvalho Chehab 	/* enable autodetection block */
368cb7a01acSMauro Carvalho Chehab 	reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
369cb7a01acSMauro Carvalho Chehab 	adv7183_write(sd, ADV7183_IN_CTRL, reg);
370cb7a01acSMauro Carvalho Chehab 
371cb7a01acSMauro Carvalho Chehab 	/* wait autodetection switch */
372cb7a01acSMauro Carvalho Chehab 	mdelay(10);
373cb7a01acSMauro Carvalho Chehab 
374cb7a01acSMauro Carvalho Chehab 	/* get autodetection result */
375cb7a01acSMauro Carvalho Chehab 	reg = adv7183_read(sd, ADV7183_STATUS_1);
376cb7a01acSMauro Carvalho Chehab 	switch ((reg >> 0x4) & 0x7) {
377cb7a01acSMauro Carvalho Chehab 	case 0:
378cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_NTSC;
379cb7a01acSMauro Carvalho Chehab 		break;
380cb7a01acSMauro Carvalho Chehab 	case 1:
381cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_NTSC_443;
382cb7a01acSMauro Carvalho Chehab 		break;
383cb7a01acSMauro Carvalho Chehab 	case 2:
384cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_PAL_M;
385cb7a01acSMauro Carvalho Chehab 		break;
386cb7a01acSMauro Carvalho Chehab 	case 3:
387cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_PAL_60;
388cb7a01acSMauro Carvalho Chehab 		break;
389cb7a01acSMauro Carvalho Chehab 	case 4:
390cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_PAL;
391cb7a01acSMauro Carvalho Chehab 		break;
392cb7a01acSMauro Carvalho Chehab 	case 5:
393cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_SECAM;
394cb7a01acSMauro Carvalho Chehab 		break;
395cb7a01acSMauro Carvalho Chehab 	case 6:
396cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_PAL_Nc;
397cb7a01acSMauro Carvalho Chehab 		break;
398cb7a01acSMauro Carvalho Chehab 	case 7:
399cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_SECAM;
400cb7a01acSMauro Carvalho Chehab 		break;
401cb7a01acSMauro Carvalho Chehab 	default:
402cb7a01acSMauro Carvalho Chehab 		*std = V4L2_STD_UNKNOWN;
403cb7a01acSMauro Carvalho Chehab 		break;
404cb7a01acSMauro Carvalho Chehab 	}
405cb7a01acSMauro Carvalho Chehab 
406cb7a01acSMauro Carvalho Chehab 	/* after std detection, write back user set std */
407cb7a01acSMauro Carvalho Chehab 	adv7183_s_std(sd, decoder->std);
408cb7a01acSMauro Carvalho Chehab 	return 0;
409cb7a01acSMauro Carvalho Chehab }
410cb7a01acSMauro Carvalho Chehab 
411cb7a01acSMauro Carvalho Chehab static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
412cb7a01acSMauro Carvalho Chehab {
413cb7a01acSMauro Carvalho Chehab 	int reg;
414cb7a01acSMauro Carvalho Chehab 
415cb7a01acSMauro Carvalho Chehab 	*status = V4L2_IN_ST_NO_SIGNAL;
416cb7a01acSMauro Carvalho Chehab 	reg = adv7183_read(sd, ADV7183_STATUS_1);
417cb7a01acSMauro Carvalho Chehab 	if (reg < 0)
418cb7a01acSMauro Carvalho Chehab 		return reg;
419cb7a01acSMauro Carvalho Chehab 	if (reg & 0x1)
420cb7a01acSMauro Carvalho Chehab 		*status = 0;
421cb7a01acSMauro Carvalho Chehab 	return 0;
422cb7a01acSMauro Carvalho Chehab }
423cb7a01acSMauro Carvalho Chehab 
424cb7a01acSMauro Carvalho Chehab static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
425cb7a01acSMauro Carvalho Chehab 				enum v4l2_mbus_pixelcode *code)
426cb7a01acSMauro Carvalho Chehab {
427cb7a01acSMauro Carvalho Chehab 	if (index > 0)
428cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
429cb7a01acSMauro Carvalho Chehab 
430cb7a01acSMauro Carvalho Chehab 	*code = V4L2_MBUS_FMT_UYVY8_2X8;
431cb7a01acSMauro Carvalho Chehab 	return 0;
432cb7a01acSMauro Carvalho Chehab }
433cb7a01acSMauro Carvalho Chehab 
434cb7a01acSMauro Carvalho Chehab static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
435cb7a01acSMauro Carvalho Chehab 				struct v4l2_mbus_framefmt *fmt)
436cb7a01acSMauro Carvalho Chehab {
437cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
438cb7a01acSMauro Carvalho Chehab 
439cb7a01acSMauro Carvalho Chehab 	fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
440cb7a01acSMauro Carvalho Chehab 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
441cb7a01acSMauro Carvalho Chehab 	if (decoder->std & V4L2_STD_525_60) {
442cb7a01acSMauro Carvalho Chehab 		fmt->field = V4L2_FIELD_SEQ_TB;
443cb7a01acSMauro Carvalho Chehab 		fmt->width = 720;
444cb7a01acSMauro Carvalho Chehab 		fmt->height = 480;
445cb7a01acSMauro Carvalho Chehab 	} else {
446cb7a01acSMauro Carvalho Chehab 		fmt->field = V4L2_FIELD_SEQ_BT;
447cb7a01acSMauro Carvalho Chehab 		fmt->width = 720;
448cb7a01acSMauro Carvalho Chehab 		fmt->height = 576;
449cb7a01acSMauro Carvalho Chehab 	}
450cb7a01acSMauro Carvalho Chehab 	return 0;
451cb7a01acSMauro Carvalho Chehab }
452cb7a01acSMauro Carvalho Chehab 
453cb7a01acSMauro Carvalho Chehab static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
454cb7a01acSMauro Carvalho Chehab 				struct v4l2_mbus_framefmt *fmt)
455cb7a01acSMauro Carvalho Chehab {
456cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
457cb7a01acSMauro Carvalho Chehab 
458cb7a01acSMauro Carvalho Chehab 	adv7183_try_mbus_fmt(sd, fmt);
459cb7a01acSMauro Carvalho Chehab 	decoder->fmt = *fmt;
460cb7a01acSMauro Carvalho Chehab 	return 0;
461cb7a01acSMauro Carvalho Chehab }
462cb7a01acSMauro Carvalho Chehab 
463cb7a01acSMauro Carvalho Chehab static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
464cb7a01acSMauro Carvalho Chehab 				struct v4l2_mbus_framefmt *fmt)
465cb7a01acSMauro Carvalho Chehab {
466cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
467cb7a01acSMauro Carvalho Chehab 
468cb7a01acSMauro Carvalho Chehab 	*fmt = decoder->fmt;
469cb7a01acSMauro Carvalho Chehab 	return 0;
470cb7a01acSMauro Carvalho Chehab }
471cb7a01acSMauro Carvalho Chehab 
472cb7a01acSMauro Carvalho Chehab static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
473cb7a01acSMauro Carvalho Chehab {
474cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
475cb7a01acSMauro Carvalho Chehab 
476cb7a01acSMauro Carvalho Chehab 	if (enable)
477cb7a01acSMauro Carvalho Chehab 		gpio_direction_output(decoder->oe_pin, 0);
478cb7a01acSMauro Carvalho Chehab 	else
479cb7a01acSMauro Carvalho Chehab 		gpio_direction_output(decoder->oe_pin, 1);
480cb7a01acSMauro Carvalho Chehab 	udelay(1);
481cb7a01acSMauro Carvalho Chehab 	return 0;
482cb7a01acSMauro Carvalho Chehab }
483cb7a01acSMauro Carvalho Chehab 
484cb7a01acSMauro Carvalho Chehab static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
485cb7a01acSMauro Carvalho Chehab 		struct v4l2_dbg_chip_ident *chip)
486cb7a01acSMauro Carvalho Chehab {
487cb7a01acSMauro Carvalho Chehab 	int rev;
488cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
489cb7a01acSMauro Carvalho Chehab 
490cb7a01acSMauro Carvalho Chehab 	/* 0x11 for adv7183, 0x13 for adv7183b */
491cb7a01acSMauro Carvalho Chehab 	rev = adv7183_read(sd, ADV7183_IDENT);
492cb7a01acSMauro Carvalho Chehab 
493cb7a01acSMauro Carvalho Chehab 	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
494cb7a01acSMauro Carvalho Chehab }
495cb7a01acSMauro Carvalho Chehab 
496cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
497cb7a01acSMauro Carvalho Chehab static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
498cb7a01acSMauro Carvalho Chehab {
499cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
500cb7a01acSMauro Carvalho Chehab 
501cb7a01acSMauro Carvalho Chehab 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
502cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
503cb7a01acSMauro Carvalho Chehab 	if (!capable(CAP_SYS_ADMIN))
504cb7a01acSMauro Carvalho Chehab 		return -EPERM;
505cb7a01acSMauro Carvalho Chehab 	reg->val = adv7183_read(sd, reg->reg & 0xff);
506cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
507cb7a01acSMauro Carvalho Chehab 	return 0;
508cb7a01acSMauro Carvalho Chehab }
509cb7a01acSMauro Carvalho Chehab 
510cb7a01acSMauro Carvalho Chehab static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
511cb7a01acSMauro Carvalho Chehab {
512cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
513cb7a01acSMauro Carvalho Chehab 
514cb7a01acSMauro Carvalho Chehab 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
515cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
516cb7a01acSMauro Carvalho Chehab 	if (!capable(CAP_SYS_ADMIN))
517cb7a01acSMauro Carvalho Chehab 		return -EPERM;
518cb7a01acSMauro Carvalho Chehab 	adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
519cb7a01acSMauro Carvalho Chehab 	return 0;
520cb7a01acSMauro Carvalho Chehab }
521cb7a01acSMauro Carvalho Chehab #endif
522cb7a01acSMauro Carvalho Chehab 
523cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
524cb7a01acSMauro Carvalho Chehab 	.s_ctrl = adv7183_s_ctrl,
525cb7a01acSMauro Carvalho Chehab };
526cb7a01acSMauro Carvalho Chehab 
527cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops adv7183_core_ops = {
528cb7a01acSMauro Carvalho Chehab 	.log_status = adv7183_log_status,
529cb7a01acSMauro Carvalho Chehab 	.g_std = adv7183_g_std,
530cb7a01acSMauro Carvalho Chehab 	.s_std = adv7183_s_std,
531cb7a01acSMauro Carvalho Chehab 	.reset = adv7183_reset,
532cb7a01acSMauro Carvalho Chehab 	.g_chip_ident = adv7183_g_chip_ident,
533cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
534cb7a01acSMauro Carvalho Chehab 	.g_register = adv7183_g_register,
535cb7a01acSMauro Carvalho Chehab 	.s_register = adv7183_s_register,
536cb7a01acSMauro Carvalho Chehab #endif
537cb7a01acSMauro Carvalho Chehab };
538cb7a01acSMauro Carvalho Chehab 
539cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops adv7183_video_ops = {
540cb7a01acSMauro Carvalho Chehab 	.s_routing = adv7183_s_routing,
541cb7a01acSMauro Carvalho Chehab 	.querystd = adv7183_querystd,
542cb7a01acSMauro Carvalho Chehab 	.g_input_status = adv7183_g_input_status,
543cb7a01acSMauro Carvalho Chehab 	.enum_mbus_fmt = adv7183_enum_mbus_fmt,
544cb7a01acSMauro Carvalho Chehab 	.try_mbus_fmt = adv7183_try_mbus_fmt,
545cb7a01acSMauro Carvalho Chehab 	.s_mbus_fmt = adv7183_s_mbus_fmt,
546cb7a01acSMauro Carvalho Chehab 	.g_mbus_fmt = adv7183_g_mbus_fmt,
547cb7a01acSMauro Carvalho Chehab 	.s_stream = adv7183_s_stream,
548cb7a01acSMauro Carvalho Chehab };
549cb7a01acSMauro Carvalho Chehab 
550cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops adv7183_ops = {
551cb7a01acSMauro Carvalho Chehab 	.core = &adv7183_core_ops,
552cb7a01acSMauro Carvalho Chehab 	.video = &adv7183_video_ops,
553cb7a01acSMauro Carvalho Chehab };
554cb7a01acSMauro Carvalho Chehab 
555cb7a01acSMauro Carvalho Chehab static int adv7183_probe(struct i2c_client *client,
556cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
557cb7a01acSMauro Carvalho Chehab {
558cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder;
559cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
560cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl;
561cb7a01acSMauro Carvalho Chehab 	int ret;
562cb7a01acSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt fmt;
563cb7a01acSMauro Carvalho Chehab 	const unsigned *pin_array;
564cb7a01acSMauro Carvalho Chehab 
565cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
566cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
567cb7a01acSMauro Carvalho Chehab 		return -EIO;
568cb7a01acSMauro Carvalho Chehab 
569cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
570cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
571cb7a01acSMauro Carvalho Chehab 
572cb7a01acSMauro Carvalho Chehab 	pin_array = client->dev.platform_data;
573cb7a01acSMauro Carvalho Chehab 	if (pin_array == NULL)
574cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
575cb7a01acSMauro Carvalho Chehab 
576cb7a01acSMauro Carvalho Chehab 	decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
577cb7a01acSMauro Carvalho Chehab 	if (decoder == NULL)
578cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
579cb7a01acSMauro Carvalho Chehab 
580cb7a01acSMauro Carvalho Chehab 	decoder->reset_pin = pin_array[0];
581cb7a01acSMauro Carvalho Chehab 	decoder->oe_pin = pin_array[1];
582cb7a01acSMauro Carvalho Chehab 
583cb7a01acSMauro Carvalho Chehab 	if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
584cb7a01acSMauro Carvalho Chehab 		v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
585cb7a01acSMauro Carvalho Chehab 		ret = -EBUSY;
586cb7a01acSMauro Carvalho Chehab 		goto err_free_decoder;
587cb7a01acSMauro Carvalho Chehab 	}
588cb7a01acSMauro Carvalho Chehab 
589cb7a01acSMauro Carvalho Chehab 	if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
590cb7a01acSMauro Carvalho Chehab 		v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
591cb7a01acSMauro Carvalho Chehab 		ret = -EBUSY;
592cb7a01acSMauro Carvalho Chehab 		goto err_free_reset;
593cb7a01acSMauro Carvalho Chehab 	}
594cb7a01acSMauro Carvalho Chehab 
595cb7a01acSMauro Carvalho Chehab 	sd = &decoder->sd;
596cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
597cb7a01acSMauro Carvalho Chehab 
598cb7a01acSMauro Carvalho Chehab 	hdl = &decoder->hdl;
599cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(hdl, 4);
600cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
601cb7a01acSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
602cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
603cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
604cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
605cb7a01acSMauro Carvalho Chehab 			V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
606cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
607cb7a01acSMauro Carvalho Chehab 			V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
608cb7a01acSMauro Carvalho Chehab 	/* hook the control handler into the driver */
609cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = hdl;
610cb7a01acSMauro Carvalho Chehab 	if (hdl->error) {
611cb7a01acSMauro Carvalho Chehab 		ret = hdl->error;
612cb7a01acSMauro Carvalho Chehab 
613cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(hdl);
614cb7a01acSMauro Carvalho Chehab 		goto err_free_oe;
615cb7a01acSMauro Carvalho Chehab 	}
616cb7a01acSMauro Carvalho Chehab 
617cb7a01acSMauro Carvalho Chehab 	/* v4l2 doesn't support an autodetect standard, pick PAL as default */
618cb7a01acSMauro Carvalho Chehab 	decoder->std = V4L2_STD_PAL;
619cb7a01acSMauro Carvalho Chehab 	decoder->input = ADV7183_COMPOSITE4;
620cb7a01acSMauro Carvalho Chehab 	decoder->output = ADV7183_8BIT_OUT;
621cb7a01acSMauro Carvalho Chehab 
622cb7a01acSMauro Carvalho Chehab 	gpio_direction_output(decoder->oe_pin, 1);
623cb7a01acSMauro Carvalho Chehab 	/* reset chip */
624cb7a01acSMauro Carvalho Chehab 	gpio_direction_output(decoder->reset_pin, 0);
625cb7a01acSMauro Carvalho Chehab 	/* reset pulse width at least 5ms */
626cb7a01acSMauro Carvalho Chehab 	mdelay(10);
627cb7a01acSMauro Carvalho Chehab 	gpio_direction_output(decoder->reset_pin, 1);
628cb7a01acSMauro Carvalho Chehab 	/* wait 5ms before any further i2c writes are performed */
629cb7a01acSMauro Carvalho Chehab 	mdelay(5);
630cb7a01acSMauro Carvalho Chehab 
631cb7a01acSMauro Carvalho Chehab 	adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
632cb7a01acSMauro Carvalho Chehab 	adv7183_s_std(sd, decoder->std);
633cb7a01acSMauro Carvalho Chehab 	fmt.width = 720;
634cb7a01acSMauro Carvalho Chehab 	fmt.height = 576;
635cb7a01acSMauro Carvalho Chehab 	adv7183_s_mbus_fmt(sd, &fmt);
636cb7a01acSMauro Carvalho Chehab 
637cb7a01acSMauro Carvalho Chehab 	/* initialize the hardware to the default control values */
638cb7a01acSMauro Carvalho Chehab 	ret = v4l2_ctrl_handler_setup(hdl);
639cb7a01acSMauro Carvalho Chehab 	if (ret) {
640cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(hdl);
641cb7a01acSMauro Carvalho Chehab 		goto err_free_oe;
642cb7a01acSMauro Carvalho Chehab 	}
643cb7a01acSMauro Carvalho Chehab 
644cb7a01acSMauro Carvalho Chehab 	return 0;
645cb7a01acSMauro Carvalho Chehab err_free_oe:
646cb7a01acSMauro Carvalho Chehab 	gpio_free(decoder->oe_pin);
647cb7a01acSMauro Carvalho Chehab err_free_reset:
648cb7a01acSMauro Carvalho Chehab 	gpio_free(decoder->reset_pin);
649cb7a01acSMauro Carvalho Chehab err_free_decoder:
650cb7a01acSMauro Carvalho Chehab 	kfree(decoder);
651cb7a01acSMauro Carvalho Chehab 	return ret;
652cb7a01acSMauro Carvalho Chehab }
653cb7a01acSMauro Carvalho Chehab 
654cb7a01acSMauro Carvalho Chehab static int adv7183_remove(struct i2c_client *client)
655cb7a01acSMauro Carvalho Chehab {
656cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
657cb7a01acSMauro Carvalho Chehab 	struct adv7183 *decoder = to_adv7183(sd);
658cb7a01acSMauro Carvalho Chehab 
659cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
660cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(sd->ctrl_handler);
661cb7a01acSMauro Carvalho Chehab 	gpio_free(decoder->oe_pin);
662cb7a01acSMauro Carvalho Chehab 	gpio_free(decoder->reset_pin);
663cb7a01acSMauro Carvalho Chehab 	kfree(decoder);
664cb7a01acSMauro Carvalho Chehab 	return 0;
665cb7a01acSMauro Carvalho Chehab }
666cb7a01acSMauro Carvalho Chehab 
667cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id adv7183_id[] = {
668cb7a01acSMauro Carvalho Chehab 	{"adv7183", 0},
669cb7a01acSMauro Carvalho Chehab 	{},
670cb7a01acSMauro Carvalho Chehab };
671cb7a01acSMauro Carvalho Chehab 
672cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, adv7183_id);
673cb7a01acSMauro Carvalho Chehab 
674cb7a01acSMauro Carvalho Chehab static struct i2c_driver adv7183_driver = {
675cb7a01acSMauro Carvalho Chehab 	.driver = {
676cb7a01acSMauro Carvalho Chehab 		.owner  = THIS_MODULE,
677cb7a01acSMauro Carvalho Chehab 		.name   = "adv7183",
678cb7a01acSMauro Carvalho Chehab 	},
679cb7a01acSMauro Carvalho Chehab 	.probe          = adv7183_probe,
6804c62e976SGreg Kroah-Hartman 	.remove         = adv7183_remove,
681cb7a01acSMauro Carvalho Chehab 	.id_table       = adv7183_id,
682cb7a01acSMauro Carvalho Chehab };
683cb7a01acSMauro Carvalho Chehab 
68401aea0bfSWei Yongjun module_i2c_driver(adv7183_driver);
685cb7a01acSMauro Carvalho Chehab 
686cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
687cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
688cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
689