xref: /openbmc/linux/drivers/media/i2c/saa7115.c (revision 30634e8e)
1cb7a01acSMauro Carvalho Chehab /* saa711x - Philips SAA711x video decoder driver
2cb7a01acSMauro Carvalho Chehab  * This driver can work with saa7111, saa7111a, saa7113, saa7114,
3cb7a01acSMauro Carvalho Chehab  *			     saa7115 and saa7118.
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Based on saa7114 driver by Maxim Yevtyushkin, which is based on
6cb7a01acSMauro Carvalho Chehab  * the saa7111 driver by Dave Perks.
7cb7a01acSMauro Carvalho Chehab  *
8cb7a01acSMauro Carvalho Chehab  * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
9cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
10cb7a01acSMauro Carvalho Chehab  *
11cb7a01acSMauro Carvalho Chehab  * Slight changes for video timing and attachment output by
12cb7a01acSMauro Carvalho Chehab  * Wolfgang Scherr <scherr@net4you.net>
13cb7a01acSMauro Carvalho Chehab  *
14cb7a01acSMauro Carvalho Chehab  * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003)
15cb7a01acSMauro Carvalho Chehab  * by Ronald Bultje <rbultje@ronald.bitfreak.net>
16cb7a01acSMauro Carvalho Chehab  *
17cb7a01acSMauro Carvalho Chehab  * Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com>
18cb7a01acSMauro Carvalho Chehab  * (2/17/2003)
19cb7a01acSMauro Carvalho Chehab  *
20cb7a01acSMauro Carvalho Chehab  * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl>
21cb7a01acSMauro Carvalho Chehab  *
22cb7a01acSMauro Carvalho Chehab  * Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
23cb7a01acSMauro Carvalho Chehab  *	SAA7111, SAA7113 and SAA7118 support
24cb7a01acSMauro Carvalho Chehab  *
25cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
26cb7a01acSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License
27cb7a01acSMauro Carvalho Chehab  * as published by the Free Software Foundation; either version 2
28cb7a01acSMauro Carvalho Chehab  * of the License, or (at your option) any later version.
29cb7a01acSMauro Carvalho Chehab  *
30cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
31cb7a01acSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33cb7a01acSMauro Carvalho Chehab  * GNU General Public License for more details.
34cb7a01acSMauro Carvalho Chehab  *
35cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
36cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
37cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
38cb7a01acSMauro Carvalho Chehab  */
39cb7a01acSMauro Carvalho Chehab 
40cb7a01acSMauro Carvalho Chehab #include "saa711x_regs.h"
41cb7a01acSMauro Carvalho Chehab 
42cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
43cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
44cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
45cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
46cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
47cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
48cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
49cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h>
50cb7a01acSMauro Carvalho Chehab #include <media/saa7115.h>
51cb7a01acSMauro Carvalho Chehab #include <asm/div64.h>
52cb7a01acSMauro Carvalho Chehab 
53cb7a01acSMauro Carvalho Chehab #define VRES_60HZ	(480+16)
54cb7a01acSMauro Carvalho Chehab 
55cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver");
56cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR(  "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, "
57cb7a01acSMauro Carvalho Chehab 		"Hans Verkuil, Mauro Carvalho Chehab");
58cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
59cb7a01acSMauro Carvalho Chehab 
60cb7a01acSMauro Carvalho Chehab static bool debug;
61cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
62cb7a01acSMauro Carvalho Chehab 
63cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
64cb7a01acSMauro Carvalho Chehab 
65cb7a01acSMauro Carvalho Chehab 
66cb7a01acSMauro Carvalho Chehab struct saa711x_state {
67cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
68cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
69cb7a01acSMauro Carvalho Chehab 
70cb7a01acSMauro Carvalho Chehab 	struct {
71cb7a01acSMauro Carvalho Chehab 		/* chroma gain control cluster */
72cb7a01acSMauro Carvalho Chehab 		struct v4l2_ctrl *agc;
73cb7a01acSMauro Carvalho Chehab 		struct v4l2_ctrl *gain;
74cb7a01acSMauro Carvalho Chehab 	};
75cb7a01acSMauro Carvalho Chehab 
76cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std;
77cb7a01acSMauro Carvalho Chehab 	int input;
78cb7a01acSMauro Carvalho Chehab 	int output;
79cb7a01acSMauro Carvalho Chehab 	int enable;
80cb7a01acSMauro Carvalho Chehab 	int radio;
81cb7a01acSMauro Carvalho Chehab 	int width;
82cb7a01acSMauro Carvalho Chehab 	int height;
83cb7a01acSMauro Carvalho Chehab 	u32 ident;
84cb7a01acSMauro Carvalho Chehab 	u32 audclk_freq;
85cb7a01acSMauro Carvalho Chehab 	u32 crystal_freq;
86cb7a01acSMauro Carvalho Chehab 	u8 ucgc;
87cb7a01acSMauro Carvalho Chehab 	u8 cgcdiv;
88cb7a01acSMauro Carvalho Chehab 	u8 apll;
89cb7a01acSMauro Carvalho Chehab };
90cb7a01acSMauro Carvalho Chehab 
91cb7a01acSMauro Carvalho Chehab static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
92cb7a01acSMauro Carvalho Chehab {
93cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct saa711x_state, sd);
94cb7a01acSMauro Carvalho Chehab }
95cb7a01acSMauro Carvalho Chehab 
96cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
97cb7a01acSMauro Carvalho Chehab {
98cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
99cb7a01acSMauro Carvalho Chehab }
100cb7a01acSMauro Carvalho Chehab 
101cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
102cb7a01acSMauro Carvalho Chehab 
103cb7a01acSMauro Carvalho Chehab static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
104cb7a01acSMauro Carvalho Chehab {
105cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
106cb7a01acSMauro Carvalho Chehab 
107cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, reg, value);
108cb7a01acSMauro Carvalho Chehab }
109cb7a01acSMauro Carvalho Chehab 
110cb7a01acSMauro Carvalho Chehab /* Sanity routine to check if a register is present */
111cb7a01acSMauro Carvalho Chehab static int saa711x_has_reg(const int id, const u8 reg)
112cb7a01acSMauro Carvalho Chehab {
113cb7a01acSMauro Carvalho Chehab 	if (id == V4L2_IDENT_SAA7111)
114cb7a01acSMauro Carvalho Chehab 		return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
115cb7a01acSMauro Carvalho Chehab 		       (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
116cb7a01acSMauro Carvalho Chehab 	if (id == V4L2_IDENT_SAA7111A)
117cb7a01acSMauro Carvalho Chehab 		return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
118cb7a01acSMauro Carvalho Chehab 		       reg != 0x14 && reg != 0x18 && reg != 0x19 &&
119cb7a01acSMauro Carvalho Chehab 		       reg != 0x1d && reg != 0x1e;
120cb7a01acSMauro Carvalho Chehab 
121cb7a01acSMauro Carvalho Chehab 	/* common for saa7113/4/5/8 */
122cb7a01acSMauro Carvalho Chehab 	if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
123cb7a01acSMauro Carvalho Chehab 	    reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) ||
124cb7a01acSMauro Carvalho Chehab 	    reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) ||
125cb7a01acSMauro Carvalho Chehab 	    reg == 0x82 || (reg >= 0x89 && reg <= 0x8e)))
126cb7a01acSMauro Carvalho Chehab 		return 0;
127cb7a01acSMauro Carvalho Chehab 
128cb7a01acSMauro Carvalho Chehab 	switch (id) {
129cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7113:
130cb7a01acSMauro Carvalho Chehab 		return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
131cb7a01acSMauro Carvalho Chehab 		       reg != 0x5d && reg < 0x63;
132cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7114:
133cb7a01acSMauro Carvalho Chehab 		return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
134cb7a01acSMauro Carvalho Chehab 		       (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
135cb7a01acSMauro Carvalho Chehab 		       reg != 0x81 && reg < 0xf0;
136cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7115:
137cb7a01acSMauro Carvalho Chehab 		return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
138cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7118:
139cb7a01acSMauro Carvalho Chehab 		return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
140cb7a01acSMauro Carvalho Chehab 		       (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
141cb7a01acSMauro Carvalho Chehab 		       (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
142cb7a01acSMauro Carvalho Chehab 	}
143cb7a01acSMauro Carvalho Chehab 	return 1;
144cb7a01acSMauro Carvalho Chehab }
145cb7a01acSMauro Carvalho Chehab 
146cb7a01acSMauro Carvalho Chehab static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
147cb7a01acSMauro Carvalho Chehab {
148cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
149cb7a01acSMauro Carvalho Chehab 	unsigned char reg, data;
150cb7a01acSMauro Carvalho Chehab 
151cb7a01acSMauro Carvalho Chehab 	while (*regs != 0x00) {
152cb7a01acSMauro Carvalho Chehab 		reg = *(regs++);
153cb7a01acSMauro Carvalho Chehab 		data = *(regs++);
154cb7a01acSMauro Carvalho Chehab 
155cb7a01acSMauro Carvalho Chehab 		/* According with datasheets, reserved regs should be
156cb7a01acSMauro Carvalho Chehab 		   filled with 0 - seems better not to touch on they */
157cb7a01acSMauro Carvalho Chehab 		if (saa711x_has_reg(state->ident, reg)) {
158cb7a01acSMauro Carvalho Chehab 			if (saa711x_write(sd, reg, data) < 0)
159cb7a01acSMauro Carvalho Chehab 				return -1;
160cb7a01acSMauro Carvalho Chehab 		} else {
161cb7a01acSMauro Carvalho Chehab 			v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
162cb7a01acSMauro Carvalho Chehab 		}
163cb7a01acSMauro Carvalho Chehab 	}
164cb7a01acSMauro Carvalho Chehab 	return 0;
165cb7a01acSMauro Carvalho Chehab }
166cb7a01acSMauro Carvalho Chehab 
167cb7a01acSMauro Carvalho Chehab static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
168cb7a01acSMauro Carvalho Chehab {
169cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
170cb7a01acSMauro Carvalho Chehab 
171cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_byte_data(client, reg);
172cb7a01acSMauro Carvalho Chehab }
173cb7a01acSMauro Carvalho Chehab 
174cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
175cb7a01acSMauro Carvalho Chehab 
176cb7a01acSMauro Carvalho Chehab /* SAA7111 initialization table */
177cb7a01acSMauro Carvalho Chehab static const unsigned char saa7111_init[] = {
178cb7a01acSMauro Carvalho Chehab 	R_01_INC_DELAY, 0x00,		/* reserved */
179cb7a01acSMauro Carvalho Chehab 
180cb7a01acSMauro Carvalho Chehab 	/*front end */
181cb7a01acSMauro Carvalho Chehab 	R_02_INPUT_CNTL_1, 0xd0,	/* FUSE=3, GUDL=2, MODE=0 */
182cb7a01acSMauro Carvalho Chehab 	R_03_INPUT_CNTL_2, 0x23,	/* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0,
183cb7a01acSMauro Carvalho Chehab 					 * GAFIX=0, GAI1=256, GAI2=256 */
184cb7a01acSMauro Carvalho Chehab 	R_04_INPUT_CNTL_3, 0x00,	/* GAI1=256 */
185cb7a01acSMauro Carvalho Chehab 	R_05_INPUT_CNTL_4, 0x00,	/* GAI2=256 */
186cb7a01acSMauro Carvalho Chehab 
187cb7a01acSMauro Carvalho Chehab 	/* decoder */
188cb7a01acSMauro Carvalho Chehab 	R_06_H_SYNC_START, 0xf3,	/* HSB at  13(50Hz) /  17(60Hz)
189cb7a01acSMauro Carvalho Chehab 					 * pixels after end of last line */
190cb7a01acSMauro Carvalho Chehab 	R_07_H_SYNC_STOP, 0xe8,		/* HSS seems to be needed to
191cb7a01acSMauro Carvalho Chehab 					 * work with NTSC, too */
192cb7a01acSMauro Carvalho Chehab 	R_08_SYNC_CNTL, 0xc8,		/* AUFD=1, FSEL=1, EXFIL=0,
193cb7a01acSMauro Carvalho Chehab 					 * VTRC=1, HPLL=0, VNOI=0 */
194cb7a01acSMauro Carvalho Chehab 	R_09_LUMA_CNTL, 0x01,		/* BYPS=0, PREF=0, BPSS=0,
195cb7a01acSMauro Carvalho Chehab 					 * VBLB=0, UPTCV=0, APER=1 */
196cb7a01acSMauro Carvalho Chehab 	R_0A_LUMA_BRIGHT_CNTL, 0x80,
197cb7a01acSMauro Carvalho Chehab 	R_0B_LUMA_CONTRAST_CNTL, 0x47,	/* 0b - CONT=1.109 */
198cb7a01acSMauro Carvalho Chehab 	R_0C_CHROMA_SAT_CNTL, 0x40,
199cb7a01acSMauro Carvalho Chehab 	R_0D_CHROMA_HUE_CNTL, 0x00,
200cb7a01acSMauro Carvalho Chehab 	R_0E_CHROMA_CNTL_1, 0x01,	/* 0e - CDTO=0, CSTD=0, DCCF=0,
201cb7a01acSMauro Carvalho Chehab 					 * FCTC=0, CHBW=1 */
202cb7a01acSMauro Carvalho Chehab 	R_0F_CHROMA_GAIN_CNTL, 0x00,	/* reserved */
203cb7a01acSMauro Carvalho Chehab 	R_10_CHROMA_CNTL_2, 0x48,	/* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
204cb7a01acSMauro Carvalho Chehab 	R_11_MODE_DELAY_CNTL, 0x1c,	/* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
205cb7a01acSMauro Carvalho Chehab 					 * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
206cb7a01acSMauro Carvalho Chehab 	R_12_RT_SIGNAL_CNTL, 0x00,	/* 12 - output control 2 */
207cb7a01acSMauro Carvalho Chehab 	R_13_RT_X_PORT_OUT_CNTL, 0x00,	/* 13 - output control 3 */
208cb7a01acSMauro Carvalho Chehab 	R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
209cb7a01acSMauro Carvalho Chehab 	R_15_VGATE_START_FID_CHG, 0x00,
210cb7a01acSMauro Carvalho Chehab 	R_16_VGATE_STOP, 0x00,
211cb7a01acSMauro Carvalho Chehab 	R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
212cb7a01acSMauro Carvalho Chehab 
213cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
214cb7a01acSMauro Carvalho Chehab };
215cb7a01acSMauro Carvalho Chehab 
216cb7a01acSMauro Carvalho Chehab /* SAA7113 init codes */
217cb7a01acSMauro Carvalho Chehab static const unsigned char saa7113_init[] = {
218cb7a01acSMauro Carvalho Chehab 	R_01_INC_DELAY, 0x08,
219cb7a01acSMauro Carvalho Chehab 	R_02_INPUT_CNTL_1, 0xc2,
220cb7a01acSMauro Carvalho Chehab 	R_03_INPUT_CNTL_2, 0x30,
221cb7a01acSMauro Carvalho Chehab 	R_04_INPUT_CNTL_3, 0x00,
222cb7a01acSMauro Carvalho Chehab 	R_05_INPUT_CNTL_4, 0x00,
223cb7a01acSMauro Carvalho Chehab 	R_06_H_SYNC_START, 0x89,
224cb7a01acSMauro Carvalho Chehab 	R_07_H_SYNC_STOP, 0x0d,
225cb7a01acSMauro Carvalho Chehab 	R_08_SYNC_CNTL, 0x88,
226cb7a01acSMauro Carvalho Chehab 	R_09_LUMA_CNTL, 0x01,
227cb7a01acSMauro Carvalho Chehab 	R_0A_LUMA_BRIGHT_CNTL, 0x80,
228cb7a01acSMauro Carvalho Chehab 	R_0B_LUMA_CONTRAST_CNTL, 0x47,
229cb7a01acSMauro Carvalho Chehab 	R_0C_CHROMA_SAT_CNTL, 0x40,
230cb7a01acSMauro Carvalho Chehab 	R_0D_CHROMA_HUE_CNTL, 0x00,
231cb7a01acSMauro Carvalho Chehab 	R_0E_CHROMA_CNTL_1, 0x01,
232cb7a01acSMauro Carvalho Chehab 	R_0F_CHROMA_GAIN_CNTL, 0x2a,
233cb7a01acSMauro Carvalho Chehab 	R_10_CHROMA_CNTL_2, 0x08,
234cb7a01acSMauro Carvalho Chehab 	R_11_MODE_DELAY_CNTL, 0x0c,
235cb7a01acSMauro Carvalho Chehab 	R_12_RT_SIGNAL_CNTL, 0x07,
236cb7a01acSMauro Carvalho Chehab 	R_13_RT_X_PORT_OUT_CNTL, 0x00,
237cb7a01acSMauro Carvalho Chehab 	R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
238cb7a01acSMauro Carvalho Chehab 	R_15_VGATE_START_FID_CHG, 0x00,
239cb7a01acSMauro Carvalho Chehab 	R_16_VGATE_STOP, 0x00,
240cb7a01acSMauro Carvalho Chehab 	R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
241cb7a01acSMauro Carvalho Chehab 
242cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
243cb7a01acSMauro Carvalho Chehab };
244cb7a01acSMauro Carvalho Chehab 
245cb7a01acSMauro Carvalho Chehab /* If a value differs from the Hauppauge driver values, then the comment starts with
246cb7a01acSMauro Carvalho Chehab    'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the
247cb7a01acSMauro Carvalho Chehab    Hauppauge driver sets. */
248cb7a01acSMauro Carvalho Chehab 
249cb7a01acSMauro Carvalho Chehab /* SAA7114 and SAA7115 initialization table */
250cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_init_auto_input[] = {
251cb7a01acSMauro Carvalho Chehab 		/* Front-End Part */
252cb7a01acSMauro Carvalho Chehab 	R_01_INC_DELAY, 0x48,			/* white peak control disabled */
253cb7a01acSMauro Carvalho Chehab 	R_03_INPUT_CNTL_2, 0x20,		/* was 0x30. 0x20: long vertical blanking */
254cb7a01acSMauro Carvalho Chehab 	R_04_INPUT_CNTL_3, 0x90,		/* analog gain set to 0 */
255cb7a01acSMauro Carvalho Chehab 	R_05_INPUT_CNTL_4, 0x90,		/* analog gain set to 0 */
256cb7a01acSMauro Carvalho Chehab 		/* Decoder Part */
257cb7a01acSMauro Carvalho Chehab 	R_06_H_SYNC_START, 0xeb,		/* horiz sync begin = -21 */
258cb7a01acSMauro Carvalho Chehab 	R_07_H_SYNC_STOP, 0xe0,			/* horiz sync stop = -17 */
259cb7a01acSMauro Carvalho Chehab 	R_09_LUMA_CNTL, 0x53,			/* 0x53, was 0x56 for 60hz. luminance control */
260cb7a01acSMauro Carvalho Chehab 	R_0A_LUMA_BRIGHT_CNTL, 0x80,		/* was 0x88. decoder brightness, 0x80 is itu standard */
261cb7a01acSMauro Carvalho Chehab 	R_0B_LUMA_CONTRAST_CNTL, 0x44,		/* was 0x48. decoder contrast, 0x44 is itu standard */
262cb7a01acSMauro Carvalho Chehab 	R_0C_CHROMA_SAT_CNTL, 0x40,		/* was 0x47. decoder saturation, 0x40 is itu standard */
263cb7a01acSMauro Carvalho Chehab 	R_0D_CHROMA_HUE_CNTL, 0x00,
264cb7a01acSMauro Carvalho Chehab 	R_0F_CHROMA_GAIN_CNTL, 0x00,		/* use automatic gain  */
265cb7a01acSMauro Carvalho Chehab 	R_10_CHROMA_CNTL_2, 0x06,		/* chroma: active adaptive combfilter */
266cb7a01acSMauro Carvalho Chehab 	R_11_MODE_DELAY_CNTL, 0x00,
267cb7a01acSMauro Carvalho Chehab 	R_12_RT_SIGNAL_CNTL, 0x9d,		/* RTS0 output control: VGATE */
268cb7a01acSMauro Carvalho Chehab 	R_13_RT_X_PORT_OUT_CNTL, 0x80,		/* ITU656 standard mode, RTCO output enable RTCE */
269cb7a01acSMauro Carvalho Chehab 	R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
270cb7a01acSMauro Carvalho Chehab 	R_18_RAW_DATA_GAIN_CNTL, 0x40,		/* gain 0x00 = nominal */
271cb7a01acSMauro Carvalho Chehab 	R_19_RAW_DATA_OFF_CNTL, 0x80,
272cb7a01acSMauro Carvalho Chehab 	R_1A_COLOR_KILL_LVL_CNTL, 0x77,		/* recommended value */
273cb7a01acSMauro Carvalho Chehab 	R_1B_MISC_TVVCRDET, 0x42,		/* recommended value */
274cb7a01acSMauro Carvalho Chehab 	R_1C_ENHAN_COMB_CTRL1, 0xa9,		/* recommended value */
275cb7a01acSMauro Carvalho Chehab 	R_1D_ENHAN_COMB_CTRL2, 0x01,		/* recommended value */
276cb7a01acSMauro Carvalho Chehab 
277cb7a01acSMauro Carvalho Chehab 
278cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x0,		/* No tasks enabled at init */
279cb7a01acSMauro Carvalho Chehab 
280cb7a01acSMauro Carvalho Chehab 		/* Power Device Control */
281cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,	/* reset device */
282cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,	/* set device programmed, all in operational mode */
283cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
284cb7a01acSMauro Carvalho Chehab };
285cb7a01acSMauro Carvalho Chehab 
286cb7a01acSMauro Carvalho Chehab /* Used to reset saa7113, saa7114 and saa7115 */
287cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_cfg_reset_scaler[] = {
288cb7a01acSMauro Carvalho Chehab 	R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00,	/* disable I-port output */
289cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,		/* reset scaler */
290cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,		/* activate scaler */
291cb7a01acSMauro Carvalho Chehab 	R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,	/* enable I-port output */
292cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
293cb7a01acSMauro Carvalho Chehab };
294cb7a01acSMauro Carvalho Chehab 
295cb7a01acSMauro Carvalho Chehab /* ============== SAA7715 VIDEO templates =============  */
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_cfg_60hz_video[] = {
298cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x00,			/* reset tasks */
299cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,		/* reset scaler */
300cb7a01acSMauro Carvalho Chehab 
301cb7a01acSMauro Carvalho Chehab 	R_15_VGATE_START_FID_CHG, 0x03,
302cb7a01acSMauro Carvalho Chehab 	R_16_VGATE_STOP, 0x11,
303cb7a01acSMauro Carvalho Chehab 	R_17_MISC_VGATE_CONF_AND_MSB, 0x9c,
304cb7a01acSMauro Carvalho Chehab 
305cb7a01acSMauro Carvalho Chehab 	R_08_SYNC_CNTL, 0x68,			/* 0xBO: auto detection, 0x68 = NTSC */
306cb7a01acSMauro Carvalho Chehab 	R_0E_CHROMA_CNTL_1, 0x07,		/* video autodetection is on */
307cb7a01acSMauro Carvalho Chehab 
308cb7a01acSMauro Carvalho Chehab 	R_5A_V_OFF_FOR_SLICER, 0x06,		/* standard 60hz value for ITU656 line counting */
309cb7a01acSMauro Carvalho Chehab 
310cb7a01acSMauro Carvalho Chehab 	/* Task A */
311cb7a01acSMauro Carvalho Chehab 	R_90_A_TASK_HANDLING_CNTL, 0x80,
312cb7a01acSMauro Carvalho Chehab 	R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
313cb7a01acSMauro Carvalho Chehab 	R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
314cb7a01acSMauro Carvalho Chehab 	R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
315cb7a01acSMauro Carvalho Chehab 
316cb7a01acSMauro Carvalho Chehab 	/* hoffset low (input), 0x0002 is minimum */
317cb7a01acSMauro Carvalho Chehab 	R_94_A_HORIZ_INPUT_WINDOW_START, 0x01,
318cb7a01acSMauro Carvalho Chehab 	R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
319cb7a01acSMauro Carvalho Chehab 
320cb7a01acSMauro Carvalho Chehab 	/* hsize low (input), 0x02d0 = 720 */
321cb7a01acSMauro Carvalho Chehab 	R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
322cb7a01acSMauro Carvalho Chehab 	R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
323cb7a01acSMauro Carvalho Chehab 
324cb7a01acSMauro Carvalho Chehab 	R_98_A_VERT_INPUT_WINDOW_START, 0x05,
325cb7a01acSMauro Carvalho Chehab 	R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
326cb7a01acSMauro Carvalho Chehab 
327cb7a01acSMauro Carvalho Chehab 	R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c,
328cb7a01acSMauro Carvalho Chehab 	R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
329cb7a01acSMauro Carvalho Chehab 
330cb7a01acSMauro Carvalho Chehab 	R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
331cb7a01acSMauro Carvalho Chehab 	R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05,
332cb7a01acSMauro Carvalho Chehab 
333cb7a01acSMauro Carvalho Chehab 	R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c,
334cb7a01acSMauro Carvalho Chehab 	R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00,
335cb7a01acSMauro Carvalho Chehab 
336cb7a01acSMauro Carvalho Chehab 	/* Task B */
337cb7a01acSMauro Carvalho Chehab 	R_C0_B_TASK_HANDLING_CNTL, 0x00,
338cb7a01acSMauro Carvalho Chehab 	R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
339cb7a01acSMauro Carvalho Chehab 	R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
340cb7a01acSMauro Carvalho Chehab 	R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab 	/* 0x0002 is minimum */
343cb7a01acSMauro Carvalho Chehab 	R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02,
344cb7a01acSMauro Carvalho Chehab 	R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
345cb7a01acSMauro Carvalho Chehab 
346cb7a01acSMauro Carvalho Chehab 	/* 0x02d0 = 720 */
347cb7a01acSMauro Carvalho Chehab 	R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
348cb7a01acSMauro Carvalho Chehab 	R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
349cb7a01acSMauro Carvalho Chehab 
350cb7a01acSMauro Carvalho Chehab 	/* vwindow start 0x12 = 18 */
351cb7a01acSMauro Carvalho Chehab 	R_C8_B_VERT_INPUT_WINDOW_START, 0x12,
352cb7a01acSMauro Carvalho Chehab 	R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
353cb7a01acSMauro Carvalho Chehab 
354cb7a01acSMauro Carvalho Chehab 	/* vwindow length 0xf8 = 248 */
355cb7a01acSMauro Carvalho Chehab 	R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1,
356cb7a01acSMauro Carvalho Chehab 	R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9,
357cb7a01acSMauro Carvalho Chehab 
358cb7a01acSMauro Carvalho Chehab 	/* hwindow 0x02d0 = 720 */
359cb7a01acSMauro Carvalho Chehab 	R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
360cb7a01acSMauro Carvalho Chehab 	R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
361cb7a01acSMauro Carvalho Chehab 
362cb7a01acSMauro Carvalho Chehab 	R_F0_LFCO_PER_LINE, 0xad,		/* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
363cb7a01acSMauro Carvalho Chehab 	R_F1_P_I_PARAM_SELECT, 0x05,		/* low bit with 0xF0 */
364cb7a01acSMauro Carvalho Chehab 	R_F5_PULSGEN_LINE_LENGTH, 0xad,
365cb7a01acSMauro Carvalho Chehab 	R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
366cb7a01acSMauro Carvalho Chehab 
367cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
368cb7a01acSMauro Carvalho Chehab };
369cb7a01acSMauro Carvalho Chehab 
370cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_cfg_50hz_video[] = {
371cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x00,
372cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,	/* reset scaler */
373cb7a01acSMauro Carvalho Chehab 
374cb7a01acSMauro Carvalho Chehab 	R_15_VGATE_START_FID_CHG, 0x37,		/* VGATE start */
375cb7a01acSMauro Carvalho Chehab 	R_16_VGATE_STOP, 0x16,
376cb7a01acSMauro Carvalho Chehab 	R_17_MISC_VGATE_CONF_AND_MSB, 0x99,
377cb7a01acSMauro Carvalho Chehab 
378cb7a01acSMauro Carvalho Chehab 	R_08_SYNC_CNTL, 0x28,			/* 0x28 = PAL */
379cb7a01acSMauro Carvalho Chehab 	R_0E_CHROMA_CNTL_1, 0x07,
380cb7a01acSMauro Carvalho Chehab 
381cb7a01acSMauro Carvalho Chehab 	R_5A_V_OFF_FOR_SLICER, 0x03,		/* standard 50hz value */
382cb7a01acSMauro Carvalho Chehab 
383cb7a01acSMauro Carvalho Chehab 	/* Task A */
384cb7a01acSMauro Carvalho Chehab 	R_90_A_TASK_HANDLING_CNTL, 0x81,
385cb7a01acSMauro Carvalho Chehab 	R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
386cb7a01acSMauro Carvalho Chehab 	R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
387cb7a01acSMauro Carvalho Chehab 	R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
388cb7a01acSMauro Carvalho Chehab 
389cb7a01acSMauro Carvalho Chehab 	/* This is weird: the datasheet says that you should use 2 as the minimum value, */
390cb7a01acSMauro Carvalho Chehab 	/* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
391cb7a01acSMauro Carvalho Chehab 	/* hoffset low (input), 0x0002 is minimum */
392cb7a01acSMauro Carvalho Chehab 	R_94_A_HORIZ_INPUT_WINDOW_START, 0x00,
393cb7a01acSMauro Carvalho Chehab 	R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
394cb7a01acSMauro Carvalho Chehab 
395cb7a01acSMauro Carvalho Chehab 	/* hsize low (input), 0x02d0 = 720 */
396cb7a01acSMauro Carvalho Chehab 	R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
397cb7a01acSMauro Carvalho Chehab 	R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
398cb7a01acSMauro Carvalho Chehab 
399cb7a01acSMauro Carvalho Chehab 	R_98_A_VERT_INPUT_WINDOW_START, 0x03,
400cb7a01acSMauro Carvalho Chehab 	R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
401cb7a01acSMauro Carvalho Chehab 
402cb7a01acSMauro Carvalho Chehab 	/* vsize 0x12 = 18 */
403cb7a01acSMauro Carvalho Chehab 	R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12,
404cb7a01acSMauro Carvalho Chehab 	R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
405cb7a01acSMauro Carvalho Chehab 
406cb7a01acSMauro Carvalho Chehab 	/* hsize 0x05a0 = 1440 */
407cb7a01acSMauro Carvalho Chehab 	R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
408cb7a01acSMauro Carvalho Chehab 	R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05,	/* hsize hi (output) */
409cb7a01acSMauro Carvalho Chehab 	R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12,		/* vsize low (output), 0x12 = 18 */
410cb7a01acSMauro Carvalho Chehab 	R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00,	/* vsize hi (output) */
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab 	/* Task B */
413cb7a01acSMauro Carvalho Chehab 	R_C0_B_TASK_HANDLING_CNTL, 0x00,
414cb7a01acSMauro Carvalho Chehab 	R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
415cb7a01acSMauro Carvalho Chehab 	R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
416cb7a01acSMauro Carvalho Chehab 	R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
417cb7a01acSMauro Carvalho Chehab 
418cb7a01acSMauro Carvalho Chehab 	/* This is weird: the datasheet says that you should use 2 as the minimum value, */
419cb7a01acSMauro Carvalho Chehab 	/* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
420cb7a01acSMauro Carvalho Chehab 	/* hoffset low (input), 0x0002 is minimum. See comment above. */
421cb7a01acSMauro Carvalho Chehab 	R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00,
422cb7a01acSMauro Carvalho Chehab 	R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
423cb7a01acSMauro Carvalho Chehab 
424cb7a01acSMauro Carvalho Chehab 	/* hsize 0x02d0 = 720 */
425cb7a01acSMauro Carvalho Chehab 	R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
426cb7a01acSMauro Carvalho Chehab 	R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
427cb7a01acSMauro Carvalho Chehab 
428cb7a01acSMauro Carvalho Chehab 	/* voffset 0x16 = 22 */
429cb7a01acSMauro Carvalho Chehab 	R_C8_B_VERT_INPUT_WINDOW_START, 0x16,
430cb7a01acSMauro Carvalho Chehab 	R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
431cb7a01acSMauro Carvalho Chehab 
432cb7a01acSMauro Carvalho Chehab 	/* vsize 0x0120 = 288 */
433cb7a01acSMauro Carvalho Chehab 	R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20,
434cb7a01acSMauro Carvalho Chehab 	R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01,
435cb7a01acSMauro Carvalho Chehab 
436cb7a01acSMauro Carvalho Chehab 	/* hsize 0x02d0 = 720 */
437cb7a01acSMauro Carvalho Chehab 	R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
438cb7a01acSMauro Carvalho Chehab 	R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
439cb7a01acSMauro Carvalho Chehab 
440cb7a01acSMauro Carvalho Chehab 	R_F0_LFCO_PER_LINE, 0xb0,		/* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
441cb7a01acSMauro Carvalho Chehab 	R_F1_P_I_PARAM_SELECT, 0x05,		/* low bit with 0xF0, (was 0x05) */
442cb7a01acSMauro Carvalho Chehab 	R_F5_PULSGEN_LINE_LENGTH, 0xb0,
443cb7a01acSMauro Carvalho Chehab 	R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
444cb7a01acSMauro Carvalho Chehab 
445cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
446cb7a01acSMauro Carvalho Chehab };
447cb7a01acSMauro Carvalho Chehab 
448cb7a01acSMauro Carvalho Chehab /* ============== SAA7715 VIDEO templates (end) =======  */
449cb7a01acSMauro Carvalho Chehab 
450cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_cfg_vbi_on[] = {
451cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x00,			/* reset tasks */
452cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,		/* reset scaler */
453cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x30,			/* Activate both tasks */
454cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,		/* activate scaler */
455cb7a01acSMauro Carvalho Chehab 	R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,	/* Enable I-port output */
456cb7a01acSMauro Carvalho Chehab 
457cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
458cb7a01acSMauro Carvalho Chehab };
459cb7a01acSMauro Carvalho Chehab 
460cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_cfg_vbi_off[] = {
461cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x00,			/* reset tasks */
462cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,		/* reset scaler */
463cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x20,			/* Activate only task "B" */
464cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,		/* activate scaler */
465cb7a01acSMauro Carvalho Chehab 	R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,	/* Enable I-port output */
466cb7a01acSMauro Carvalho Chehab 
467cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
468cb7a01acSMauro Carvalho Chehab };
469cb7a01acSMauro Carvalho Chehab 
470cb7a01acSMauro Carvalho Chehab 
471cb7a01acSMauro Carvalho Chehab static const unsigned char saa7115_init_misc[] = {
472cb7a01acSMauro Carvalho Chehab 	R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01,
473cb7a01acSMauro Carvalho Chehab 	R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01,
474cb7a01acSMauro Carvalho Chehab 	R_84_I_PORT_SIGNAL_DEF, 0x20,
475cb7a01acSMauro Carvalho Chehab 	R_85_I_PORT_SIGNAL_POLAR, 0x21,
476cb7a01acSMauro Carvalho Chehab 	R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5,
477cb7a01acSMauro Carvalho Chehab 	R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,
478cb7a01acSMauro Carvalho Chehab 
479cb7a01acSMauro Carvalho Chehab 	/* Task A */
480cb7a01acSMauro Carvalho Chehab 	R_A0_A_HORIZ_PRESCALING, 0x01,
481cb7a01acSMauro Carvalho Chehab 	R_A1_A_ACCUMULATION_LENGTH, 0x00,
482cb7a01acSMauro Carvalho Chehab 	R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
483cb7a01acSMauro Carvalho Chehab 
484cb7a01acSMauro Carvalho Chehab 	/* Configure controls at nominal value*/
485cb7a01acSMauro Carvalho Chehab 	R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80,
486cb7a01acSMauro Carvalho Chehab 	R_A5_A_LUMA_CONTRAST_CNTL, 0x40,
487cb7a01acSMauro Carvalho Chehab 	R_A6_A_CHROMA_SATURATION_CNTL, 0x40,
488cb7a01acSMauro Carvalho Chehab 
489cb7a01acSMauro Carvalho Chehab 	/* note: 2 x zoom ensures that VBI lines have same length as video lines. */
490cb7a01acSMauro Carvalho Chehab 	R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00,
491cb7a01acSMauro Carvalho Chehab 	R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02,
492cb7a01acSMauro Carvalho Chehab 
493cb7a01acSMauro Carvalho Chehab 	R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00,
494cb7a01acSMauro Carvalho Chehab 
495cb7a01acSMauro Carvalho Chehab 	/* must be horiz lum scaling / 2 */
496cb7a01acSMauro Carvalho Chehab 	R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00,
497cb7a01acSMauro Carvalho Chehab 	R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01,
498cb7a01acSMauro Carvalho Chehab 
499cb7a01acSMauro Carvalho Chehab 	/* must be offset luma / 2 */
500cb7a01acSMauro Carvalho Chehab 	R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00,
501cb7a01acSMauro Carvalho Chehab 
502cb7a01acSMauro Carvalho Chehab 	R_B0_A_VERT_LUMA_SCALING_INC, 0x00,
503cb7a01acSMauro Carvalho Chehab 	R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04,
504cb7a01acSMauro Carvalho Chehab 
505cb7a01acSMauro Carvalho Chehab 	R_B2_A_VERT_CHROMA_SCALING_INC, 0x00,
506cb7a01acSMauro Carvalho Chehab 	R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04,
507cb7a01acSMauro Carvalho Chehab 
508cb7a01acSMauro Carvalho Chehab 	R_B4_A_VERT_SCALING_MODE_CNTL, 0x01,
509cb7a01acSMauro Carvalho Chehab 
510cb7a01acSMauro Carvalho Chehab 	R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00,
511cb7a01acSMauro Carvalho Chehab 	R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00,
512cb7a01acSMauro Carvalho Chehab 	R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00,
513cb7a01acSMauro Carvalho Chehab 	R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00,
514cb7a01acSMauro Carvalho Chehab 
515cb7a01acSMauro Carvalho Chehab 	R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00,
516cb7a01acSMauro Carvalho Chehab 	R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00,
517cb7a01acSMauro Carvalho Chehab 	R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00,
518cb7a01acSMauro Carvalho Chehab 	R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00,
519cb7a01acSMauro Carvalho Chehab 
520cb7a01acSMauro Carvalho Chehab 	/* Task B */
521cb7a01acSMauro Carvalho Chehab 	R_D0_B_HORIZ_PRESCALING, 0x01,
522cb7a01acSMauro Carvalho Chehab 	R_D1_B_ACCUMULATION_LENGTH, 0x00,
523cb7a01acSMauro Carvalho Chehab 	R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
524cb7a01acSMauro Carvalho Chehab 
525cb7a01acSMauro Carvalho Chehab 	/* Configure controls at nominal value*/
526cb7a01acSMauro Carvalho Chehab 	R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80,
527cb7a01acSMauro Carvalho Chehab 	R_D5_B_LUMA_CONTRAST_CNTL, 0x40,
528cb7a01acSMauro Carvalho Chehab 	R_D6_B_CHROMA_SATURATION_CNTL, 0x40,
529cb7a01acSMauro Carvalho Chehab 
530cb7a01acSMauro Carvalho Chehab 	/* hor lum scaling 0x0400 = 1 */
531cb7a01acSMauro Carvalho Chehab 	R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00,
532cb7a01acSMauro Carvalho Chehab 	R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04,
533cb7a01acSMauro Carvalho Chehab 
534cb7a01acSMauro Carvalho Chehab 	R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00,
535cb7a01acSMauro Carvalho Chehab 
536cb7a01acSMauro Carvalho Chehab 	/* must be hor lum scaling / 2 */
537cb7a01acSMauro Carvalho Chehab 	R_DC_B_HORIZ_CHROMA_SCALING, 0x00,
538cb7a01acSMauro Carvalho Chehab 	R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02,
539cb7a01acSMauro Carvalho Chehab 
540cb7a01acSMauro Carvalho Chehab 	/* must be offset luma / 2 */
541cb7a01acSMauro Carvalho Chehab 	R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00,
542cb7a01acSMauro Carvalho Chehab 
543cb7a01acSMauro Carvalho Chehab 	R_E0_B_VERT_LUMA_SCALING_INC, 0x00,
544cb7a01acSMauro Carvalho Chehab 	R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04,
545cb7a01acSMauro Carvalho Chehab 
546cb7a01acSMauro Carvalho Chehab 	R_E2_B_VERT_CHROMA_SCALING_INC, 0x00,
547cb7a01acSMauro Carvalho Chehab 	R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04,
548cb7a01acSMauro Carvalho Chehab 
549cb7a01acSMauro Carvalho Chehab 	R_E4_B_VERT_SCALING_MODE_CNTL, 0x01,
550cb7a01acSMauro Carvalho Chehab 
551cb7a01acSMauro Carvalho Chehab 	R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00,
552cb7a01acSMauro Carvalho Chehab 	R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00,
553cb7a01acSMauro Carvalho Chehab 	R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00,
554cb7a01acSMauro Carvalho Chehab 	R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00,
555cb7a01acSMauro Carvalho Chehab 
556cb7a01acSMauro Carvalho Chehab 	R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00,
557cb7a01acSMauro Carvalho Chehab 	R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00,
558cb7a01acSMauro Carvalho Chehab 	R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00,
559cb7a01acSMauro Carvalho Chehab 	R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00,
560cb7a01acSMauro Carvalho Chehab 
561cb7a01acSMauro Carvalho Chehab 	R_F2_NOMINAL_PLL2_DTO, 0x50,		/* crystal clock = 24.576 MHz, target = 27MHz */
562cb7a01acSMauro Carvalho Chehab 	R_F3_PLL_INCREMENT, 0x46,
563cb7a01acSMauro Carvalho Chehab 	R_F4_PLL2_STATUS, 0x00,
564cb7a01acSMauro Carvalho Chehab 	R_F7_PULSE_A_POS_MSB, 0x4b,		/* not the recommended settings! */
565cb7a01acSMauro Carvalho Chehab 	R_F8_PULSE_B_POS, 0x00,
566cb7a01acSMauro Carvalho Chehab 	R_F9_PULSE_B_POS_MSB, 0x4b,
567cb7a01acSMauro Carvalho Chehab 	R_FA_PULSE_C_POS, 0x00,
568cb7a01acSMauro Carvalho Chehab 	R_FB_PULSE_C_POS_MSB, 0x4b,
569cb7a01acSMauro Carvalho Chehab 
570cb7a01acSMauro Carvalho Chehab 	/* PLL2 lock detection settings: 71 lines 50% phase error */
571cb7a01acSMauro Carvalho Chehab 	R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88,
572cb7a01acSMauro Carvalho Chehab 
573cb7a01acSMauro Carvalho Chehab 	/* Turn off VBI */
574cb7a01acSMauro Carvalho Chehab 	R_40_SLICER_CNTL_1, 0x20,             /* No framing code errors allowed. */
575cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE, 0xff,
576cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+1, 0xff,
577cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+2, 0xff,
578cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+3, 0xff,
579cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+4, 0xff,
580cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+5, 0xff,
581cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+6, 0xff,
582cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+7, 0xff,
583cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+8, 0xff,
584cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+9, 0xff,
585cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+10, 0xff,
586cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+11, 0xff,
587cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+12, 0xff,
588cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+13, 0xff,
589cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+14, 0xff,
590cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+15, 0xff,
591cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+16, 0xff,
592cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+17, 0xff,
593cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+18, 0xff,
594cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+19, 0xff,
595cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+20, 0xff,
596cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+21, 0xff,
597cb7a01acSMauro Carvalho Chehab 	R_41_LCR_BASE+22, 0xff,
598cb7a01acSMauro Carvalho Chehab 	R_58_PROGRAM_FRAMING_CODE, 0x40,
599cb7a01acSMauro Carvalho Chehab 	R_59_H_OFF_FOR_SLICER, 0x47,
600cb7a01acSMauro Carvalho Chehab 	R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83,
601cb7a01acSMauro Carvalho Chehab 	R_5D_DID, 0xbd,
602cb7a01acSMauro Carvalho Chehab 	R_5E_SDID, 0x35,
603cb7a01acSMauro Carvalho Chehab 
604cb7a01acSMauro Carvalho Chehab 	R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */
605cb7a01acSMauro Carvalho Chehab 
606cb7a01acSMauro Carvalho Chehab 	R_80_GLOBAL_CNTL_1, 0x20,		/* enable task B */
607cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
608cb7a01acSMauro Carvalho Chehab 	R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,
609cb7a01acSMauro Carvalho Chehab 	0x00, 0x00
610cb7a01acSMauro Carvalho Chehab };
611cb7a01acSMauro Carvalho Chehab 
612cb7a01acSMauro Carvalho Chehab static int saa711x_odd_parity(u8 c)
613cb7a01acSMauro Carvalho Chehab {
614cb7a01acSMauro Carvalho Chehab 	c ^= (c >> 4);
615cb7a01acSMauro Carvalho Chehab 	c ^= (c >> 2);
616cb7a01acSMauro Carvalho Chehab 	c ^= (c >> 1);
617cb7a01acSMauro Carvalho Chehab 
618cb7a01acSMauro Carvalho Chehab 	return c & 1;
619cb7a01acSMauro Carvalho Chehab }
620cb7a01acSMauro Carvalho Chehab 
621cb7a01acSMauro Carvalho Chehab static int saa711x_decode_vps(u8 *dst, u8 *p)
622cb7a01acSMauro Carvalho Chehab {
623cb7a01acSMauro Carvalho Chehab 	static const u8 biphase_tbl[] = {
624cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
625cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
626cb7a01acSMauro Carvalho Chehab 		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
627cb7a01acSMauro Carvalho Chehab 		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
628cb7a01acSMauro Carvalho Chehab 		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
629cb7a01acSMauro Carvalho Chehab 		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
630cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
631cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
632cb7a01acSMauro Carvalho Chehab 		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
633cb7a01acSMauro Carvalho Chehab 		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
634cb7a01acSMauro Carvalho Chehab 		0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
635cb7a01acSMauro Carvalho Chehab 		0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
636cb7a01acSMauro Carvalho Chehab 		0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
637cb7a01acSMauro Carvalho Chehab 		0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
638cb7a01acSMauro Carvalho Chehab 		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
639cb7a01acSMauro Carvalho Chehab 		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
640cb7a01acSMauro Carvalho Chehab 		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
641cb7a01acSMauro Carvalho Chehab 		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
642cb7a01acSMauro Carvalho Chehab 		0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
643cb7a01acSMauro Carvalho Chehab 		0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
644cb7a01acSMauro Carvalho Chehab 		0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
645cb7a01acSMauro Carvalho Chehab 		0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
646cb7a01acSMauro Carvalho Chehab 		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
647cb7a01acSMauro Carvalho Chehab 		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
648cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
649cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
650cb7a01acSMauro Carvalho Chehab 		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
651cb7a01acSMauro Carvalho Chehab 		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
652cb7a01acSMauro Carvalho Chehab 		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
653cb7a01acSMauro Carvalho Chehab 		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
654cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
655cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
656cb7a01acSMauro Carvalho Chehab 	};
657cb7a01acSMauro Carvalho Chehab 	int i;
658cb7a01acSMauro Carvalho Chehab 	u8 c, err = 0;
659cb7a01acSMauro Carvalho Chehab 
660cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < 2 * 13; i += 2) {
661cb7a01acSMauro Carvalho Chehab 		err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
662cb7a01acSMauro Carvalho Chehab 		c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4);
663cb7a01acSMauro Carvalho Chehab 		dst[i / 2] = c;
664cb7a01acSMauro Carvalho Chehab 	}
665cb7a01acSMauro Carvalho Chehab 	return err & 0xf0;
666cb7a01acSMauro Carvalho Chehab }
667cb7a01acSMauro Carvalho Chehab 
668cb7a01acSMauro Carvalho Chehab static int saa711x_decode_wss(u8 *p)
669cb7a01acSMauro Carvalho Chehab {
670cb7a01acSMauro Carvalho Chehab 	static const int wss_bits[8] = {
671cb7a01acSMauro Carvalho Chehab 		0, 0, 0, 1, 0, 1, 1, 1
672cb7a01acSMauro Carvalho Chehab 	};
673cb7a01acSMauro Carvalho Chehab 	unsigned char parity;
674cb7a01acSMauro Carvalho Chehab 	int wss = 0;
675cb7a01acSMauro Carvalho Chehab 	int i;
676cb7a01acSMauro Carvalho Chehab 
677cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < 16; i++) {
678cb7a01acSMauro Carvalho Chehab 		int b1 = wss_bits[p[i] & 7];
679cb7a01acSMauro Carvalho Chehab 		int b2 = wss_bits[(p[i] >> 3) & 7];
680cb7a01acSMauro Carvalho Chehab 
681cb7a01acSMauro Carvalho Chehab 		if (b1 == b2)
682cb7a01acSMauro Carvalho Chehab 			return -1;
683cb7a01acSMauro Carvalho Chehab 		wss |= b2 << i;
684cb7a01acSMauro Carvalho Chehab 	}
685cb7a01acSMauro Carvalho Chehab 	parity = wss & 15;
686cb7a01acSMauro Carvalho Chehab 	parity ^= parity >> 2;
687cb7a01acSMauro Carvalho Chehab 	parity ^= parity >> 1;
688cb7a01acSMauro Carvalho Chehab 
689cb7a01acSMauro Carvalho Chehab 	if (!(parity & 1))
690cb7a01acSMauro Carvalho Chehab 		return -1;
691cb7a01acSMauro Carvalho Chehab 
692cb7a01acSMauro Carvalho Chehab 	return wss;
693cb7a01acSMauro Carvalho Chehab }
694cb7a01acSMauro Carvalho Chehab 
695cb7a01acSMauro Carvalho Chehab static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
696cb7a01acSMauro Carvalho Chehab {
697cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
698cb7a01acSMauro Carvalho Chehab 	u32 acpf;
699cb7a01acSMauro Carvalho Chehab 	u32 acni;
700cb7a01acSMauro Carvalho Chehab 	u32 hz;
701cb7a01acSMauro Carvalho Chehab 	u64 f;
702cb7a01acSMauro Carvalho Chehab 	u8 acc = 0; 	/* reg 0x3a, audio clock control */
703cb7a01acSMauro Carvalho Chehab 
704cb7a01acSMauro Carvalho Chehab 	/* Checks for chips that don't have audio clock (saa7111, saa7113) */
705cb7a01acSMauro Carvalho Chehab 	if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
706cb7a01acSMauro Carvalho Chehab 		return 0;
707cb7a01acSMauro Carvalho Chehab 
708cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
709cb7a01acSMauro Carvalho Chehab 
710cb7a01acSMauro Carvalho Chehab 	/* sanity check */
711cb7a01acSMauro Carvalho Chehab 	if (freq < 32000 || freq > 48000)
712cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
713cb7a01acSMauro Carvalho Chehab 
714cb7a01acSMauro Carvalho Chehab 	/* hz is the refresh rate times 100 */
715cb7a01acSMauro Carvalho Chehab 	hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000;
716cb7a01acSMauro Carvalho Chehab 	/* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */
717cb7a01acSMauro Carvalho Chehab 	acpf = (25600 * freq) / hz;
718cb7a01acSMauro Carvalho Chehab 	/* acni = (256 * freq * 2^23) / crystal_frequency =
719cb7a01acSMauro Carvalho Chehab 		  (freq * 2^(8+23)) / crystal_frequency =
720cb7a01acSMauro Carvalho Chehab 		  (freq << 31) / crystal_frequency */
721cb7a01acSMauro Carvalho Chehab 	f = freq;
722cb7a01acSMauro Carvalho Chehab 	f = f << 31;
723cb7a01acSMauro Carvalho Chehab 	do_div(f, state->crystal_freq);
724cb7a01acSMauro Carvalho Chehab 	acni = f;
725cb7a01acSMauro Carvalho Chehab 	if (state->ucgc) {
726cb7a01acSMauro Carvalho Chehab 		acpf = acpf * state->cgcdiv / 16;
727cb7a01acSMauro Carvalho Chehab 		acni = acni * state->cgcdiv / 16;
728cb7a01acSMauro Carvalho Chehab 		acc = 0x80;
729cb7a01acSMauro Carvalho Chehab 		if (state->cgcdiv == 3)
730cb7a01acSMauro Carvalho Chehab 			acc |= 0x40;
731cb7a01acSMauro Carvalho Chehab 	}
732cb7a01acSMauro Carvalho Chehab 	if (state->apll)
733cb7a01acSMauro Carvalho Chehab 		acc |= 0x08;
734cb7a01acSMauro Carvalho Chehab 
735cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
736cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
737cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
738cb7a01acSMauro Carvalho Chehab 
739cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
740cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
741cb7a01acSMauro Carvalho Chehab 							(acpf >> 8) & 0xff);
742cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
743cb7a01acSMauro Carvalho Chehab 							(acpf >> 16) & 0x03);
744cb7a01acSMauro Carvalho Chehab 
745cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
746cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
747cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
748cb7a01acSMauro Carvalho Chehab 	state->audclk_freq = freq;
749cb7a01acSMauro Carvalho Chehab 	return 0;
750cb7a01acSMauro Carvalho Chehab }
751cb7a01acSMauro Carvalho Chehab 
752cb7a01acSMauro Carvalho Chehab static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
753cb7a01acSMauro Carvalho Chehab {
754cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
755cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
756cb7a01acSMauro Carvalho Chehab 
757cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
758cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CHROMA_AGC:
759cb7a01acSMauro Carvalho Chehab 		/* chroma gain cluster */
760cb7a01acSMauro Carvalho Chehab 		if (state->agc->val)
761cb7a01acSMauro Carvalho Chehab 			state->gain->val =
762cb7a01acSMauro Carvalho Chehab 				saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
763cb7a01acSMauro Carvalho Chehab 		break;
764cb7a01acSMauro Carvalho Chehab 	}
765cb7a01acSMauro Carvalho Chehab 	return 0;
766cb7a01acSMauro Carvalho Chehab }
767cb7a01acSMauro Carvalho Chehab 
768cb7a01acSMauro Carvalho Chehab static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
769cb7a01acSMauro Carvalho Chehab {
770cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
771cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
772cb7a01acSMauro Carvalho Chehab 
773cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
774cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
775cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
776cb7a01acSMauro Carvalho Chehab 		break;
777cb7a01acSMauro Carvalho Chehab 
778cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
779cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
780cb7a01acSMauro Carvalho Chehab 		break;
781cb7a01acSMauro Carvalho Chehab 
782cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
783cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
784cb7a01acSMauro Carvalho Chehab 		break;
785cb7a01acSMauro Carvalho Chehab 
786cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
787cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
788cb7a01acSMauro Carvalho Chehab 		break;
789cb7a01acSMauro Carvalho Chehab 
790cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CHROMA_AGC:
791cb7a01acSMauro Carvalho Chehab 		/* chroma gain cluster */
792cb7a01acSMauro Carvalho Chehab 		if (state->agc->val)
793cb7a01acSMauro Carvalho Chehab 			saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
794cb7a01acSMauro Carvalho Chehab 		else
795cb7a01acSMauro Carvalho Chehab 			saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
796cb7a01acSMauro Carvalho Chehab 		break;
797cb7a01acSMauro Carvalho Chehab 
798cb7a01acSMauro Carvalho Chehab 	default:
799cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
800cb7a01acSMauro Carvalho Chehab 	}
801cb7a01acSMauro Carvalho Chehab 
802cb7a01acSMauro Carvalho Chehab 	return 0;
803cb7a01acSMauro Carvalho Chehab }
804cb7a01acSMauro Carvalho Chehab 
805cb7a01acSMauro Carvalho Chehab static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
806cb7a01acSMauro Carvalho Chehab {
807cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
808cb7a01acSMauro Carvalho Chehab 	int HPSC, HFSC;
809cb7a01acSMauro Carvalho Chehab 	int VSCY;
810cb7a01acSMauro Carvalho Chehab 	int res;
811cb7a01acSMauro Carvalho Chehab 	int is_50hz = state->std & V4L2_STD_625_50;
812cb7a01acSMauro Carvalho Chehab 	int Vsrc = is_50hz ? 576 : 480;
813cb7a01acSMauro Carvalho Chehab 
814cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
815cb7a01acSMauro Carvalho Chehab 
816cb7a01acSMauro Carvalho Chehab 	/* FIXME need better bounds checking here */
817cb7a01acSMauro Carvalho Chehab 	if ((width < 1) || (width > 1440))
818cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
819cb7a01acSMauro Carvalho Chehab 	if ((height < 1) || (height > Vsrc))
820cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
821cb7a01acSMauro Carvalho Chehab 
822cb7a01acSMauro Carvalho Chehab 	if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
823cb7a01acSMauro Carvalho Chehab 		/* Decoder only supports 720 columns and 480 or 576 lines */
824cb7a01acSMauro Carvalho Chehab 		if (width != 720)
825cb7a01acSMauro Carvalho Chehab 			return -EINVAL;
826cb7a01acSMauro Carvalho Chehab 		if (height != Vsrc)
827cb7a01acSMauro Carvalho Chehab 			return -EINVAL;
828cb7a01acSMauro Carvalho Chehab 	}
829cb7a01acSMauro Carvalho Chehab 
830cb7a01acSMauro Carvalho Chehab 	state->width = width;
831cb7a01acSMauro Carvalho Chehab 	state->height = height;
832cb7a01acSMauro Carvalho Chehab 
833cb7a01acSMauro Carvalho Chehab 	if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH))
834cb7a01acSMauro Carvalho Chehab 		return 0;
835cb7a01acSMauro Carvalho Chehab 
836cb7a01acSMauro Carvalho Chehab 	/* probably have a valid size, let's set it */
837cb7a01acSMauro Carvalho Chehab 	/* Set output width/height */
838cb7a01acSMauro Carvalho Chehab 	/* width */
839cb7a01acSMauro Carvalho Chehab 
840cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
841cb7a01acSMauro Carvalho Chehab 					(u8) (width & 0xff));
842cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
843cb7a01acSMauro Carvalho Chehab 					(u8) ((width >> 8) & 0xff));
844cb7a01acSMauro Carvalho Chehab 
845cb7a01acSMauro Carvalho Chehab 	/* Vertical Scaling uses height/2 */
846cb7a01acSMauro Carvalho Chehab 	res = height / 2;
847cb7a01acSMauro Carvalho Chehab 
848cb7a01acSMauro Carvalho Chehab 	/* On 60Hz, it is using a higher Vertical Output Size */
849cb7a01acSMauro Carvalho Chehab 	if (!is_50hz)
850cb7a01acSMauro Carvalho Chehab 		res += (VRES_60HZ - 480) >> 1;
851cb7a01acSMauro Carvalho Chehab 
852cb7a01acSMauro Carvalho Chehab 		/* height */
853cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
854cb7a01acSMauro Carvalho Chehab 					(u8) (res & 0xff));
855cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
856cb7a01acSMauro Carvalho Chehab 					(u8) ((res >> 8) & 0xff));
857cb7a01acSMauro Carvalho Chehab 
858cb7a01acSMauro Carvalho Chehab 	/* Scaling settings */
859cb7a01acSMauro Carvalho Chehab 	/* Hprescaler is floor(inres/outres) */
860cb7a01acSMauro Carvalho Chehab 	HPSC = (int)(720 / width);
861cb7a01acSMauro Carvalho Chehab 	/* 0 is not allowed (div. by zero) */
862cb7a01acSMauro Carvalho Chehab 	HPSC = HPSC ? HPSC : 1;
863cb7a01acSMauro Carvalho Chehab 	HFSC = (int)((1024 * 720) / (HPSC * width));
864cb7a01acSMauro Carvalho Chehab 	/* FIXME hardcodes to "Task B"
865cb7a01acSMauro Carvalho Chehab 	 * write H prescaler integer */
866cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
867cb7a01acSMauro Carvalho Chehab 				(u8) (HPSC & 0x3f));
868cb7a01acSMauro Carvalho Chehab 
869cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
870cb7a01acSMauro Carvalho Chehab 	/* write H fine-scaling (luminance) */
871cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
872cb7a01acSMauro Carvalho Chehab 				(u8) (HFSC & 0xff));
873cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
874cb7a01acSMauro Carvalho Chehab 				(u8) ((HFSC >> 8) & 0xff));
875cb7a01acSMauro Carvalho Chehab 	/* write H fine-scaling (chrominance)
876cb7a01acSMauro Carvalho Chehab 	 * must be lum/2, so i'll just bitshift :) */
877cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
878cb7a01acSMauro Carvalho Chehab 				(u8) ((HFSC >> 1) & 0xff));
879cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
880cb7a01acSMauro Carvalho Chehab 				(u8) ((HFSC >> 9) & 0xff));
881cb7a01acSMauro Carvalho Chehab 
882cb7a01acSMauro Carvalho Chehab 	VSCY = (int)((1024 * Vsrc) / height);
883cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
884cb7a01acSMauro Carvalho Chehab 
885cb7a01acSMauro Carvalho Chehab 	/* Correct Contrast and Luminance */
886cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
887cb7a01acSMauro Carvalho Chehab 					(u8) (64 * 1024 / VSCY));
888cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
889cb7a01acSMauro Carvalho Chehab 					(u8) (64 * 1024 / VSCY));
890cb7a01acSMauro Carvalho Chehab 
891cb7a01acSMauro Carvalho Chehab 		/* write V fine-scaling (luminance) */
892cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
893cb7a01acSMauro Carvalho Chehab 					(u8) (VSCY & 0xff));
894cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
895cb7a01acSMauro Carvalho Chehab 					(u8) ((VSCY >> 8) & 0xff));
896cb7a01acSMauro Carvalho Chehab 		/* write V fine-scaling (chrominance) */
897cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
898cb7a01acSMauro Carvalho Chehab 					(u8) (VSCY & 0xff));
899cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
900cb7a01acSMauro Carvalho Chehab 					(u8) ((VSCY >> 8) & 0xff));
901cb7a01acSMauro Carvalho Chehab 
902cb7a01acSMauro Carvalho Chehab 	saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
903cb7a01acSMauro Carvalho Chehab 
904cb7a01acSMauro Carvalho Chehab 	/* Activates task "B" */
905cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_80_GLOBAL_CNTL_1,
906cb7a01acSMauro Carvalho Chehab 				saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
907cb7a01acSMauro Carvalho Chehab 
908cb7a01acSMauro Carvalho Chehab 	return 0;
909cb7a01acSMauro Carvalho Chehab }
910cb7a01acSMauro Carvalho Chehab 
911cb7a01acSMauro Carvalho Chehab static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
912cb7a01acSMauro Carvalho Chehab {
913cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
914cb7a01acSMauro Carvalho Chehab 
915cb7a01acSMauro Carvalho Chehab 	/* Prevent unnecessary standard changes. During a standard
916cb7a01acSMauro Carvalho Chehab 	   change the I-Port is temporarily disabled. Any devices
917cb7a01acSMauro Carvalho Chehab 	   reading from that port can get confused.
918cb7a01acSMauro Carvalho Chehab 	   Note that s_std is also used to switch from
919cb7a01acSMauro Carvalho Chehab 	   radio to TV mode, so if a s_std is broadcast to
920cb7a01acSMauro Carvalho Chehab 	   all I2C devices then you do not want to have an unwanted
921cb7a01acSMauro Carvalho Chehab 	   side-effect here. */
922cb7a01acSMauro Carvalho Chehab 	if (std == state->std)
923cb7a01acSMauro Carvalho Chehab 		return;
924cb7a01acSMauro Carvalho Chehab 
925cb7a01acSMauro Carvalho Chehab 	state->std = std;
926cb7a01acSMauro Carvalho Chehab 
927cb7a01acSMauro Carvalho Chehab 	// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
928cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_525_60) {
929cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
930cb7a01acSMauro Carvalho Chehab 		saa711x_writeregs(sd, saa7115_cfg_60hz_video);
931cb7a01acSMauro Carvalho Chehab 		saa711x_set_size(sd, 720, 480);
932cb7a01acSMauro Carvalho Chehab 	} else {
933cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
934cb7a01acSMauro Carvalho Chehab 		saa711x_writeregs(sd, saa7115_cfg_50hz_video);
935cb7a01acSMauro Carvalho Chehab 		saa711x_set_size(sd, 720, 576);
936cb7a01acSMauro Carvalho Chehab 	}
937cb7a01acSMauro Carvalho Chehab 
938cb7a01acSMauro Carvalho Chehab 	/* Register 0E - Bits D6-D4 on NO-AUTO mode
939cb7a01acSMauro Carvalho Chehab 		(SAA7111 and SAA7113 doesn't have auto mode)
940cb7a01acSMauro Carvalho Chehab 	    50 Hz / 625 lines           60 Hz / 525 lines
941cb7a01acSMauro Carvalho Chehab 	000 PAL BGDHI (4.43Mhz)         NTSC M (3.58MHz)
942cb7a01acSMauro Carvalho Chehab 	001 NTSC 4.43 (50 Hz)           PAL 4.43 (60 Hz)
943cb7a01acSMauro Carvalho Chehab 	010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz)
944cb7a01acSMauro Carvalho Chehab 	011 NTSC N (3.58MHz)            PAL M (3.58MHz)
945cb7a01acSMauro Carvalho Chehab 	100 reserved                    NTSC-Japan (3.58MHz)
946cb7a01acSMauro Carvalho Chehab 	*/
947cb7a01acSMauro Carvalho Chehab 	if (state->ident <= V4L2_IDENT_SAA7113) {
948cb7a01acSMauro Carvalho Chehab 		u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
949cb7a01acSMauro Carvalho Chehab 
950cb7a01acSMauro Carvalho Chehab 		if (std == V4L2_STD_PAL_M) {
951cb7a01acSMauro Carvalho Chehab 			reg |= 0x30;
952cb7a01acSMauro Carvalho Chehab 		} else if (std == V4L2_STD_PAL_Nc) {
953cb7a01acSMauro Carvalho Chehab 			reg |= 0x20;
954cb7a01acSMauro Carvalho Chehab 		} else if (std == V4L2_STD_PAL_60) {
955cb7a01acSMauro Carvalho Chehab 			reg |= 0x10;
956cb7a01acSMauro Carvalho Chehab 		} else if (std == V4L2_STD_NTSC_M_JP) {
957cb7a01acSMauro Carvalho Chehab 			reg |= 0x40;
958cb7a01acSMauro Carvalho Chehab 		} else if (std & V4L2_STD_SECAM) {
959cb7a01acSMauro Carvalho Chehab 			reg |= 0x50;
960cb7a01acSMauro Carvalho Chehab 		}
961cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
962cb7a01acSMauro Carvalho Chehab 	} else {
963cb7a01acSMauro Carvalho Chehab 		/* restart task B if needed */
964cb7a01acSMauro Carvalho Chehab 		int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
965cb7a01acSMauro Carvalho Chehab 
966cb7a01acSMauro Carvalho Chehab 		if (taskb && state->ident == V4L2_IDENT_SAA7114) {
967cb7a01acSMauro Carvalho Chehab 			saa711x_writeregs(sd, saa7115_cfg_vbi_on);
968cb7a01acSMauro Carvalho Chehab 		}
969cb7a01acSMauro Carvalho Chehab 
970cb7a01acSMauro Carvalho Chehab 		/* switch audio mode too! */
971cb7a01acSMauro Carvalho Chehab 		saa711x_s_clock_freq(sd, state->audclk_freq);
972cb7a01acSMauro Carvalho Chehab 	}
973cb7a01acSMauro Carvalho Chehab }
974cb7a01acSMauro Carvalho Chehab 
975cb7a01acSMauro Carvalho Chehab /* setup the sliced VBI lcr registers according to the sliced VBI format */
976cb7a01acSMauro Carvalho Chehab static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
977cb7a01acSMauro Carvalho Chehab {
978cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
979cb7a01acSMauro Carvalho Chehab 	int is_50hz = (state->std & V4L2_STD_625_50);
980cb7a01acSMauro Carvalho Chehab 	u8 lcr[24];
981cb7a01acSMauro Carvalho Chehab 	int i, x;
982cb7a01acSMauro Carvalho Chehab 
983cb7a01acSMauro Carvalho Chehab #if 1
984cb7a01acSMauro Carvalho Chehab 	/* saa7113/7114/7118 VBI support are experimental */
985cb7a01acSMauro Carvalho Chehab 	if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
986cb7a01acSMauro Carvalho Chehab 		return;
987cb7a01acSMauro Carvalho Chehab 
988cb7a01acSMauro Carvalho Chehab #else
989cb7a01acSMauro Carvalho Chehab 	/* SAA7113 and SAA7118 also should support VBI - Need testing */
990cb7a01acSMauro Carvalho Chehab 	if (state->ident != V4L2_IDENT_SAA7115)
991cb7a01acSMauro Carvalho Chehab 		return;
992cb7a01acSMauro Carvalho Chehab #endif
993cb7a01acSMauro Carvalho Chehab 
994cb7a01acSMauro Carvalho Chehab 	for (i = 0; i <= 23; i++)
995cb7a01acSMauro Carvalho Chehab 		lcr[i] = 0xff;
996cb7a01acSMauro Carvalho Chehab 
997cb7a01acSMauro Carvalho Chehab 	if (fmt == NULL) {
998cb7a01acSMauro Carvalho Chehab 		/* raw VBI */
999cb7a01acSMauro Carvalho Chehab 		if (is_50hz)
1000cb7a01acSMauro Carvalho Chehab 			for (i = 6; i <= 23; i++)
1001cb7a01acSMauro Carvalho Chehab 				lcr[i] = 0xdd;
1002cb7a01acSMauro Carvalho Chehab 		else
1003cb7a01acSMauro Carvalho Chehab 			for (i = 10; i <= 21; i++)
1004cb7a01acSMauro Carvalho Chehab 				lcr[i] = 0xdd;
1005cb7a01acSMauro Carvalho Chehab 	} else {
1006cb7a01acSMauro Carvalho Chehab 		/* sliced VBI */
1007cb7a01acSMauro Carvalho Chehab 		/* first clear lines that cannot be captured */
1008cb7a01acSMauro Carvalho Chehab 		if (is_50hz) {
1009cb7a01acSMauro Carvalho Chehab 			for (i = 0; i <= 5; i++)
1010cb7a01acSMauro Carvalho Chehab 				fmt->service_lines[0][i] =
1011cb7a01acSMauro Carvalho Chehab 					fmt->service_lines[1][i] = 0;
1012cb7a01acSMauro Carvalho Chehab 		}
1013cb7a01acSMauro Carvalho Chehab 		else {
1014cb7a01acSMauro Carvalho Chehab 			for (i = 0; i <= 9; i++)
1015cb7a01acSMauro Carvalho Chehab 				fmt->service_lines[0][i] =
1016cb7a01acSMauro Carvalho Chehab 					fmt->service_lines[1][i] = 0;
1017cb7a01acSMauro Carvalho Chehab 			for (i = 22; i <= 23; i++)
1018cb7a01acSMauro Carvalho Chehab 				fmt->service_lines[0][i] =
1019cb7a01acSMauro Carvalho Chehab 					fmt->service_lines[1][i] = 0;
1020cb7a01acSMauro Carvalho Chehab 		}
1021cb7a01acSMauro Carvalho Chehab 
1022cb7a01acSMauro Carvalho Chehab 		/* Now set the lcr values according to the specified service */
1023cb7a01acSMauro Carvalho Chehab 		for (i = 6; i <= 23; i++) {
1024cb7a01acSMauro Carvalho Chehab 			lcr[i] = 0;
1025cb7a01acSMauro Carvalho Chehab 			for (x = 0; x <= 1; x++) {
1026cb7a01acSMauro Carvalho Chehab 				switch (fmt->service_lines[1-x][i]) {
1027cb7a01acSMauro Carvalho Chehab 					case 0:
1028cb7a01acSMauro Carvalho Chehab 						lcr[i] |= 0xf << (4 * x);
1029cb7a01acSMauro Carvalho Chehab 						break;
1030cb7a01acSMauro Carvalho Chehab 					case V4L2_SLICED_TELETEXT_B:
1031cb7a01acSMauro Carvalho Chehab 						lcr[i] |= 1 << (4 * x);
1032cb7a01acSMauro Carvalho Chehab 						break;
1033cb7a01acSMauro Carvalho Chehab 					case V4L2_SLICED_CAPTION_525:
1034cb7a01acSMauro Carvalho Chehab 						lcr[i] |= 4 << (4 * x);
1035cb7a01acSMauro Carvalho Chehab 						break;
1036cb7a01acSMauro Carvalho Chehab 					case V4L2_SLICED_WSS_625:
1037cb7a01acSMauro Carvalho Chehab 						lcr[i] |= 5 << (4 * x);
1038cb7a01acSMauro Carvalho Chehab 						break;
1039cb7a01acSMauro Carvalho Chehab 					case V4L2_SLICED_VPS:
1040cb7a01acSMauro Carvalho Chehab 						lcr[i] |= 7 << (4 * x);
1041cb7a01acSMauro Carvalho Chehab 						break;
1042cb7a01acSMauro Carvalho Chehab 				}
1043cb7a01acSMauro Carvalho Chehab 			}
1044cb7a01acSMauro Carvalho Chehab 		}
1045cb7a01acSMauro Carvalho Chehab 	}
1046cb7a01acSMauro Carvalho Chehab 
1047cb7a01acSMauro Carvalho Chehab 	/* write the lcr registers */
1048cb7a01acSMauro Carvalho Chehab 	for (i = 2; i <= 23; i++) {
1049cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
1050cb7a01acSMauro Carvalho Chehab 	}
1051cb7a01acSMauro Carvalho Chehab 
1052cb7a01acSMauro Carvalho Chehab 	/* enable/disable raw VBI capturing */
1053cb7a01acSMauro Carvalho Chehab 	saa711x_writeregs(sd, fmt == NULL ?
1054cb7a01acSMauro Carvalho Chehab 				saa7115_cfg_vbi_on :
1055cb7a01acSMauro Carvalho Chehab 				saa7115_cfg_vbi_off);
1056cb7a01acSMauro Carvalho Chehab }
1057cb7a01acSMauro Carvalho Chehab 
1058cb7a01acSMauro Carvalho Chehab static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced)
1059cb7a01acSMauro Carvalho Chehab {
1060cb7a01acSMauro Carvalho Chehab 	static u16 lcr2vbi[] = {
1061cb7a01acSMauro Carvalho Chehab 		0, V4L2_SLICED_TELETEXT_B, 0,	/* 1 */
1062cb7a01acSMauro Carvalho Chehab 		0, V4L2_SLICED_CAPTION_525,	/* 4 */
1063cb7a01acSMauro Carvalho Chehab 		V4L2_SLICED_WSS_625, 0,		/* 5 */
1064cb7a01acSMauro Carvalho Chehab 		V4L2_SLICED_VPS, 0, 0, 0, 0,	/* 7 */
1065cb7a01acSMauro Carvalho Chehab 		0, 0, 0, 0
1066cb7a01acSMauro Carvalho Chehab 	};
1067cb7a01acSMauro Carvalho Chehab 	int i;
1068cb7a01acSMauro Carvalho Chehab 
106930634e8eSHans Verkuil 	memset(sliced->service_lines, 0, sizeof(sliced->service_lines));
107030634e8eSHans Verkuil 	sliced->service_set = 0;
1071cb7a01acSMauro Carvalho Chehab 	/* done if using raw VBI */
1072cb7a01acSMauro Carvalho Chehab 	if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
1073cb7a01acSMauro Carvalho Chehab 		return 0;
1074cb7a01acSMauro Carvalho Chehab 	for (i = 2; i <= 23; i++) {
1075cb7a01acSMauro Carvalho Chehab 		u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
1076cb7a01acSMauro Carvalho Chehab 
1077cb7a01acSMauro Carvalho Chehab 		sliced->service_lines[0][i] = lcr2vbi[v >> 4];
1078cb7a01acSMauro Carvalho Chehab 		sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
1079cb7a01acSMauro Carvalho Chehab 		sliced->service_set |=
1080cb7a01acSMauro Carvalho Chehab 			sliced->service_lines[0][i] | sliced->service_lines[1][i];
1081cb7a01acSMauro Carvalho Chehab 	}
1082cb7a01acSMauro Carvalho Chehab 	return 0;
1083cb7a01acSMauro Carvalho Chehab }
1084cb7a01acSMauro Carvalho Chehab 
1085cb7a01acSMauro Carvalho Chehab static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
1086cb7a01acSMauro Carvalho Chehab {
1087cb7a01acSMauro Carvalho Chehab 	saa711x_set_lcr(sd, NULL);
1088cb7a01acSMauro Carvalho Chehab 	return 0;
1089cb7a01acSMauro Carvalho Chehab }
1090cb7a01acSMauro Carvalho Chehab 
1091cb7a01acSMauro Carvalho Chehab static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
1092cb7a01acSMauro Carvalho Chehab {
1093cb7a01acSMauro Carvalho Chehab 	saa711x_set_lcr(sd, fmt);
1094cb7a01acSMauro Carvalho Chehab 	return 0;
1095cb7a01acSMauro Carvalho Chehab }
1096cb7a01acSMauro Carvalho Chehab 
1097cb7a01acSMauro Carvalho Chehab static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
1098cb7a01acSMauro Carvalho Chehab {
1099cb7a01acSMauro Carvalho Chehab 	if (fmt->code != V4L2_MBUS_FMT_FIXED)
1100cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1101cb7a01acSMauro Carvalho Chehab 	fmt->field = V4L2_FIELD_INTERLACED;
1102cb7a01acSMauro Carvalho Chehab 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
1103cb7a01acSMauro Carvalho Chehab 	return saa711x_set_size(sd, fmt->width, fmt->height);
1104cb7a01acSMauro Carvalho Chehab }
1105cb7a01acSMauro Carvalho Chehab 
1106cb7a01acSMauro Carvalho Chehab /* Decode the sliced VBI data stream as created by the saa7115.
1107cb7a01acSMauro Carvalho Chehab    The format is described in the saa7115 datasheet in Tables 25 and 26
1108cb7a01acSMauro Carvalho Chehab    and in Figure 33.
1109cb7a01acSMauro Carvalho Chehab    The current implementation uses SAV/EAV codes and not the ancillary data
1110cb7a01acSMauro Carvalho Chehab    headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
1111cb7a01acSMauro Carvalho Chehab    code. */
1112cb7a01acSMauro Carvalho Chehab static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
1113cb7a01acSMauro Carvalho Chehab {
1114cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1115cb7a01acSMauro Carvalho Chehab 	static const char vbi_no_data_pattern[] = {
1116cb7a01acSMauro Carvalho Chehab 		0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
1117cb7a01acSMauro Carvalho Chehab 	};
1118cb7a01acSMauro Carvalho Chehab 	u8 *p = vbi->p;
1119cb7a01acSMauro Carvalho Chehab 	u32 wss;
1120cb7a01acSMauro Carvalho Chehab 	int id1, id2;   /* the ID1 and ID2 bytes from the internal header */
1121cb7a01acSMauro Carvalho Chehab 
1122cb7a01acSMauro Carvalho Chehab 	vbi->type = 0;  /* mark result as a failure */
1123cb7a01acSMauro Carvalho Chehab 	id1 = p[2];
1124cb7a01acSMauro Carvalho Chehab 	id2 = p[3];
1125cb7a01acSMauro Carvalho Chehab 	/* Note: the field bit is inverted for 60 Hz video */
1126cb7a01acSMauro Carvalho Chehab 	if (state->std & V4L2_STD_525_60)
1127cb7a01acSMauro Carvalho Chehab 		id1 ^= 0x40;
1128cb7a01acSMauro Carvalho Chehab 
1129cb7a01acSMauro Carvalho Chehab 	/* Skip internal header, p now points to the start of the payload */
1130cb7a01acSMauro Carvalho Chehab 	p += 4;
1131cb7a01acSMauro Carvalho Chehab 	vbi->p = p;
1132cb7a01acSMauro Carvalho Chehab 
1133cb7a01acSMauro Carvalho Chehab 	/* calculate field and line number of the VBI packet (1-23) */
1134cb7a01acSMauro Carvalho Chehab 	vbi->is_second_field = ((id1 & 0x40) != 0);
1135cb7a01acSMauro Carvalho Chehab 	vbi->line = (id1 & 0x3f) << 3;
1136cb7a01acSMauro Carvalho Chehab 	vbi->line |= (id2 & 0x70) >> 4;
1137cb7a01acSMauro Carvalho Chehab 
1138cb7a01acSMauro Carvalho Chehab 	/* Obtain data type */
1139cb7a01acSMauro Carvalho Chehab 	id2 &= 0xf;
1140cb7a01acSMauro Carvalho Chehab 
1141cb7a01acSMauro Carvalho Chehab 	/* If the VBI slicer does not detect any signal it will fill up
1142cb7a01acSMauro Carvalho Chehab 	   the payload buffer with 0xa0 bytes. */
1143cb7a01acSMauro Carvalho Chehab 	if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
1144cb7a01acSMauro Carvalho Chehab 		return 0;
1145cb7a01acSMauro Carvalho Chehab 
1146cb7a01acSMauro Carvalho Chehab 	/* decode payloads */
1147cb7a01acSMauro Carvalho Chehab 	switch (id2) {
1148cb7a01acSMauro Carvalho Chehab 	case 1:
1149cb7a01acSMauro Carvalho Chehab 		vbi->type = V4L2_SLICED_TELETEXT_B;
1150cb7a01acSMauro Carvalho Chehab 		break;
1151cb7a01acSMauro Carvalho Chehab 	case 4:
1152cb7a01acSMauro Carvalho Chehab 		if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
1153cb7a01acSMauro Carvalho Chehab 			return 0;
1154cb7a01acSMauro Carvalho Chehab 		vbi->type = V4L2_SLICED_CAPTION_525;
1155cb7a01acSMauro Carvalho Chehab 		break;
1156cb7a01acSMauro Carvalho Chehab 	case 5:
1157cb7a01acSMauro Carvalho Chehab 		wss = saa711x_decode_wss(p);
1158cb7a01acSMauro Carvalho Chehab 		if (wss == -1)
1159cb7a01acSMauro Carvalho Chehab 			return 0;
1160cb7a01acSMauro Carvalho Chehab 		p[0] = wss & 0xff;
1161cb7a01acSMauro Carvalho Chehab 		p[1] = wss >> 8;
1162cb7a01acSMauro Carvalho Chehab 		vbi->type = V4L2_SLICED_WSS_625;
1163cb7a01acSMauro Carvalho Chehab 		break;
1164cb7a01acSMauro Carvalho Chehab 	case 7:
1165cb7a01acSMauro Carvalho Chehab 		if (saa711x_decode_vps(p, p) != 0)
1166cb7a01acSMauro Carvalho Chehab 			return 0;
1167cb7a01acSMauro Carvalho Chehab 		vbi->type = V4L2_SLICED_VPS;
1168cb7a01acSMauro Carvalho Chehab 		break;
1169cb7a01acSMauro Carvalho Chehab 	default:
1170cb7a01acSMauro Carvalho Chehab 		break;
1171cb7a01acSMauro Carvalho Chehab 	}
1172cb7a01acSMauro Carvalho Chehab 	return 0;
1173cb7a01acSMauro Carvalho Chehab }
1174cb7a01acSMauro Carvalho Chehab 
1175cb7a01acSMauro Carvalho Chehab /* ============ SAA7115 AUDIO settings (end) ============= */
1176cb7a01acSMauro Carvalho Chehab 
1177cb7a01acSMauro Carvalho Chehab static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
1178cb7a01acSMauro Carvalho Chehab {
1179cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1180cb7a01acSMauro Carvalho Chehab 	int status;
1181cb7a01acSMauro Carvalho Chehab 
1182cb7a01acSMauro Carvalho Chehab 	if (state->radio)
1183cb7a01acSMauro Carvalho Chehab 		return 0;
1184cb7a01acSMauro Carvalho Chehab 	status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
1185cb7a01acSMauro Carvalho Chehab 
1186cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
1187cb7a01acSMauro Carvalho Chehab 	vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
1188cb7a01acSMauro Carvalho Chehab 	return 0;
1189cb7a01acSMauro Carvalho Chehab }
1190cb7a01acSMauro Carvalho Chehab 
1191cb7a01acSMauro Carvalho Chehab static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
1192cb7a01acSMauro Carvalho Chehab {
1193cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1194cb7a01acSMauro Carvalho Chehab 
1195cb7a01acSMauro Carvalho Chehab 	state->radio = 0;
1196cb7a01acSMauro Carvalho Chehab 	saa711x_set_v4lstd(sd, std);
1197cb7a01acSMauro Carvalho Chehab 	return 0;
1198cb7a01acSMauro Carvalho Chehab }
1199cb7a01acSMauro Carvalho Chehab 
1200cb7a01acSMauro Carvalho Chehab static int saa711x_s_radio(struct v4l2_subdev *sd)
1201cb7a01acSMauro Carvalho Chehab {
1202cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1203cb7a01acSMauro Carvalho Chehab 
1204cb7a01acSMauro Carvalho Chehab 	state->radio = 1;
1205cb7a01acSMauro Carvalho Chehab 	return 0;
1206cb7a01acSMauro Carvalho Chehab }
1207cb7a01acSMauro Carvalho Chehab 
1208cb7a01acSMauro Carvalho Chehab static int saa711x_s_routing(struct v4l2_subdev *sd,
1209cb7a01acSMauro Carvalho Chehab 			     u32 input, u32 output, u32 config)
1210cb7a01acSMauro Carvalho Chehab {
1211cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1212cb7a01acSMauro Carvalho Chehab 	u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
1213cb7a01acSMauro Carvalho Chehab 
1214cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
1215cb7a01acSMauro Carvalho Chehab 		input, output);
1216cb7a01acSMauro Carvalho Chehab 
1217cb7a01acSMauro Carvalho Chehab 	/* saa7111/3 does not have these inputs */
1218cb7a01acSMauro Carvalho Chehab 	if (state->ident <= V4L2_IDENT_SAA7113 &&
1219cb7a01acSMauro Carvalho Chehab 	    (input == SAA7115_COMPOSITE4 ||
1220cb7a01acSMauro Carvalho Chehab 	     input == SAA7115_COMPOSITE5)) {
1221cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1222cb7a01acSMauro Carvalho Chehab 	}
1223cb7a01acSMauro Carvalho Chehab 	if (input > SAA7115_SVIDEO3)
1224cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1225cb7a01acSMauro Carvalho Chehab 	if (state->input == input && state->output == output)
1226cb7a01acSMauro Carvalho Chehab 		return 0;
1227cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
1228cb7a01acSMauro Carvalho Chehab 		(input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
1229cb7a01acSMauro Carvalho Chehab 		(output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
1230cb7a01acSMauro Carvalho Chehab 	state->input = input;
1231cb7a01acSMauro Carvalho Chehab 
1232cb7a01acSMauro Carvalho Chehab 	/* saa7111 has slightly different input numbering */
1233cb7a01acSMauro Carvalho Chehab 	if (state->ident <= V4L2_IDENT_SAA7111A) {
1234cb7a01acSMauro Carvalho Chehab 		if (input >= SAA7115_COMPOSITE4)
1235cb7a01acSMauro Carvalho Chehab 			input -= 2;
1236cb7a01acSMauro Carvalho Chehab 		/* saa7111 specific */
1237cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_10_CHROMA_CNTL_2,
1238cb7a01acSMauro Carvalho Chehab 				(saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
1239cb7a01acSMauro Carvalho Chehab 				((output & 0xc0) ^ 0x40));
1240cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
1241cb7a01acSMauro Carvalho Chehab 				(saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
1242cb7a01acSMauro Carvalho Chehab 				((output & 2) ? 0x0a : 0));
1243cb7a01acSMauro Carvalho Chehab 	}
1244cb7a01acSMauro Carvalho Chehab 
1245cb7a01acSMauro Carvalho Chehab 	/* select mode */
1246cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_02_INPUT_CNTL_1,
1247cb7a01acSMauro Carvalho Chehab 		      (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
1248cb7a01acSMauro Carvalho Chehab 		       input);
1249cb7a01acSMauro Carvalho Chehab 
1250cb7a01acSMauro Carvalho Chehab 	/* bypass chrominance trap for S-Video modes */
1251cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_09_LUMA_CNTL,
1252cb7a01acSMauro Carvalho Chehab 			(saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
1253cb7a01acSMauro Carvalho Chehab 			(state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
1254cb7a01acSMauro Carvalho Chehab 
1255cb7a01acSMauro Carvalho Chehab 	state->output = output;
1256cb7a01acSMauro Carvalho Chehab 	if (state->ident == V4L2_IDENT_SAA7114 ||
1257cb7a01acSMauro Carvalho Chehab 			state->ident == V4L2_IDENT_SAA7115) {
1258cb7a01acSMauro Carvalho Chehab 		saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
1259cb7a01acSMauro Carvalho Chehab 				(saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
1260cb7a01acSMauro Carvalho Chehab 				(state->output & 0x01));
1261cb7a01acSMauro Carvalho Chehab 	}
1262cb7a01acSMauro Carvalho Chehab 	return 0;
1263cb7a01acSMauro Carvalho Chehab }
1264cb7a01acSMauro Carvalho Chehab 
1265cb7a01acSMauro Carvalho Chehab static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
1266cb7a01acSMauro Carvalho Chehab {
1267cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1268cb7a01acSMauro Carvalho Chehab 
1269cb7a01acSMauro Carvalho Chehab 	if (state->ident > V4L2_IDENT_SAA7111A)
1270cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1271cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
1272cb7a01acSMauro Carvalho Chehab 		(val ? 0x80 : 0));
1273cb7a01acSMauro Carvalho Chehab 	return 0;
1274cb7a01acSMauro Carvalho Chehab }
1275cb7a01acSMauro Carvalho Chehab 
1276cb7a01acSMauro Carvalho Chehab static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
1277cb7a01acSMauro Carvalho Chehab {
1278cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1279cb7a01acSMauro Carvalho Chehab 
1280cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "%s output\n",
1281cb7a01acSMauro Carvalho Chehab 			enable ? "enable" : "disable");
1282cb7a01acSMauro Carvalho Chehab 
1283cb7a01acSMauro Carvalho Chehab 	if (state->enable == enable)
1284cb7a01acSMauro Carvalho Chehab 		return 0;
1285cb7a01acSMauro Carvalho Chehab 	state->enable = enable;
1286cb7a01acSMauro Carvalho Chehab 	if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED))
1287cb7a01acSMauro Carvalho Chehab 		return 0;
1288cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable);
1289cb7a01acSMauro Carvalho Chehab 	return 0;
1290cb7a01acSMauro Carvalho Chehab }
1291cb7a01acSMauro Carvalho Chehab 
1292cb7a01acSMauro Carvalho Chehab static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
1293cb7a01acSMauro Carvalho Chehab {
1294cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1295cb7a01acSMauro Carvalho Chehab 
1296cb7a01acSMauro Carvalho Chehab 	if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)
1297cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1298cb7a01acSMauro Carvalho Chehab 	state->crystal_freq = freq;
1299cb7a01acSMauro Carvalho Chehab 	state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
1300cb7a01acSMauro Carvalho Chehab 	state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
1301cb7a01acSMauro Carvalho Chehab 	state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
1302cb7a01acSMauro Carvalho Chehab 	saa711x_s_clock_freq(sd, state->audclk_freq);
1303cb7a01acSMauro Carvalho Chehab 	return 0;
1304cb7a01acSMauro Carvalho Chehab }
1305cb7a01acSMauro Carvalho Chehab 
1306cb7a01acSMauro Carvalho Chehab static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
1307cb7a01acSMauro Carvalho Chehab {
1308cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "decoder RESET\n");
1309cb7a01acSMauro Carvalho Chehab 	saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
1310cb7a01acSMauro Carvalho Chehab 	return 0;
1311cb7a01acSMauro Carvalho Chehab }
1312cb7a01acSMauro Carvalho Chehab 
1313cb7a01acSMauro Carvalho Chehab static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
1314cb7a01acSMauro Carvalho Chehab {
1315cb7a01acSMauro Carvalho Chehab 	/* Note: the internal field ID is inverted for NTSC,
1316cb7a01acSMauro Carvalho Chehab 	   so data->field 0 maps to the saa7115 even field,
1317cb7a01acSMauro Carvalho Chehab 	   whereas for PAL it maps to the saa7115 odd field. */
1318cb7a01acSMauro Carvalho Chehab 	switch (data->id) {
1319cb7a01acSMauro Carvalho Chehab 	case V4L2_SLICED_WSS_625:
1320cb7a01acSMauro Carvalho Chehab 		if (saa711x_read(sd, 0x6b) & 0xc0)
1321cb7a01acSMauro Carvalho Chehab 			return -EIO;
1322cb7a01acSMauro Carvalho Chehab 		data->data[0] = saa711x_read(sd, 0x6c);
1323cb7a01acSMauro Carvalho Chehab 		data->data[1] = saa711x_read(sd, 0x6d);
1324cb7a01acSMauro Carvalho Chehab 		return 0;
1325cb7a01acSMauro Carvalho Chehab 	case V4L2_SLICED_CAPTION_525:
1326cb7a01acSMauro Carvalho Chehab 		if (data->field == 0) {
1327cb7a01acSMauro Carvalho Chehab 			/* CC */
1328cb7a01acSMauro Carvalho Chehab 			if (saa711x_read(sd, 0x66) & 0x30)
1329cb7a01acSMauro Carvalho Chehab 				return -EIO;
1330cb7a01acSMauro Carvalho Chehab 			data->data[0] = saa711x_read(sd, 0x69);
1331cb7a01acSMauro Carvalho Chehab 			data->data[1] = saa711x_read(sd, 0x6a);
1332cb7a01acSMauro Carvalho Chehab 			return 0;
1333cb7a01acSMauro Carvalho Chehab 		}
1334cb7a01acSMauro Carvalho Chehab 		/* XDS */
1335cb7a01acSMauro Carvalho Chehab 		if (saa711x_read(sd, 0x66) & 0xc0)
1336cb7a01acSMauro Carvalho Chehab 			return -EIO;
1337cb7a01acSMauro Carvalho Chehab 		data->data[0] = saa711x_read(sd, 0x67);
1338cb7a01acSMauro Carvalho Chehab 		data->data[1] = saa711x_read(sd, 0x68);
1339cb7a01acSMauro Carvalho Chehab 		return 0;
1340cb7a01acSMauro Carvalho Chehab 	default:
1341cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1342cb7a01acSMauro Carvalho Chehab 	}
1343cb7a01acSMauro Carvalho Chehab }
1344cb7a01acSMauro Carvalho Chehab 
1345cb7a01acSMauro Carvalho Chehab static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
1346cb7a01acSMauro Carvalho Chehab {
1347cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1348cb7a01acSMauro Carvalho Chehab 	int reg1f, reg1e;
1349cb7a01acSMauro Carvalho Chehab 
1350cb7a01acSMauro Carvalho Chehab 	/*
1351cb7a01acSMauro Carvalho Chehab 	 * The V4L2 core already initializes std with all supported
1352cb7a01acSMauro Carvalho Chehab 	 * Standards. All driver needs to do is to mask it, to remove
1353cb7a01acSMauro Carvalho Chehab 	 * standards that don't apply from the mask
1354cb7a01acSMauro Carvalho Chehab 	 */
1355cb7a01acSMauro Carvalho Chehab 
1356cb7a01acSMauro Carvalho Chehab 	reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
1357cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
1358cb7a01acSMauro Carvalho Chehab 
1359cb7a01acSMauro Carvalho Chehab 	/* horizontal/vertical not locked */
1360cb7a01acSMauro Carvalho Chehab 	if (reg1f & 0x40)
1361cb7a01acSMauro Carvalho Chehab 		goto ret;
1362cb7a01acSMauro Carvalho Chehab 
1363cb7a01acSMauro Carvalho Chehab 	if (reg1f & 0x20)
1364cb7a01acSMauro Carvalho Chehab 		*std &= V4L2_STD_525_60;
1365cb7a01acSMauro Carvalho Chehab 	else
1366cb7a01acSMauro Carvalho Chehab 		*std &= V4L2_STD_625_50;
1367cb7a01acSMauro Carvalho Chehab 
1368cb7a01acSMauro Carvalho Chehab 	if (state->ident != V4L2_IDENT_SAA7115)
1369cb7a01acSMauro Carvalho Chehab 		goto ret;
1370cb7a01acSMauro Carvalho Chehab 
1371cb7a01acSMauro Carvalho Chehab 	reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
1372cb7a01acSMauro Carvalho Chehab 
1373cb7a01acSMauro Carvalho Chehab 	switch (reg1e & 0x03) {
1374cb7a01acSMauro Carvalho Chehab 	case 1:
1375cb7a01acSMauro Carvalho Chehab 		*std &= V4L2_STD_NTSC;
1376cb7a01acSMauro Carvalho Chehab 		break;
1377cb7a01acSMauro Carvalho Chehab 	case 2:
1378cb7a01acSMauro Carvalho Chehab 		/*
1379cb7a01acSMauro Carvalho Chehab 		 * V4L2_STD_PAL just cover the european PAL standards.
1380cb7a01acSMauro Carvalho Chehab 		 * This is wrong, as the device could also be using an
1381cb7a01acSMauro Carvalho Chehab 		 * other PAL standard.
1382cb7a01acSMauro Carvalho Chehab 		 */
1383cb7a01acSMauro Carvalho Chehab 		*std &= V4L2_STD_PAL   | V4L2_STD_PAL_N  | V4L2_STD_PAL_Nc |
1384cb7a01acSMauro Carvalho Chehab 			V4L2_STD_PAL_M | V4L2_STD_PAL_60;
1385cb7a01acSMauro Carvalho Chehab 		break;
1386cb7a01acSMauro Carvalho Chehab 	case 3:
1387cb7a01acSMauro Carvalho Chehab 		*std &= V4L2_STD_SECAM;
1388cb7a01acSMauro Carvalho Chehab 		break;
1389cb7a01acSMauro Carvalho Chehab 	default:
1390cb7a01acSMauro Carvalho Chehab 		/* Can't detect anything */
1391cb7a01acSMauro Carvalho Chehab 		break;
1392cb7a01acSMauro Carvalho Chehab 	}
1393cb7a01acSMauro Carvalho Chehab 
1394cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
1395cb7a01acSMauro Carvalho Chehab 
1396cb7a01acSMauro Carvalho Chehab ret:
1397cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std);
1398cb7a01acSMauro Carvalho Chehab 
1399cb7a01acSMauro Carvalho Chehab 	return 0;
1400cb7a01acSMauro Carvalho Chehab }
1401cb7a01acSMauro Carvalho Chehab 
1402cb7a01acSMauro Carvalho Chehab static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
1403cb7a01acSMauro Carvalho Chehab {
1404cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1405cb7a01acSMauro Carvalho Chehab 	int reg1e = 0x80;
1406cb7a01acSMauro Carvalho Chehab 	int reg1f;
1407cb7a01acSMauro Carvalho Chehab 
1408cb7a01acSMauro Carvalho Chehab 	*status = V4L2_IN_ST_NO_SIGNAL;
1409cb7a01acSMauro Carvalho Chehab 	if (state->ident == V4L2_IDENT_SAA7115)
1410cb7a01acSMauro Carvalho Chehab 		reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
1411cb7a01acSMauro Carvalho Chehab 	reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
1412cb7a01acSMauro Carvalho Chehab 	if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
1413cb7a01acSMauro Carvalho Chehab 		*status = 0;
1414cb7a01acSMauro Carvalho Chehab 	return 0;
1415cb7a01acSMauro Carvalho Chehab }
1416cb7a01acSMauro Carvalho Chehab 
1417cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1418cb7a01acSMauro Carvalho Chehab static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1419cb7a01acSMauro Carvalho Chehab {
1420cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1421cb7a01acSMauro Carvalho Chehab 
1422cb7a01acSMauro Carvalho Chehab 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
1423cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1424cb7a01acSMauro Carvalho Chehab 	if (!capable(CAP_SYS_ADMIN))
1425cb7a01acSMauro Carvalho Chehab 		return -EPERM;
1426cb7a01acSMauro Carvalho Chehab 	reg->val = saa711x_read(sd, reg->reg & 0xff);
1427cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
1428cb7a01acSMauro Carvalho Chehab 	return 0;
1429cb7a01acSMauro Carvalho Chehab }
1430cb7a01acSMauro Carvalho Chehab 
1431cb7a01acSMauro Carvalho Chehab static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1432cb7a01acSMauro Carvalho Chehab {
1433cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1434cb7a01acSMauro Carvalho Chehab 
1435cb7a01acSMauro Carvalho Chehab 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
1436cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1437cb7a01acSMauro Carvalho Chehab 	if (!capable(CAP_SYS_ADMIN))
1438cb7a01acSMauro Carvalho Chehab 		return -EPERM;
1439cb7a01acSMauro Carvalho Chehab 	saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
1440cb7a01acSMauro Carvalho Chehab 	return 0;
1441cb7a01acSMauro Carvalho Chehab }
1442cb7a01acSMauro Carvalho Chehab #endif
1443cb7a01acSMauro Carvalho Chehab 
1444cb7a01acSMauro Carvalho Chehab static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
1445cb7a01acSMauro Carvalho Chehab {
1446cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1447cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1448cb7a01acSMauro Carvalho Chehab 
1449cb7a01acSMauro Carvalho Chehab 	return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
1450cb7a01acSMauro Carvalho Chehab }
1451cb7a01acSMauro Carvalho Chehab 
1452cb7a01acSMauro Carvalho Chehab static int saa711x_log_status(struct v4l2_subdev *sd)
1453cb7a01acSMauro Carvalho Chehab {
1454cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state = to_state(sd);
1455cb7a01acSMauro Carvalho Chehab 	int reg1e, reg1f;
1456cb7a01acSMauro Carvalho Chehab 	int signalOk;
1457cb7a01acSMauro Carvalho Chehab 	int vcr;
1458cb7a01acSMauro Carvalho Chehab 
1459cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
1460cb7a01acSMauro Carvalho Chehab 	if (state->ident != V4L2_IDENT_SAA7115) {
1461cb7a01acSMauro Carvalho Chehab 		/* status for the saa7114 */
1462cb7a01acSMauro Carvalho Chehab 		reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
1463cb7a01acSMauro Carvalho Chehab 		signalOk = (reg1f & 0xc1) == 0x81;
1464cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Video signal:    %s\n", signalOk ? "ok" : "bad");
1465cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
1466cb7a01acSMauro Carvalho Chehab 		return 0;
1467cb7a01acSMauro Carvalho Chehab 	}
1468cb7a01acSMauro Carvalho Chehab 
1469cb7a01acSMauro Carvalho Chehab 	/* status for the saa7115 */
1470cb7a01acSMauro Carvalho Chehab 	reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
1471cb7a01acSMauro Carvalho Chehab 	reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
1472cb7a01acSMauro Carvalho Chehab 
1473cb7a01acSMauro Carvalho Chehab 	signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
1474cb7a01acSMauro Carvalho Chehab 	vcr = !(reg1f & 0x10);
1475cb7a01acSMauro Carvalho Chehab 
1476cb7a01acSMauro Carvalho Chehab 	if (state->input >= 6)
1477cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Input:           S-Video %d\n", state->input - 6);
1478cb7a01acSMauro Carvalho Chehab 	else
1479cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Input:           Composite %d\n", state->input);
1480cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Video signal:    %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
1481cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Frequency:       %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
1482cb7a01acSMauro Carvalho Chehab 
1483cb7a01acSMauro Carvalho Chehab 	switch (reg1e & 0x03) {
1484cb7a01acSMauro Carvalho Chehab 	case 1:
1485cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Detected format: NTSC\n");
1486cb7a01acSMauro Carvalho Chehab 		break;
1487cb7a01acSMauro Carvalho Chehab 	case 2:
1488cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Detected format: PAL\n");
1489cb7a01acSMauro Carvalho Chehab 		break;
1490cb7a01acSMauro Carvalho Chehab 	case 3:
1491cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Detected format: SECAM\n");
1492cb7a01acSMauro Carvalho Chehab 		break;
1493cb7a01acSMauro Carvalho Chehab 	default:
1494cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "Detected format: BW/No color\n");
1495cb7a01acSMauro Carvalho Chehab 		break;
1496cb7a01acSMauro Carvalho Chehab 	}
1497cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
1498cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
1499cb7a01acSMauro Carvalho Chehab 	return 0;
1500cb7a01acSMauro Carvalho Chehab }
1501cb7a01acSMauro Carvalho Chehab 
1502cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1503cb7a01acSMauro Carvalho Chehab 
1504cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
1505cb7a01acSMauro Carvalho Chehab 	.s_ctrl = saa711x_s_ctrl,
1506cb7a01acSMauro Carvalho Chehab 	.g_volatile_ctrl = saa711x_g_volatile_ctrl,
1507cb7a01acSMauro Carvalho Chehab };
1508cb7a01acSMauro Carvalho Chehab 
1509cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops saa711x_core_ops = {
1510cb7a01acSMauro Carvalho Chehab 	.log_status = saa711x_log_status,
1511cb7a01acSMauro Carvalho Chehab 	.g_chip_ident = saa711x_g_chip_ident,
1512cb7a01acSMauro Carvalho Chehab 	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
1513cb7a01acSMauro Carvalho Chehab 	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
1514cb7a01acSMauro Carvalho Chehab 	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
1515cb7a01acSMauro Carvalho Chehab 	.g_ctrl = v4l2_subdev_g_ctrl,
1516cb7a01acSMauro Carvalho Chehab 	.s_ctrl = v4l2_subdev_s_ctrl,
1517cb7a01acSMauro Carvalho Chehab 	.queryctrl = v4l2_subdev_queryctrl,
1518cb7a01acSMauro Carvalho Chehab 	.querymenu = v4l2_subdev_querymenu,
1519cb7a01acSMauro Carvalho Chehab 	.s_std = saa711x_s_std,
1520cb7a01acSMauro Carvalho Chehab 	.reset = saa711x_reset,
1521cb7a01acSMauro Carvalho Chehab 	.s_gpio = saa711x_s_gpio,
1522cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1523cb7a01acSMauro Carvalho Chehab 	.g_register = saa711x_g_register,
1524cb7a01acSMauro Carvalho Chehab 	.s_register = saa711x_s_register,
1525cb7a01acSMauro Carvalho Chehab #endif
1526cb7a01acSMauro Carvalho Chehab };
1527cb7a01acSMauro Carvalho Chehab 
1528cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
1529cb7a01acSMauro Carvalho Chehab 	.s_radio = saa711x_s_radio,
1530cb7a01acSMauro Carvalho Chehab 	.g_tuner = saa711x_g_tuner,
1531cb7a01acSMauro Carvalho Chehab };
1532cb7a01acSMauro Carvalho Chehab 
1533cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
1534cb7a01acSMauro Carvalho Chehab 	.s_clock_freq = saa711x_s_clock_freq,
1535cb7a01acSMauro Carvalho Chehab };
1536cb7a01acSMauro Carvalho Chehab 
1537cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops saa711x_video_ops = {
1538cb7a01acSMauro Carvalho Chehab 	.s_routing = saa711x_s_routing,
1539cb7a01acSMauro Carvalho Chehab 	.s_crystal_freq = saa711x_s_crystal_freq,
1540cb7a01acSMauro Carvalho Chehab 	.s_mbus_fmt = saa711x_s_mbus_fmt,
1541cb7a01acSMauro Carvalho Chehab 	.s_stream = saa711x_s_stream,
1542cb7a01acSMauro Carvalho Chehab 	.querystd = saa711x_querystd,
1543cb7a01acSMauro Carvalho Chehab 	.g_input_status = saa711x_g_input_status,
1544cb7a01acSMauro Carvalho Chehab };
1545cb7a01acSMauro Carvalho Chehab 
1546cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = {
1547cb7a01acSMauro Carvalho Chehab 	.g_vbi_data = saa711x_g_vbi_data,
1548cb7a01acSMauro Carvalho Chehab 	.decode_vbi_line = saa711x_decode_vbi_line,
1549cb7a01acSMauro Carvalho Chehab 	.g_sliced_fmt = saa711x_g_sliced_fmt,
1550cb7a01acSMauro Carvalho Chehab 	.s_sliced_fmt = saa711x_s_sliced_fmt,
1551cb7a01acSMauro Carvalho Chehab 	.s_raw_fmt = saa711x_s_raw_fmt,
1552cb7a01acSMauro Carvalho Chehab };
1553cb7a01acSMauro Carvalho Chehab 
1554cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops saa711x_ops = {
1555cb7a01acSMauro Carvalho Chehab 	.core = &saa711x_core_ops,
1556cb7a01acSMauro Carvalho Chehab 	.tuner = &saa711x_tuner_ops,
1557cb7a01acSMauro Carvalho Chehab 	.audio = &saa711x_audio_ops,
1558cb7a01acSMauro Carvalho Chehab 	.video = &saa711x_video_ops,
1559cb7a01acSMauro Carvalho Chehab 	.vbi = &saa711x_vbi_ops,
1560cb7a01acSMauro Carvalho Chehab };
1561cb7a01acSMauro Carvalho Chehab 
1562cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1563cb7a01acSMauro Carvalho Chehab 
1564cb7a01acSMauro Carvalho Chehab static int saa711x_probe(struct i2c_client *client,
1565cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1566cb7a01acSMauro Carvalho Chehab {
1567cb7a01acSMauro Carvalho Chehab 	struct saa711x_state *state;
1568cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1569cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl;
1570cb7a01acSMauro Carvalho Chehab 	int i;
1571cb7a01acSMauro Carvalho Chehab 	char name[17];
1572cb7a01acSMauro Carvalho Chehab 	char chip_id;
1573cb7a01acSMauro Carvalho Chehab 	int autodetect = !id || id->driver_data == 1;
1574cb7a01acSMauro Carvalho Chehab 
1575cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1576cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
1577cb7a01acSMauro Carvalho Chehab 		return -EIO;
1578cb7a01acSMauro Carvalho Chehab 
1579cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < 0x0f; i++) {
1580cb7a01acSMauro Carvalho Chehab 		i2c_smbus_write_byte_data(client, 0, i);
1581cb7a01acSMauro Carvalho Chehab 		name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
1582cb7a01acSMauro Carvalho Chehab 		if (name[i] > '9')
1583cb7a01acSMauro Carvalho Chehab 			name[i] += 'a' - '9' - 1;
1584cb7a01acSMauro Carvalho Chehab 	}
1585cb7a01acSMauro Carvalho Chehab 	name[i] = '\0';
1586cb7a01acSMauro Carvalho Chehab 
1587cb7a01acSMauro Carvalho Chehab 	chip_id = name[5];
1588cb7a01acSMauro Carvalho Chehab 
1589cb7a01acSMauro Carvalho Chehab 	/* Check whether this chip is part of the saa711x series */
1590cb7a01acSMauro Carvalho Chehab 	if (memcmp(name + 1, "f711", 4)) {
1591cb7a01acSMauro Carvalho Chehab 		v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
1592cb7a01acSMauro Carvalho Chehab 			client->addr << 1, name);
1593cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
1594cb7a01acSMauro Carvalho Chehab 	}
1595cb7a01acSMauro Carvalho Chehab 
1596cb7a01acSMauro Carvalho Chehab 	/* Safety check */
1597cb7a01acSMauro Carvalho Chehab 	if (!autodetect && id->name[6] != chip_id) {
1598cb7a01acSMauro Carvalho Chehab 		v4l_warn(client, "found saa711%c while %s was expected\n",
1599cb7a01acSMauro Carvalho Chehab 			 chip_id, id->name);
1600cb7a01acSMauro Carvalho Chehab 	}
1601cb7a01acSMauro Carvalho Chehab 	snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
1602cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
1603cb7a01acSMauro Carvalho Chehab 		 client->addr << 1, client->adapter->name);
1604cb7a01acSMauro Carvalho Chehab 
1605cb7a01acSMauro Carvalho Chehab 	state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
1606cb7a01acSMauro Carvalho Chehab 	if (state == NULL)
1607cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1608cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
1609cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
1610cb7a01acSMauro Carvalho Chehab 
1611cb7a01acSMauro Carvalho Chehab 	hdl = &state->hdl;
1612cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(hdl, 6);
1613cb7a01acSMauro Carvalho Chehab 	/* add in ascending ID order */
1614cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
1615cb7a01acSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
1616cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
1617cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 127, 1, 64);
1618cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
1619cb7a01acSMauro Carvalho Chehab 			V4L2_CID_SATURATION, 0, 127, 1, 64);
1620cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
1621cb7a01acSMauro Carvalho Chehab 			V4L2_CID_HUE, -128, 127, 1, 0);
1622cb7a01acSMauro Carvalho Chehab 	state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
1623cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
1624cb7a01acSMauro Carvalho Chehab 	state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
1625cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
1626cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = hdl;
1627cb7a01acSMauro Carvalho Chehab 	if (hdl->error) {
1628cb7a01acSMauro Carvalho Chehab 		int err = hdl->error;
1629cb7a01acSMauro Carvalho Chehab 
1630cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(hdl);
1631cb7a01acSMauro Carvalho Chehab 		kfree(state);
1632cb7a01acSMauro Carvalho Chehab 		return err;
1633cb7a01acSMauro Carvalho Chehab 	}
1634cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
1635cb7a01acSMauro Carvalho Chehab 
1636cb7a01acSMauro Carvalho Chehab 	state->input = -1;
1637cb7a01acSMauro Carvalho Chehab 	state->output = SAA7115_IPORT_ON;
1638cb7a01acSMauro Carvalho Chehab 	state->enable = 1;
1639cb7a01acSMauro Carvalho Chehab 	state->radio = 0;
1640cb7a01acSMauro Carvalho Chehab 	switch (chip_id) {
1641cb7a01acSMauro Carvalho Chehab 	case '1':
1642cb7a01acSMauro Carvalho Chehab 		state->ident = V4L2_IDENT_SAA7111;
1643cb7a01acSMauro Carvalho Chehab 		if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
1644cb7a01acSMauro Carvalho Chehab 			v4l_info(client, "saa7111a variant found\n");
1645cb7a01acSMauro Carvalho Chehab 			state->ident = V4L2_IDENT_SAA7111A;
1646cb7a01acSMauro Carvalho Chehab 		}
1647cb7a01acSMauro Carvalho Chehab 		break;
1648cb7a01acSMauro Carvalho Chehab 	case '3':
1649cb7a01acSMauro Carvalho Chehab 		state->ident = V4L2_IDENT_SAA7113;
1650cb7a01acSMauro Carvalho Chehab 		break;
1651cb7a01acSMauro Carvalho Chehab 	case '4':
1652cb7a01acSMauro Carvalho Chehab 		state->ident = V4L2_IDENT_SAA7114;
1653cb7a01acSMauro Carvalho Chehab 		break;
1654cb7a01acSMauro Carvalho Chehab 	case '5':
1655cb7a01acSMauro Carvalho Chehab 		state->ident = V4L2_IDENT_SAA7115;
1656cb7a01acSMauro Carvalho Chehab 		break;
1657cb7a01acSMauro Carvalho Chehab 	case '8':
1658cb7a01acSMauro Carvalho Chehab 		state->ident = V4L2_IDENT_SAA7118;
1659cb7a01acSMauro Carvalho Chehab 		break;
1660cb7a01acSMauro Carvalho Chehab 	default:
1661cb7a01acSMauro Carvalho Chehab 		state->ident = V4L2_IDENT_SAA7111;
1662cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
1663cb7a01acSMauro Carvalho Chehab 		break;
1664cb7a01acSMauro Carvalho Chehab 	}
1665cb7a01acSMauro Carvalho Chehab 
1666cb7a01acSMauro Carvalho Chehab 	state->audclk_freq = 48000;
1667cb7a01acSMauro Carvalho Chehab 
1668cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "writing init values\n");
1669cb7a01acSMauro Carvalho Chehab 
1670cb7a01acSMauro Carvalho Chehab 	/* init to 60hz/48khz */
1671cb7a01acSMauro Carvalho Chehab 	state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
1672cb7a01acSMauro Carvalho Chehab 	switch (state->ident) {
1673cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7111:
1674cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7111A:
1675cb7a01acSMauro Carvalho Chehab 		saa711x_writeregs(sd, saa7111_init);
1676cb7a01acSMauro Carvalho Chehab 		break;
1677cb7a01acSMauro Carvalho Chehab 	case V4L2_IDENT_SAA7113:
1678cb7a01acSMauro Carvalho Chehab 		saa711x_writeregs(sd, saa7113_init);
1679cb7a01acSMauro Carvalho Chehab 		break;
1680cb7a01acSMauro Carvalho Chehab 	default:
1681cb7a01acSMauro Carvalho Chehab 		state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
1682cb7a01acSMauro Carvalho Chehab 		saa711x_writeregs(sd, saa7115_init_auto_input);
1683cb7a01acSMauro Carvalho Chehab 	}
1684cb7a01acSMauro Carvalho Chehab 	if (state->ident > V4L2_IDENT_SAA7111A)
1685cb7a01acSMauro Carvalho Chehab 		saa711x_writeregs(sd, saa7115_init_misc);
1686cb7a01acSMauro Carvalho Chehab 	saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
1687cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(hdl);
1688cb7a01acSMauro Carvalho Chehab 
1689cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
1690cb7a01acSMauro Carvalho Chehab 		saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
1691cb7a01acSMauro Carvalho Chehab 		saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
1692cb7a01acSMauro Carvalho Chehab 	return 0;
1693cb7a01acSMauro Carvalho Chehab }
1694cb7a01acSMauro Carvalho Chehab 
1695cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1696cb7a01acSMauro Carvalho Chehab 
1697cb7a01acSMauro Carvalho Chehab static int saa711x_remove(struct i2c_client *client)
1698cb7a01acSMauro Carvalho Chehab {
1699cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1700cb7a01acSMauro Carvalho Chehab 
1701cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
1702cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(sd->ctrl_handler);
1703cb7a01acSMauro Carvalho Chehab 	kfree(to_state(sd));
1704cb7a01acSMauro Carvalho Chehab 	return 0;
1705cb7a01acSMauro Carvalho Chehab }
1706cb7a01acSMauro Carvalho Chehab 
1707cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id saa711x_id[] = {
1708cb7a01acSMauro Carvalho Chehab 	{ "saa7115_auto", 1 }, /* autodetect */
1709cb7a01acSMauro Carvalho Chehab 	{ "saa7111", 0 },
1710cb7a01acSMauro Carvalho Chehab 	{ "saa7113", 0 },
1711cb7a01acSMauro Carvalho Chehab 	{ "saa7114", 0 },
1712cb7a01acSMauro Carvalho Chehab 	{ "saa7115", 0 },
1713cb7a01acSMauro Carvalho Chehab 	{ "saa7118", 0 },
1714cb7a01acSMauro Carvalho Chehab 	{ }
1715cb7a01acSMauro Carvalho Chehab };
1716cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, saa711x_id);
1717cb7a01acSMauro Carvalho Chehab 
1718cb7a01acSMauro Carvalho Chehab static struct i2c_driver saa711x_driver = {
1719cb7a01acSMauro Carvalho Chehab 	.driver = {
1720cb7a01acSMauro Carvalho Chehab 		.owner	= THIS_MODULE,
1721cb7a01acSMauro Carvalho Chehab 		.name	= "saa7115",
1722cb7a01acSMauro Carvalho Chehab 	},
1723cb7a01acSMauro Carvalho Chehab 	.probe		= saa711x_probe,
1724cb7a01acSMauro Carvalho Chehab 	.remove		= saa711x_remove,
1725cb7a01acSMauro Carvalho Chehab 	.id_table	= saa711x_id,
1726cb7a01acSMauro Carvalho Chehab };
1727cb7a01acSMauro Carvalho Chehab 
1728cb7a01acSMauro Carvalho Chehab module_i2c_driver(saa711x_driver);
1729