xref: /openbmc/linux/drivers/media/i2c/tvp5150.c (revision 32590819)
1459ee17cSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
2459ee17cSMauro Carvalho Chehab //
3459ee17cSMauro Carvalho Chehab // tvp5150 - Texas Instruments TVP5150A/AM1 and TVP5151 video decoder driver
4459ee17cSMauro Carvalho Chehab //
532590819SMauro Carvalho Chehab // Copyright (c) 2005,2006 Mauro Carvalho Chehab <mchehab@kernel.org>
6cb7a01acSMauro Carvalho Chehab 
7b802fb99SJavier Martinez Canillas #include <dt-bindings/media/tvp5150.h>
8cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
9cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
10cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
1209aa2609SJavier Martinez Canillas #include <linux/gpio/consumer.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
14859969b3SSakari Ailus #include <linux/of_graph.h>
15c7d97499SJavier Martinez Canillas #include <media/v4l2-async.h>
16cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
17cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
18859969b3SSakari Ailus #include <media/v4l2-fwnode.h>
1955606310SMauro Carvalho Chehab #include <media/v4l2-mc.h>
20cb7a01acSMauro Carvalho Chehab 
21cb7a01acSMauro Carvalho Chehab #include "tvp5150_reg.h"
22cb7a01acSMauro Carvalho Chehab 
23785a3de1SPhilipp Zabel #define TVP5150_H_MAX		720U
24785a3de1SPhilipp Zabel #define TVP5150_V_MAX_525_60	480U
25785a3de1SPhilipp Zabel #define TVP5150_V_MAX_OTHERS	576U
26cb7a01acSMauro Carvalho Chehab #define TVP5150_MAX_CROP_LEFT	511
27cb7a01acSMauro Carvalho Chehab #define TVP5150_MAX_CROP_TOP	127
28cb7a01acSMauro Carvalho Chehab #define TVP5150_CROP_SHIFT	2
29cb7a01acSMauro Carvalho Chehab 
30c43875f6SMauro Carvalho Chehab MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver");
31cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
32459ee17cSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
33cb7a01acSMauro Carvalho Chehab 
34cb7a01acSMauro Carvalho Chehab 
35cb7a01acSMauro Carvalho Chehab static int debug;
362a0489d3SPhilipp Zabel module_param(debug, int, 0644);
37cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-2)");
38cb7a01acSMauro Carvalho Chehab 
39ad0e3744SMauro Carvalho Chehab #define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg)
40ad0e3744SMauro Carvalho Chehab 
41cb7a01acSMauro Carvalho Chehab struct tvp5150 {
42cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
4355606310SMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
4455606310SMauro Carvalho Chehab 	struct media_pad pads[DEMOD_NUM_PADS];
45f7b4b54eSJavier Martinez Canillas 	struct media_entity input_ent[TVP5150_INPUT_NUM];
46f7b4b54eSJavier Martinez Canillas 	struct media_pad input_pad[TVP5150_INPUT_NUM];
4755606310SMauro Carvalho Chehab #endif
48cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
49cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect rect;
50cb7a01acSMauro Carvalho Chehab 
51cb7a01acSMauro Carvalho Chehab 	v4l2_std_id norm;	/* Current set standard */
52cb7a01acSMauro Carvalho Chehab 	u32 input;
53cb7a01acSMauro Carvalho Chehab 	u32 output;
54cb7a01acSMauro Carvalho Chehab 	int enable;
55a2e5f1b3SJavier Martinez Canillas 
5682275133SJavier Martinez Canillas 	u16 dev_id;
5782275133SJavier Martinez Canillas 	u16 rom_ver;
5882275133SJavier Martinez Canillas 
59a2e5f1b3SJavier Martinez Canillas 	enum v4l2_mbus_type mbus_type;
60cb7a01acSMauro Carvalho Chehab };
61cb7a01acSMauro Carvalho Chehab 
62cb7a01acSMauro Carvalho Chehab static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
63cb7a01acSMauro Carvalho Chehab {
64cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct tvp5150, sd);
65cb7a01acSMauro Carvalho Chehab }
66cb7a01acSMauro Carvalho Chehab 
67cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
68cb7a01acSMauro Carvalho Chehab {
69cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct tvp5150, hdl)->sd;
70cb7a01acSMauro Carvalho Chehab }
71cb7a01acSMauro Carvalho Chehab 
72cb7a01acSMauro Carvalho Chehab static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
73cb7a01acSMauro Carvalho Chehab {
74cb7a01acSMauro Carvalho Chehab 	struct i2c_client *c = v4l2_get_subdevdata(sd);
75cb7a01acSMauro Carvalho Chehab 	int rc;
76cb7a01acSMauro Carvalho Chehab 
77e35ce2e4SLaurent Pinchart 	rc = i2c_smbus_read_byte_data(c, addr);
78e35ce2e4SLaurent Pinchart 	if (rc < 0) {
79257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "i2c i/o error: rc == %d\n", rc);
80e35ce2e4SLaurent Pinchart 		return rc;
81cb7a01acSMauro Carvalho Chehab 	}
82cb7a01acSMauro Carvalho Chehab 
83257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 2, debug, "tvp5150: read 0x%02x = %02x\n", addr, rc);
84cb7a01acSMauro Carvalho Chehab 
85e35ce2e4SLaurent Pinchart 	return rc;
86cb7a01acSMauro Carvalho Chehab }
87cb7a01acSMauro Carvalho Chehab 
88cacdd6a4SJavier Martinez Canillas static int tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
89cb7a01acSMauro Carvalho Chehab 				 unsigned char value)
90cb7a01acSMauro Carvalho Chehab {
91cb7a01acSMauro Carvalho Chehab 	struct i2c_client *c = v4l2_get_subdevdata(sd);
92cb7a01acSMauro Carvalho Chehab 	int rc;
93cb7a01acSMauro Carvalho Chehab 
94257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 2, debug, "tvp5150: writing %02x %02x\n", addr, value);
95e35ce2e4SLaurent Pinchart 	rc = i2c_smbus_write_byte_data(c, addr, value);
96e35ce2e4SLaurent Pinchart 	if (rc < 0)
97257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "i2c i/o error: rc == %d\n", rc);
98cacdd6a4SJavier Martinez Canillas 
99cacdd6a4SJavier Martinez Canillas 	return rc;
100cb7a01acSMauro Carvalho Chehab }
101cb7a01acSMauro Carvalho Chehab 
102cb7a01acSMauro Carvalho Chehab static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
103cb7a01acSMauro Carvalho Chehab 				const u8 end, int max_line)
104cb7a01acSMauro Carvalho Chehab {
105e5134114SMauro Carvalho Chehab 	u8 buf[16];
106e5134114SMauro Carvalho Chehab 	int i = 0, j, len;
107cb7a01acSMauro Carvalho Chehab 
108e5134114SMauro Carvalho Chehab 	if (max_line > 16) {
109e5134114SMauro Carvalho Chehab 		dprintk0(sd->dev, "too much data to dump\n");
110e5134114SMauro Carvalho Chehab 		return;
111cb7a01acSMauro Carvalho Chehab 	}
112cb7a01acSMauro Carvalho Chehab 
113e5134114SMauro Carvalho Chehab 	for (i = init; i < end; i += max_line) {
114e5134114SMauro Carvalho Chehab 		len = (end - i > max_line) ? max_line : end - i;
115e5134114SMauro Carvalho Chehab 
116e5134114SMauro Carvalho Chehab 		for (j = 0; j < len; j++)
117e5134114SMauro Carvalho Chehab 			buf[j] = tvp5150_read(sd, i + j);
118e5134114SMauro Carvalho Chehab 
119e5134114SMauro Carvalho Chehab 		dprintk0(sd->dev, "%s reg %02x = %*ph\n", s, i, len, buf);
120cb7a01acSMauro Carvalho Chehab 	}
121cb7a01acSMauro Carvalho Chehab }
122cb7a01acSMauro Carvalho Chehab 
123cb7a01acSMauro Carvalho Chehab static int tvp5150_log_status(struct v4l2_subdev *sd)
124cb7a01acSMauro Carvalho Chehab {
125ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Video input source selection #1 = 0x%02x\n",
126cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
127ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Analog channel controls = 0x%02x\n",
128cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
129ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Operation mode controls = 0x%02x\n",
130cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_OP_MODE_CTL));
131ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Miscellaneous controls = 0x%02x\n",
132cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MISC_CTL));
133ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Autoswitch mask= 0x%02x\n",
134cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_AUTOSW_MSK));
135ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Color killer threshold control = 0x%02x\n",
136cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
137ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
138cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
139cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
140cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
141ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Brightness control = 0x%02x\n",
142cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_BRIGHT_CTL));
143ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Color saturation control = 0x%02x\n",
144cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_SATURATION_CTL));
145ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Hue control = 0x%02x\n",
146cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_HUE_CTL));
147ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Contrast control = 0x%02x\n",
148cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CONTRAST_CTL));
149ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Outputs and data rates select = 0x%02x\n",
150cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
151ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Configuration shared pins = 0x%02x\n",
152cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
153ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Active video cropping start = 0x%02x%02x\n",
154cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
155cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
156ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Active video cropping stop  = 0x%02x%02x\n",
157cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
158cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
159ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Genlock/RTC = 0x%02x\n",
160cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_GENLOCK));
161ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Horizontal sync start = 0x%02x\n",
162cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
163ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Vertical blanking start = 0x%02x\n",
164cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
165ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Vertical blanking stop = 0x%02x\n",
166cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
167ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
168cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
169cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
170ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt reset register B = 0x%02x\n",
171cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
172ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt enable register B = 0x%02x\n",
173cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
174ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt configuration register B = 0x%02x\n",
175cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
176ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Video standard = 0x%02x\n",
177cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VIDEO_STD));
178ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
179cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
180cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
181ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Macrovision on counter = 0x%02x\n",
182cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
183ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Macrovision off counter = 0x%02x\n",
184cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
185ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
186cb7a01acSMauro Carvalho Chehab 		(tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
187ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Device ID = %02x%02x\n",
188cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MSB_DEV_ID),
189cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LSB_DEV_ID));
190ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: ROM version = (hex) %02x.%02x\n",
191cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
192cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
193ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Vertical line count = 0x%02x%02x\n",
194cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
195cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
196ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt status register B = 0x%02x\n",
197cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
198ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt active register B = 0x%02x\n",
199cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
200ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
201cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_1),
202cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_2),
203cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_3),
204cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_4),
205cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_5));
206cb7a01acSMauro Carvalho Chehab 
207cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "Teletext filter 1",   TVP5150_TELETEXT_FIL1_INI,
208cb7a01acSMauro Carvalho Chehab 			TVP5150_TELETEXT_FIL1_END, 8);
209cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "Teletext filter 2",   TVP5150_TELETEXT_FIL2_INI,
210cb7a01acSMauro Carvalho Chehab 			TVP5150_TELETEXT_FIL2_END, 8);
211cb7a01acSMauro Carvalho Chehab 
212ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Teletext filter enable = 0x%02x\n",
213cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
214ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt status register A = 0x%02x\n",
215cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
216ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt enable register A = 0x%02x\n",
217cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
218ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt configuration = 0x%02x\n",
219cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_CONF));
220ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: VDP status register = 0x%02x\n",
221cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
222ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO word count = 0x%02x\n",
223cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
224ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO interrupt threshold = 0x%02x\n",
225cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
226ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO reset = 0x%02x\n",
227cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_RESET));
228ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Line number interrupt = 0x%02x\n",
229cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
230ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Pixel alignment register = 0x%02x%02x\n",
231cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
232cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
233ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO output control = 0x%02x\n",
234cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
235ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Full field enable = 0x%02x\n",
236cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
237ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Full field mode register = 0x%02x\n",
238cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
239cb7a01acSMauro Carvalho Chehab 
240cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "CC   data",   TVP5150_CC_DATA_INI,
241cb7a01acSMauro Carvalho Chehab 			TVP5150_CC_DATA_END, 8);
242cb7a01acSMauro Carvalho Chehab 
243cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "WSS  data",   TVP5150_WSS_DATA_INI,
244cb7a01acSMauro Carvalho Chehab 			TVP5150_WSS_DATA_END, 8);
245cb7a01acSMauro Carvalho Chehab 
246cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "VPS  data",   TVP5150_VPS_DATA_INI,
247cb7a01acSMauro Carvalho Chehab 			TVP5150_VPS_DATA_END, 8);
248cb7a01acSMauro Carvalho Chehab 
249cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "VITC data",   TVP5150_VITC_DATA_INI,
250cb7a01acSMauro Carvalho Chehab 			TVP5150_VITC_DATA_END, 10);
251cb7a01acSMauro Carvalho Chehab 
252cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "Line mode",   TVP5150_LINE_MODE_INI,
253cb7a01acSMauro Carvalho Chehab 			TVP5150_LINE_MODE_END, 8);
254cb7a01acSMauro Carvalho Chehab 	return 0;
255cb7a01acSMauro Carvalho Chehab }
256cb7a01acSMauro Carvalho Chehab 
257cb7a01acSMauro Carvalho Chehab /****************************************************************************
258cb7a01acSMauro Carvalho Chehab 			Basic functions
259cb7a01acSMauro Carvalho Chehab  ****************************************************************************/
260cb7a01acSMauro Carvalho Chehab 
2616e98bee2SLaurent Pinchart static void tvp5150_selmux(struct v4l2_subdev *sd)
262cb7a01acSMauro Carvalho Chehab {
263cb7a01acSMauro Carvalho Chehab 	int opmode = 0;
264cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
265cb7a01acSMauro Carvalho Chehab 	int input = 0;
266cb7a01acSMauro Carvalho Chehab 	int val;
267cb7a01acSMauro Carvalho Chehab 
268c43875f6SMauro Carvalho Chehab 	/* Only tvp5150am1 and tvp5151 have signal generator support */
269c43875f6SMauro Carvalho Chehab 	if ((decoder->dev_id == 0x5150 && decoder->rom_ver == 0x0400) ||
270c43875f6SMauro Carvalho Chehab 	    (decoder->dev_id == 0x5151 && decoder->rom_ver == 0x0100)) {
271c43875f6SMauro Carvalho Chehab 		if (!decoder->enable)
272cb7a01acSMauro Carvalho Chehab 			input = 8;
273c43875f6SMauro Carvalho Chehab 	}
274cb7a01acSMauro Carvalho Chehab 
275cb7a01acSMauro Carvalho Chehab 	switch (decoder->input) {
276cb7a01acSMauro Carvalho Chehab 	case TVP5150_COMPOSITE1:
277cb7a01acSMauro Carvalho Chehab 		input |= 2;
278cb7a01acSMauro Carvalho Chehab 		/* fall through */
279cb7a01acSMauro Carvalho Chehab 	case TVP5150_COMPOSITE0:
280cb7a01acSMauro Carvalho Chehab 		break;
281cb7a01acSMauro Carvalho Chehab 	case TVP5150_SVIDEO:
282cb7a01acSMauro Carvalho Chehab 	default:
283cb7a01acSMauro Carvalho Chehab 		input |= 1;
284cb7a01acSMauro Carvalho Chehab 		break;
285cb7a01acSMauro Carvalho Chehab 	}
286cb7a01acSMauro Carvalho Chehab 
287257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "Selecting video route: route input=%i, output=%i => tvp5150 input=%i, opmode=%i\n",
288cb7a01acSMauro Carvalho Chehab 			decoder->input, decoder->output,
289cb7a01acSMauro Carvalho Chehab 			input, opmode);
290cb7a01acSMauro Carvalho Chehab 
291cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
292cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
293cb7a01acSMauro Carvalho Chehab 
294b4b2de38SLaurent Pinchart 	/*
295b4b2de38SLaurent Pinchart 	 * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For
296b4b2de38SLaurent Pinchart 	 * S-Video we output the vertical lock (VLK) signal on FID/GLCO/VLK/HVLK
297b4b2de38SLaurent Pinchart 	 * and set INTREQ/GPCL/VBLK to logic 0. For composite we output the
298b4b2de38SLaurent Pinchart 	 * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set
299b4b2de38SLaurent Pinchart 	 * INTREQ/GPCL/VBLK to logic 1.
300cb7a01acSMauro Carvalho Chehab 	 */
301cb7a01acSMauro Carvalho Chehab 	val = tvp5150_read(sd, TVP5150_MISC_CTL);
302cb7a01acSMauro Carvalho Chehab 	if (val < 0) {
303257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "%s: failed with error = %d\n", __func__, val);
304cb7a01acSMauro Carvalho Chehab 		return;
305cb7a01acSMauro Carvalho Chehab 	}
306cb7a01acSMauro Carvalho Chehab 
307cb7a01acSMauro Carvalho Chehab 	if (decoder->input == TVP5150_SVIDEO)
308b4b2de38SLaurent Pinchart 		val = (val & ~TVP5150_MISC_CTL_GPCL) | TVP5150_MISC_CTL_HVLK;
309cb7a01acSMauro Carvalho Chehab 	else
310b4b2de38SLaurent Pinchart 		val = (val & ~TVP5150_MISC_CTL_HVLK) | TVP5150_MISC_CTL_GPCL;
311cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_MISC_CTL, val);
312cb7a01acSMauro Carvalho Chehab };
313cb7a01acSMauro Carvalho Chehab 
314cb7a01acSMauro Carvalho Chehab struct i2c_reg_value {
315cb7a01acSMauro Carvalho Chehab 	unsigned char reg;
316cb7a01acSMauro Carvalho Chehab 	unsigned char value;
317cb7a01acSMauro Carvalho Chehab };
318cb7a01acSMauro Carvalho Chehab 
319cb7a01acSMauro Carvalho Chehab /* Default values as sugested at TVP5150AM1 datasheet */
320cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp5150_init_default[] = {
321cb7a01acSMauro Carvalho Chehab 	{ /* 0x00 */
322cb7a01acSMauro Carvalho Chehab 		TVP5150_VD_IN_SRC_SEL_1,0x00
323cb7a01acSMauro Carvalho Chehab 	},
324cb7a01acSMauro Carvalho Chehab 	{ /* 0x01 */
325cb7a01acSMauro Carvalho Chehab 		TVP5150_ANAL_CHL_CTL,0x15
326cb7a01acSMauro Carvalho Chehab 	},
327cb7a01acSMauro Carvalho Chehab 	{ /* 0x02 */
328cb7a01acSMauro Carvalho Chehab 		TVP5150_OP_MODE_CTL,0x00
329cb7a01acSMauro Carvalho Chehab 	},
330cb7a01acSMauro Carvalho Chehab 	{ /* 0x03 */
331cb7a01acSMauro Carvalho Chehab 		TVP5150_MISC_CTL,0x01
332cb7a01acSMauro Carvalho Chehab 	},
333cb7a01acSMauro Carvalho Chehab 	{ /* 0x06 */
334cb7a01acSMauro Carvalho Chehab 		TVP5150_COLOR_KIL_THSH_CTL,0x10
335cb7a01acSMauro Carvalho Chehab 	},
336cb7a01acSMauro Carvalho Chehab 	{ /* 0x07 */
337cb7a01acSMauro Carvalho Chehab 		TVP5150_LUMA_PROC_CTL_1,0x60
338cb7a01acSMauro Carvalho Chehab 	},
339cb7a01acSMauro Carvalho Chehab 	{ /* 0x08 */
340cb7a01acSMauro Carvalho Chehab 		TVP5150_LUMA_PROC_CTL_2,0x00
341cb7a01acSMauro Carvalho Chehab 	},
342cb7a01acSMauro Carvalho Chehab 	{ /* 0x09 */
343cb7a01acSMauro Carvalho Chehab 		TVP5150_BRIGHT_CTL,0x80
344cb7a01acSMauro Carvalho Chehab 	},
345cb7a01acSMauro Carvalho Chehab 	{ /* 0x0a */
346cb7a01acSMauro Carvalho Chehab 		TVP5150_SATURATION_CTL,0x80
347cb7a01acSMauro Carvalho Chehab 	},
348cb7a01acSMauro Carvalho Chehab 	{ /* 0x0b */
349cb7a01acSMauro Carvalho Chehab 		TVP5150_HUE_CTL,0x00
350cb7a01acSMauro Carvalho Chehab 	},
351cb7a01acSMauro Carvalho Chehab 	{ /* 0x0c */
352cb7a01acSMauro Carvalho Chehab 		TVP5150_CONTRAST_CTL,0x80
353cb7a01acSMauro Carvalho Chehab 	},
354cb7a01acSMauro Carvalho Chehab 	{ /* 0x0d */
355cb7a01acSMauro Carvalho Chehab 		TVP5150_DATA_RATE_SEL,0x47
356cb7a01acSMauro Carvalho Chehab 	},
357cb7a01acSMauro Carvalho Chehab 	{ /* 0x0e */
358cb7a01acSMauro Carvalho Chehab 		TVP5150_LUMA_PROC_CTL_3,0x00
359cb7a01acSMauro Carvalho Chehab 	},
360cb7a01acSMauro Carvalho Chehab 	{ /* 0x0f */
361cb7a01acSMauro Carvalho Chehab 		TVP5150_CONF_SHARED_PIN,0x08
362cb7a01acSMauro Carvalho Chehab 	},
363cb7a01acSMauro Carvalho Chehab 	{ /* 0x11 */
364cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_ST_MSB,0x00
365cb7a01acSMauro Carvalho Chehab 	},
366cb7a01acSMauro Carvalho Chehab 	{ /* 0x12 */
367cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_ST_LSB,0x00
368cb7a01acSMauro Carvalho Chehab 	},
369cb7a01acSMauro Carvalho Chehab 	{ /* 0x13 */
370cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_STP_MSB,0x00
371cb7a01acSMauro Carvalho Chehab 	},
372cb7a01acSMauro Carvalho Chehab 	{ /* 0x14 */
373cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_STP_LSB,0x00
374cb7a01acSMauro Carvalho Chehab 	},
375cb7a01acSMauro Carvalho Chehab 	{ /* 0x15 */
376cb7a01acSMauro Carvalho Chehab 		TVP5150_GENLOCK,0x01
377cb7a01acSMauro Carvalho Chehab 	},
378cb7a01acSMauro Carvalho Chehab 	{ /* 0x16 */
379cb7a01acSMauro Carvalho Chehab 		TVP5150_HORIZ_SYNC_START,0x80
380cb7a01acSMauro Carvalho Chehab 	},
381cb7a01acSMauro Carvalho Chehab 	{ /* 0x18 */
382cb7a01acSMauro Carvalho Chehab 		TVP5150_VERT_BLANKING_START,0x00
383cb7a01acSMauro Carvalho Chehab 	},
384cb7a01acSMauro Carvalho Chehab 	{ /* 0x19 */
385cb7a01acSMauro Carvalho Chehab 		TVP5150_VERT_BLANKING_STOP,0x00
386cb7a01acSMauro Carvalho Chehab 	},
387cb7a01acSMauro Carvalho Chehab 	{ /* 0x1a */
388cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_1,0x0c
389cb7a01acSMauro Carvalho Chehab 	},
390cb7a01acSMauro Carvalho Chehab 	{ /* 0x1b */
391cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_2,0x14
392cb7a01acSMauro Carvalho Chehab 	},
393cb7a01acSMauro Carvalho Chehab 	{ /* 0x1c */
394cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_RESET_REG_B,0x00
395cb7a01acSMauro Carvalho Chehab 	},
396cb7a01acSMauro Carvalho Chehab 	{ /* 0x1d */
397cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_ENABLE_REG_B,0x00
398cb7a01acSMauro Carvalho Chehab 	},
399cb7a01acSMauro Carvalho Chehab 	{ /* 0x1e */
400cb7a01acSMauro Carvalho Chehab 		TVP5150_INTT_CONFIG_REG_B,0x00
401cb7a01acSMauro Carvalho Chehab 	},
402cb7a01acSMauro Carvalho Chehab 	{ /* 0x28 */
403cb7a01acSMauro Carvalho Chehab 		TVP5150_VIDEO_STD,0x00
404cb7a01acSMauro Carvalho Chehab 	},
405cb7a01acSMauro Carvalho Chehab 	{ /* 0x2e */
406cb7a01acSMauro Carvalho Chehab 		TVP5150_MACROVISION_ON_CTR,0x0f
407cb7a01acSMauro Carvalho Chehab 	},
408cb7a01acSMauro Carvalho Chehab 	{ /* 0x2f */
409cb7a01acSMauro Carvalho Chehab 		TVP5150_MACROVISION_OFF_CTR,0x01
410cb7a01acSMauro Carvalho Chehab 	},
411cb7a01acSMauro Carvalho Chehab 	{ /* 0xbb */
412cb7a01acSMauro Carvalho Chehab 		TVP5150_TELETEXT_FIL_ENA,0x00
413cb7a01acSMauro Carvalho Chehab 	},
414cb7a01acSMauro Carvalho Chehab 	{ /* 0xc0 */
415cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_STATUS_REG_A,0x00
416cb7a01acSMauro Carvalho Chehab 	},
417cb7a01acSMauro Carvalho Chehab 	{ /* 0xc1 */
418cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_ENABLE_REG_A,0x00
419cb7a01acSMauro Carvalho Chehab 	},
420cb7a01acSMauro Carvalho Chehab 	{ /* 0xc2 */
421cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_CONF,0x04
422cb7a01acSMauro Carvalho Chehab 	},
423cb7a01acSMauro Carvalho Chehab 	{ /* 0xc8 */
424cb7a01acSMauro Carvalho Chehab 		TVP5150_FIFO_INT_THRESHOLD,0x80
425cb7a01acSMauro Carvalho Chehab 	},
426cb7a01acSMauro Carvalho Chehab 	{ /* 0xc9 */
427cb7a01acSMauro Carvalho Chehab 		TVP5150_FIFO_RESET,0x00
428cb7a01acSMauro Carvalho Chehab 	},
429cb7a01acSMauro Carvalho Chehab 	{ /* 0xca */
430cb7a01acSMauro Carvalho Chehab 		TVP5150_LINE_NUMBER_INT,0x00
431cb7a01acSMauro Carvalho Chehab 	},
432cb7a01acSMauro Carvalho Chehab 	{ /* 0xcb */
433cb7a01acSMauro Carvalho Chehab 		TVP5150_PIX_ALIGN_REG_LOW,0x4e
434cb7a01acSMauro Carvalho Chehab 	},
435cb7a01acSMauro Carvalho Chehab 	{ /* 0xcc */
436cb7a01acSMauro Carvalho Chehab 		TVP5150_PIX_ALIGN_REG_HIGH,0x00
437cb7a01acSMauro Carvalho Chehab 	},
438cb7a01acSMauro Carvalho Chehab 	{ /* 0xcd */
439cb7a01acSMauro Carvalho Chehab 		TVP5150_FIFO_OUT_CTRL,0x01
440cb7a01acSMauro Carvalho Chehab 	},
441cb7a01acSMauro Carvalho Chehab 	{ /* 0xcf */
442cb7a01acSMauro Carvalho Chehab 		TVP5150_FULL_FIELD_ENA,0x00
443cb7a01acSMauro Carvalho Chehab 	},
444cb7a01acSMauro Carvalho Chehab 	{ /* 0xd0 */
445cb7a01acSMauro Carvalho Chehab 		TVP5150_LINE_MODE_INI,0x00
446cb7a01acSMauro Carvalho Chehab 	},
447cb7a01acSMauro Carvalho Chehab 	{ /* 0xfc */
448cb7a01acSMauro Carvalho Chehab 		TVP5150_FULL_FIELD_MODE_REG,0x7f
449cb7a01acSMauro Carvalho Chehab 	},
450cb7a01acSMauro Carvalho Chehab 	{ /* end of data */
451cb7a01acSMauro Carvalho Chehab 		0xff,0xff
452cb7a01acSMauro Carvalho Chehab 	}
453cb7a01acSMauro Carvalho Chehab };
454cb7a01acSMauro Carvalho Chehab 
455cb7a01acSMauro Carvalho Chehab /* Default values as sugested at TVP5150AM1 datasheet */
456cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp5150_init_enable[] = {
457cb7a01acSMauro Carvalho Chehab 	{
458cb7a01acSMauro Carvalho Chehab 		TVP5150_CONF_SHARED_PIN, 2
459cb7a01acSMauro Carvalho Chehab 	},{	/* Automatic offset and AGC enabled */
460cb7a01acSMauro Carvalho Chehab 		TVP5150_ANAL_CHL_CTL, 0x15
461cb7a01acSMauro Carvalho Chehab 	},{	/* Activate YCrCb output 0x9 or 0xd ? */
462b4b2de38SLaurent Pinchart 		TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL |
463b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_INTREQ_OE |
464b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_YCBCR_OE |
465b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_SYNC_OE |
466b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_VBLANK |
467b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_CLOCK_OE,
468cb7a01acSMauro Carvalho Chehab 	},{	/* Activates video std autodetection for all standards */
469cb7a01acSMauro Carvalho Chehab 		TVP5150_AUTOSW_MSK, 0x0
470cb7a01acSMauro Carvalho Chehab 	},{	/* Default format: 0x47. For 4:2:2: 0x40 */
471cb7a01acSMauro Carvalho Chehab 		TVP5150_DATA_RATE_SEL, 0x47
472cb7a01acSMauro Carvalho Chehab 	},{
473cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_1, 0x0c
474cb7a01acSMauro Carvalho Chehab 	},{
475cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_2, 0x54
476cb7a01acSMauro Carvalho Chehab 	},{	/* Non documented, but initialized on WinTV USB2 */
477cb7a01acSMauro Carvalho Chehab 		0x27, 0x20
478cb7a01acSMauro Carvalho Chehab 	},{
479cb7a01acSMauro Carvalho Chehab 		0xff,0xff
480cb7a01acSMauro Carvalho Chehab 	}
481cb7a01acSMauro Carvalho Chehab };
482cb7a01acSMauro Carvalho Chehab 
483cb7a01acSMauro Carvalho Chehab struct tvp5150_vbi_type {
484cb7a01acSMauro Carvalho Chehab 	unsigned int vbi_type;
485cb7a01acSMauro Carvalho Chehab 	unsigned int ini_line;
486cb7a01acSMauro Carvalho Chehab 	unsigned int end_line;
487cb7a01acSMauro Carvalho Chehab 	unsigned int by_field :1;
488cb7a01acSMauro Carvalho Chehab };
489cb7a01acSMauro Carvalho Chehab 
490cb7a01acSMauro Carvalho Chehab struct i2c_vbi_ram_value {
491cb7a01acSMauro Carvalho Chehab 	u16 reg;
492cb7a01acSMauro Carvalho Chehab 	struct tvp5150_vbi_type type;
493cb7a01acSMauro Carvalho Chehab 	unsigned char values[16];
494cb7a01acSMauro Carvalho Chehab };
495cb7a01acSMauro Carvalho Chehab 
496cb7a01acSMauro Carvalho Chehab /* This struct have the values for each supported VBI Standard
497cb7a01acSMauro Carvalho Chehab  * by
498cb7a01acSMauro Carvalho Chehab  tvp5150_vbi_types should follow the same order as vbi_ram_default
499cb7a01acSMauro Carvalho Chehab  * value 0 means rom position 0x10, value 1 means rom position 0x30
500cb7a01acSMauro Carvalho Chehab  * and so on. There are 16 possible locations from 0 to 15.
501cb7a01acSMauro Carvalho Chehab  */
502cb7a01acSMauro Carvalho Chehab 
503cb7a01acSMauro Carvalho Chehab static struct i2c_vbi_ram_value vbi_ram_default[] =
504cb7a01acSMauro Carvalho Chehab {
505cb7a01acSMauro Carvalho Chehab 	/* FIXME: Current api doesn't handle all VBI types, those not
506cb7a01acSMauro Carvalho Chehab 	   yet supported are placed under #if 0 */
507cb7a01acSMauro Carvalho Chehab #if 0
5083dd6b560SMauro Carvalho Chehab 	[0] = {0x010, /* Teletext, SECAM, WST System A */
509cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_SECAM,6,23,1},
510cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
511cb7a01acSMauro Carvalho Chehab 		  0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
512cb7a01acSMauro Carvalho Chehab 	},
513cb7a01acSMauro Carvalho Chehab #endif
5143dd6b560SMauro Carvalho Chehab 	[1] = {0x030, /* Teletext, PAL, WST System B */
515cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_B,6,22,1},
516cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
517cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
518cb7a01acSMauro Carvalho Chehab 	},
519cb7a01acSMauro Carvalho Chehab #if 0
5203dd6b560SMauro Carvalho Chehab 	[2] = {0x050, /* Teletext, PAL, WST System C */
521cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_PAL_C,6,22,1},
522cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
523cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
524cb7a01acSMauro Carvalho Chehab 	},
5253dd6b560SMauro Carvalho Chehab 	[3] = {0x070, /* Teletext, NTSC, WST System B */
526cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_NTSC_B,10,21,1},
527cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
528cb7a01acSMauro Carvalho Chehab 		  0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
529cb7a01acSMauro Carvalho Chehab 	},
5303dd6b560SMauro Carvalho Chehab 	[4] = {0x090, /* Tetetext, NTSC NABTS System C */
531cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_NTSC_C,10,21,1},
532cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
533cb7a01acSMauro Carvalho Chehab 		  0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
534cb7a01acSMauro Carvalho Chehab 	},
5353dd6b560SMauro Carvalho Chehab 	[5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */
536cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_NTSC_D,10,21,1},
537cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
538cb7a01acSMauro Carvalho Chehab 		  0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
539cb7a01acSMauro Carvalho Chehab 	},
5403dd6b560SMauro Carvalho Chehab 	[6] = {0x0d0, /* Closed Caption, PAL/SECAM */
541cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_CAPTION_625,22,22,1},
542cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
543cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
544cb7a01acSMauro Carvalho Chehab 	},
545cb7a01acSMauro Carvalho Chehab #endif
5463dd6b560SMauro Carvalho Chehab 	[7] = {0x0f0, /* Closed Caption, NTSC */
547cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_CAPTION_525,21,21,1},
548cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
549cb7a01acSMauro Carvalho Chehab 		  0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
550cb7a01acSMauro Carvalho Chehab 	},
5513dd6b560SMauro Carvalho Chehab 	[8] = {0x110, /* Wide Screen Signal, PAL/SECAM */
552cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_WSS_625,23,23,1},
553cb7a01acSMauro Carvalho Chehab 		{ 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
554cb7a01acSMauro Carvalho Chehab 		  0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
555cb7a01acSMauro Carvalho Chehab 	},
556cb7a01acSMauro Carvalho Chehab #if 0
5573dd6b560SMauro Carvalho Chehab 	[9] = {0x130, /* Wide Screen Signal, NTSC C */
558cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_WSS_525,20,20,1},
559cb7a01acSMauro Carvalho Chehab 		{ 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
560cb7a01acSMauro Carvalho Chehab 		  0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
561cb7a01acSMauro Carvalho Chehab 	},
5623dd6b560SMauro Carvalho Chehab 	[10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
563cb7a01acSMauro Carvalho Chehab 		{V4l2_SLICED_VITC_625,6,22,0},
564cb7a01acSMauro Carvalho Chehab 		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
565cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
566cb7a01acSMauro Carvalho Chehab 	},
5673dd6b560SMauro Carvalho Chehab 	[11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */
568cb7a01acSMauro Carvalho Chehab 		{V4l2_SLICED_VITC_525,10,20,0},
569cb7a01acSMauro Carvalho Chehab 		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
570cb7a01acSMauro Carvalho Chehab 		  0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
571cb7a01acSMauro Carvalho Chehab 	},
572cb7a01acSMauro Carvalho Chehab #endif
5733dd6b560SMauro Carvalho Chehab 	[12] = {0x190, /* Video Program System (VPS), PAL */
574cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_VPS,16,16,0},
575cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
576cb7a01acSMauro Carvalho Chehab 		  0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
577cb7a01acSMauro Carvalho Chehab 	},
578cb7a01acSMauro Carvalho Chehab 	/* 0x1d0 User programmable */
579cb7a01acSMauro Carvalho Chehab };
580cb7a01acSMauro Carvalho Chehab 
581cb7a01acSMauro Carvalho Chehab static int tvp5150_write_inittab(struct v4l2_subdev *sd,
582cb7a01acSMauro Carvalho Chehab 				const struct i2c_reg_value *regs)
583cb7a01acSMauro Carvalho Chehab {
584cb7a01acSMauro Carvalho Chehab 	while (regs->reg != 0xff) {
585cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, regs->reg, regs->value);
586cb7a01acSMauro Carvalho Chehab 		regs++;
587cb7a01acSMauro Carvalho Chehab 	}
588cb7a01acSMauro Carvalho Chehab 	return 0;
589cb7a01acSMauro Carvalho Chehab }
590cb7a01acSMauro Carvalho Chehab 
5913dd6b560SMauro Carvalho Chehab static int tvp5150_vdp_init(struct v4l2_subdev *sd)
592cb7a01acSMauro Carvalho Chehab {
593cb7a01acSMauro Carvalho Chehab 	unsigned int i;
5943dd6b560SMauro Carvalho Chehab 	int j;
595cb7a01acSMauro Carvalho Chehab 
596cb7a01acSMauro Carvalho Chehab 	/* Disable Full Field */
597cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
598cb7a01acSMauro Carvalho Chehab 
599cb7a01acSMauro Carvalho Chehab 	/* Before programming, Line mode should be at 0xff */
600cb7a01acSMauro Carvalho Chehab 	for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
601cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, i, 0xff);
602cb7a01acSMauro Carvalho Chehab 
603cb7a01acSMauro Carvalho Chehab 	/* Load Ram Table */
6043dd6b560SMauro Carvalho Chehab 	for (j = 0; j < ARRAY_SIZE(vbi_ram_default); j++) {
6053dd6b560SMauro Carvalho Chehab 		const struct i2c_vbi_ram_value *regs = &vbi_ram_default[j];
6063dd6b560SMauro Carvalho Chehab 
6073dd6b560SMauro Carvalho Chehab 		if (!regs->type.vbi_type)
6083dd6b560SMauro Carvalho Chehab 			continue;
6093dd6b560SMauro Carvalho Chehab 
610cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
611cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
612cb7a01acSMauro Carvalho Chehab 
613cb7a01acSMauro Carvalho Chehab 		for (i = 0; i < 16; i++)
614cb7a01acSMauro Carvalho Chehab 			tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
615cb7a01acSMauro Carvalho Chehab 	}
616cb7a01acSMauro Carvalho Chehab 	return 0;
617cb7a01acSMauro Carvalho Chehab }
618cb7a01acSMauro Carvalho Chehab 
619cb7a01acSMauro Carvalho Chehab /* Fills VBI capabilities based on i2c_vbi_ram_value struct */
620cb7a01acSMauro Carvalho Chehab static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
621cb7a01acSMauro Carvalho Chehab 				struct v4l2_sliced_vbi_cap *cap)
622cb7a01acSMauro Carvalho Chehab {
6233dd6b560SMauro Carvalho Chehab 	int line, i;
624cb7a01acSMauro Carvalho Chehab 
625257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n");
626cb7a01acSMauro Carvalho Chehab 	memset(cap, 0, sizeof *cap);
627cb7a01acSMauro Carvalho Chehab 
6283dd6b560SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
6293dd6b560SMauro Carvalho Chehab 		const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i];
6303dd6b560SMauro Carvalho Chehab 
6313dd6b560SMauro Carvalho Chehab 		if (!regs->type.vbi_type)
6323dd6b560SMauro Carvalho Chehab 			continue;
6333dd6b560SMauro Carvalho Chehab 
6343dd6b560SMauro Carvalho Chehab 		for (line = regs->type.ini_line;
6353dd6b560SMauro Carvalho Chehab 		     line <= regs->type.end_line;
6363dd6b560SMauro Carvalho Chehab 		     line++) {
637cb7a01acSMauro Carvalho Chehab 			cap->service_lines[0][line] |= regs->type.vbi_type;
638cb7a01acSMauro Carvalho Chehab 		}
639cb7a01acSMauro Carvalho Chehab 		cap->service_set |= regs->type.vbi_type;
640cb7a01acSMauro Carvalho Chehab 	}
641cb7a01acSMauro Carvalho Chehab 	return 0;
642cb7a01acSMauro Carvalho Chehab }
643cb7a01acSMauro Carvalho Chehab 
644cb7a01acSMauro Carvalho Chehab /* Set vbi processing
645cb7a01acSMauro Carvalho Chehab  * type - one of tvp5150_vbi_types
646cb7a01acSMauro Carvalho Chehab  * line - line to gather data
647cb7a01acSMauro Carvalho Chehab  * fields: bit 0 field1, bit 1, field2
648cb7a01acSMauro Carvalho Chehab  * flags (default=0xf0) is a bitmask, were set means:
649cb7a01acSMauro Carvalho Chehab  *	bit 7: enable filtering null bytes on CC
650cb7a01acSMauro Carvalho Chehab  *	bit 6: send data also to FIFO
651cb7a01acSMauro Carvalho Chehab  *	bit 5: don't allow data with errors on FIFO
652cb7a01acSMauro Carvalho Chehab  *	bit 4: enable ECC when possible
653cb7a01acSMauro Carvalho Chehab  * pix_align = pix alignment:
654cb7a01acSMauro Carvalho Chehab  *	LSB = field1
655cb7a01acSMauro Carvalho Chehab  *	MSB = field2
656cb7a01acSMauro Carvalho Chehab  */
657cb7a01acSMauro Carvalho Chehab static int tvp5150_set_vbi(struct v4l2_subdev *sd,
658cb7a01acSMauro Carvalho Chehab 			unsigned int type,u8 flags, int line,
659cb7a01acSMauro Carvalho Chehab 			const int fields)
660cb7a01acSMauro Carvalho Chehab {
661cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
662cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std = decoder->norm;
663cb7a01acSMauro Carvalho Chehab 	u8 reg;
6643dd6b560SMauro Carvalho Chehab 	int i, pos = 0;
665cb7a01acSMauro Carvalho Chehab 
666cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_ALL) {
667257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
668cb7a01acSMauro Carvalho Chehab 		return 0;
669cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_625_50) {
670cb7a01acSMauro Carvalho Chehab 		/* Don't follow NTSC Line number convension */
671cb7a01acSMauro Carvalho Chehab 		line += 3;
672cb7a01acSMauro Carvalho Chehab 	}
673cb7a01acSMauro Carvalho Chehab 
674cb7a01acSMauro Carvalho Chehab 	if (line < 6 || line > 27)
675cb7a01acSMauro Carvalho Chehab 		return 0;
676cb7a01acSMauro Carvalho Chehab 
6773dd6b560SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
6783dd6b560SMauro Carvalho Chehab 		const struct i2c_vbi_ram_value *regs =  &vbi_ram_default[i];
6793dd6b560SMauro Carvalho Chehab 
6803dd6b560SMauro Carvalho Chehab 		if (!regs->type.vbi_type)
6813dd6b560SMauro Carvalho Chehab 			continue;
6823dd6b560SMauro Carvalho Chehab 
683cb7a01acSMauro Carvalho Chehab 		if ((type & regs->type.vbi_type) &&
684cb7a01acSMauro Carvalho Chehab 		    (line >= regs->type.ini_line) &&
685b3d930aaSGustavo A. R. Silva 		    (line <= regs->type.end_line))
686cb7a01acSMauro Carvalho Chehab 			break;
687cb7a01acSMauro Carvalho Chehab 		pos++;
688cb7a01acSMauro Carvalho Chehab 	}
689b3d930aaSGustavo A. R. Silva 
690cb7a01acSMauro Carvalho Chehab 	type = pos | (flags & 0xf0);
691cb7a01acSMauro Carvalho Chehab 	reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
692cb7a01acSMauro Carvalho Chehab 
693b3d930aaSGustavo A. R. Silva 	if (fields & 1)
694cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, reg, type);
695cb7a01acSMauro Carvalho Chehab 
696b3d930aaSGustavo A. R. Silva 	if (fields & 2)
697cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, reg + 1, type);
698cb7a01acSMauro Carvalho Chehab 
699cb7a01acSMauro Carvalho Chehab 	return type;
700cb7a01acSMauro Carvalho Chehab }
701cb7a01acSMauro Carvalho Chehab 
7023dd6b560SMauro Carvalho Chehab static int tvp5150_get_vbi(struct v4l2_subdev *sd, int line)
703cb7a01acSMauro Carvalho Chehab {
704cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
705cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std = decoder->norm;
706cb7a01acSMauro Carvalho Chehab 	u8 reg;
707cb7a01acSMauro Carvalho Chehab 	int pos, type = 0;
708cb7a01acSMauro Carvalho Chehab 	int i, ret = 0;
709cb7a01acSMauro Carvalho Chehab 
710cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_ALL) {
711257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
712cb7a01acSMauro Carvalho Chehab 		return 0;
713cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_625_50) {
714cb7a01acSMauro Carvalho Chehab 		/* Don't follow NTSC Line number convension */
715cb7a01acSMauro Carvalho Chehab 		line += 3;
716cb7a01acSMauro Carvalho Chehab 	}
717cb7a01acSMauro Carvalho Chehab 
718cb7a01acSMauro Carvalho Chehab 	if (line < 6 || line > 27)
719cb7a01acSMauro Carvalho Chehab 		return 0;
720cb7a01acSMauro Carvalho Chehab 
721cb7a01acSMauro Carvalho Chehab 	reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
722cb7a01acSMauro Carvalho Chehab 
723cb7a01acSMauro Carvalho Chehab 	for (i = 0; i <= 1; i++) {
724cb7a01acSMauro Carvalho Chehab 		ret = tvp5150_read(sd, reg + i);
725cb7a01acSMauro Carvalho Chehab 		if (ret < 0) {
726257e29f8SMauro Carvalho Chehab 			dev_err(sd->dev, "%s: failed with error = %d\n",
727cb7a01acSMauro Carvalho Chehab 				 __func__, ret);
728cb7a01acSMauro Carvalho Chehab 			return 0;
729cb7a01acSMauro Carvalho Chehab 		}
730cb7a01acSMauro Carvalho Chehab 		pos = ret & 0x0f;
7313dd6b560SMauro Carvalho Chehab 		if (pos < ARRAY_SIZE(vbi_ram_default))
7323dd6b560SMauro Carvalho Chehab 			type |= vbi_ram_default[pos].type.vbi_type;
733cb7a01acSMauro Carvalho Chehab 	}
734cb7a01acSMauro Carvalho Chehab 
735cb7a01acSMauro Carvalho Chehab 	return type;
736cb7a01acSMauro Carvalho Chehab }
737cb7a01acSMauro Carvalho Chehab 
738cb7a01acSMauro Carvalho Chehab static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
739cb7a01acSMauro Carvalho Chehab {
740cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
741cb7a01acSMauro Carvalho Chehab 	int fmt = 0;
742cb7a01acSMauro Carvalho Chehab 
743cb7a01acSMauro Carvalho Chehab 	decoder->norm = std;
744cb7a01acSMauro Carvalho Chehab 
745cb7a01acSMauro Carvalho Chehab 	/* First tests should be against specific std */
746cb7a01acSMauro Carvalho Chehab 
74726811ae0SHans Verkuil 	if (std == V4L2_STD_NTSC_443) {
748cb7a01acSMauro Carvalho Chehab 		fmt = VIDEO_STD_NTSC_4_43_BIT;
74926811ae0SHans Verkuil 	} else if (std == V4L2_STD_PAL_M) {
750cb7a01acSMauro Carvalho Chehab 		fmt = VIDEO_STD_PAL_M_BIT;
75126811ae0SHans Verkuil 	} else if (std == V4L2_STD_PAL_N || std == V4L2_STD_PAL_Nc) {
752cb7a01acSMauro Carvalho Chehab 		fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
753cb7a01acSMauro Carvalho Chehab 	} else {
754cb7a01acSMauro Carvalho Chehab 		/* Then, test against generic ones */
755cb7a01acSMauro Carvalho Chehab 		if (std & V4L2_STD_NTSC)
756cb7a01acSMauro Carvalho Chehab 			fmt = VIDEO_STD_NTSC_MJ_BIT;
757cb7a01acSMauro Carvalho Chehab 		else if (std & V4L2_STD_PAL)
758cb7a01acSMauro Carvalho Chehab 			fmt = VIDEO_STD_PAL_BDGHIN_BIT;
759cb7a01acSMauro Carvalho Chehab 		else if (std & V4L2_STD_SECAM)
760cb7a01acSMauro Carvalho Chehab 			fmt = VIDEO_STD_SECAM_BIT;
761cb7a01acSMauro Carvalho Chehab 	}
762cb7a01acSMauro Carvalho Chehab 
763257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "Set video std register to %d.\n", fmt);
764cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
765cb7a01acSMauro Carvalho Chehab 	return 0;
766cb7a01acSMauro Carvalho Chehab }
767cb7a01acSMauro Carvalho Chehab 
768cb7a01acSMauro Carvalho Chehab static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
769cb7a01acSMauro Carvalho Chehab {
770cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
771cb7a01acSMauro Carvalho Chehab 
772cb7a01acSMauro Carvalho Chehab 	if (decoder->norm == std)
773cb7a01acSMauro Carvalho Chehab 		return 0;
774cb7a01acSMauro Carvalho Chehab 
775cb7a01acSMauro Carvalho Chehab 	/* Change cropping height limits */
776cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_525_60)
777cb7a01acSMauro Carvalho Chehab 		decoder->rect.height = TVP5150_V_MAX_525_60;
778cb7a01acSMauro Carvalho Chehab 	else
779cb7a01acSMauro Carvalho Chehab 		decoder->rect.height = TVP5150_V_MAX_OTHERS;
780cb7a01acSMauro Carvalho Chehab 
781cb7a01acSMauro Carvalho Chehab 
782cb7a01acSMauro Carvalho Chehab 	return tvp5150_set_std(sd, std);
783cb7a01acSMauro Carvalho Chehab }
784cb7a01acSMauro Carvalho Chehab 
785cb7a01acSMauro Carvalho Chehab static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
786cb7a01acSMauro Carvalho Chehab {
787cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
788cb7a01acSMauro Carvalho Chehab 
789cb7a01acSMauro Carvalho Chehab 	/* Initializes TVP5150 to its default values */
790cb7a01acSMauro Carvalho Chehab 	tvp5150_write_inittab(sd, tvp5150_init_default);
791cb7a01acSMauro Carvalho Chehab 
792cb7a01acSMauro Carvalho Chehab 	/* Initializes VDP registers */
7933dd6b560SMauro Carvalho Chehab 	tvp5150_vdp_init(sd);
794cb7a01acSMauro Carvalho Chehab 
795cb7a01acSMauro Carvalho Chehab 	/* Selects decoder input */
796cb7a01acSMauro Carvalho Chehab 	tvp5150_selmux(sd);
797cb7a01acSMauro Carvalho Chehab 
798cb7a01acSMauro Carvalho Chehab 	/* Initializes TVP5150 to stream enabled values */
799cb7a01acSMauro Carvalho Chehab 	tvp5150_write_inittab(sd, tvp5150_init_enable);
800cb7a01acSMauro Carvalho Chehab 
801cb7a01acSMauro Carvalho Chehab 	/* Initialize image preferences */
802cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&decoder->hdl);
803cb7a01acSMauro Carvalho Chehab 
804cb7a01acSMauro Carvalho Chehab 	tvp5150_set_std(sd, decoder->norm);
805a2e5f1b3SJavier Martinez Canillas 
806a2e5f1b3SJavier Martinez Canillas 	if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
807a2e5f1b3SJavier Martinez Canillas 		tvp5150_write(sd, TVP5150_DATA_RATE_SEL, 0x40);
808a2e5f1b3SJavier Martinez Canillas 
809cb7a01acSMauro Carvalho Chehab 	return 0;
810cb7a01acSMauro Carvalho Chehab };
811cb7a01acSMauro Carvalho Chehab 
812cb7a01acSMauro Carvalho Chehab static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
813cb7a01acSMauro Carvalho Chehab {
814cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
815c43875f6SMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
816cb7a01acSMauro Carvalho Chehab 
817cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
818cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
819cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
820cb7a01acSMauro Carvalho Chehab 		return 0;
821cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
822cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
823cb7a01acSMauro Carvalho Chehab 		return 0;
824cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
825cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
826cb7a01acSMauro Carvalho Chehab 		return 0;
827cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
828cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
829d183e4efSLaurent Pinchart 		break;
830c43875f6SMauro Carvalho Chehab 	case V4L2_CID_TEST_PATTERN:
831c43875f6SMauro Carvalho Chehab 		decoder->enable = ctrl->val ? false : true;
832c43875f6SMauro Carvalho Chehab 		tvp5150_selmux(sd);
833cb7a01acSMauro Carvalho Chehab 		return 0;
834cb7a01acSMauro Carvalho Chehab 	}
835cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
836cb7a01acSMauro Carvalho Chehab }
837cb7a01acSMauro Carvalho Chehab 
838cb7a01acSMauro Carvalho Chehab static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
839cb7a01acSMauro Carvalho Chehab {
840cb7a01acSMauro Carvalho Chehab 	int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
841cb7a01acSMauro Carvalho Chehab 
842cb7a01acSMauro Carvalho Chehab 	switch (val & 0x0F) {
843cb7a01acSMauro Carvalho Chehab 	case 0x01:
844cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
845cb7a01acSMauro Carvalho Chehab 	case 0x03:
846cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
847cb7a01acSMauro Carvalho Chehab 	case 0x05:
848cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
849cb7a01acSMauro Carvalho Chehab 	case 0x07:
850cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
851cb7a01acSMauro Carvalho Chehab 	case 0x09:
852cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
853cb7a01acSMauro Carvalho Chehab 	case 0xb:
854cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
855cb7a01acSMauro Carvalho Chehab 	default:
856cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
857cb7a01acSMauro Carvalho Chehab 	}
858cb7a01acSMauro Carvalho Chehab }
859cb7a01acSMauro Carvalho Chehab 
860da298c6dSHans Verkuil static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
861da298c6dSHans Verkuil 		struct v4l2_subdev_pad_config *cfg,
862da298c6dSHans Verkuil 		struct v4l2_subdev_format *format)
863cb7a01acSMauro Carvalho Chehab {
864da298c6dSHans Verkuil 	struct v4l2_mbus_framefmt *f;
865cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
866cb7a01acSMauro Carvalho Chehab 
867f6f0e2f5SPhilipp Zabel 	if (!format || (format->pad != DEMOD_PAD_VID_OUT))
868cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
869cb7a01acSMauro Carvalho Chehab 
870da298c6dSHans Verkuil 	f = &format->format;
871da298c6dSHans Verkuil 
872cb7a01acSMauro Carvalho Chehab 	f->width = decoder->rect.width;
8730866df8dSPhilipp Zabel 	f->height = decoder->rect.height;
874cb7a01acSMauro Carvalho Chehab 
875f5fe58fdSBoris BREZILLON 	f->code = MEDIA_BUS_FMT_UYVY8_2X8;
8764f57d27bSLaurent Pinchart 	f->field = V4L2_FIELD_ALTERNATE;
877cb7a01acSMauro Carvalho Chehab 	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
878cb7a01acSMauro Carvalho Chehab 
879257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "width = %d, height = %d\n", f->width,
880cb7a01acSMauro Carvalho Chehab 			f->height);
881cb7a01acSMauro Carvalho Chehab 	return 0;
882cb7a01acSMauro Carvalho Chehab }
883cb7a01acSMauro Carvalho Chehab 
88410d5509cSHans Verkuil static int tvp5150_set_selection(struct v4l2_subdev *sd,
88510d5509cSHans Verkuil 				 struct v4l2_subdev_pad_config *cfg,
88610d5509cSHans Verkuil 				 struct v4l2_subdev_selection *sel)
887cb7a01acSMauro Carvalho Chehab {
888cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
88910d5509cSHans Verkuil 	struct v4l2_rect rect = sel->r;
890cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std;
89110d5509cSHans Verkuil 	int hmax;
89210d5509cSHans Verkuil 
89310d5509cSHans Verkuil 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
89410d5509cSHans Verkuil 	    sel->target != V4L2_SEL_TGT_CROP)
89510d5509cSHans Verkuil 		return -EINVAL;
896cb7a01acSMauro Carvalho Chehab 
897257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
898cb7a01acSMauro Carvalho Chehab 		__func__, rect.left, rect.top, rect.width, rect.height);
899cb7a01acSMauro Carvalho Chehab 
900cb7a01acSMauro Carvalho Chehab 	/* tvp5150 has some special limits */
901cb7a01acSMauro Carvalho Chehab 	rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
902f90580caSRicardo Ribalda 	rect.width = clamp_t(unsigned int, rect.width,
903cb7a01acSMauro Carvalho Chehab 			     TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
904cb7a01acSMauro Carvalho Chehab 			     TVP5150_H_MAX - rect.left);
905cb7a01acSMauro Carvalho Chehab 	rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
906cb7a01acSMauro Carvalho Chehab 
907cb7a01acSMauro Carvalho Chehab 	/* Calculate height based on current standard */
908cb7a01acSMauro Carvalho Chehab 	if (decoder->norm == V4L2_STD_ALL)
909cb7a01acSMauro Carvalho Chehab 		std = tvp5150_read_std(sd);
910cb7a01acSMauro Carvalho Chehab 	else
911cb7a01acSMauro Carvalho Chehab 		std = decoder->norm;
912cb7a01acSMauro Carvalho Chehab 
913cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_525_60)
914cb7a01acSMauro Carvalho Chehab 		hmax = TVP5150_V_MAX_525_60;
915cb7a01acSMauro Carvalho Chehab 	else
916cb7a01acSMauro Carvalho Chehab 		hmax = TVP5150_V_MAX_OTHERS;
917cb7a01acSMauro Carvalho Chehab 
918f90580caSRicardo Ribalda 	rect.height = clamp_t(unsigned int, rect.height,
919cb7a01acSMauro Carvalho Chehab 			      hmax - TVP5150_MAX_CROP_TOP - rect.top,
920cb7a01acSMauro Carvalho Chehab 			      hmax - rect.top);
921cb7a01acSMauro Carvalho Chehab 
922cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
923cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
924cb7a01acSMauro Carvalho Chehab 		      rect.top + rect.height - hmax);
925cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
926cb7a01acSMauro Carvalho Chehab 		      rect.left >> TVP5150_CROP_SHIFT);
927cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
928cb7a01acSMauro Carvalho Chehab 		      rect.left | (1 << TVP5150_CROP_SHIFT));
929cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
930cb7a01acSMauro Carvalho Chehab 		      (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
931cb7a01acSMauro Carvalho Chehab 		      TVP5150_CROP_SHIFT);
932cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
933cb7a01acSMauro Carvalho Chehab 		      rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
934cb7a01acSMauro Carvalho Chehab 
935cb7a01acSMauro Carvalho Chehab 	decoder->rect = rect;
936cb7a01acSMauro Carvalho Chehab 
937cb7a01acSMauro Carvalho Chehab 	return 0;
938cb7a01acSMauro Carvalho Chehab }
939cb7a01acSMauro Carvalho Chehab 
94010d5509cSHans Verkuil static int tvp5150_get_selection(struct v4l2_subdev *sd,
94110d5509cSHans Verkuil 				 struct v4l2_subdev_pad_config *cfg,
94210d5509cSHans Verkuil 				 struct v4l2_subdev_selection *sel)
943cb7a01acSMauro Carvalho Chehab {
94410d5509cSHans Verkuil 	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
945cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std;
946cb7a01acSMauro Carvalho Chehab 
94710d5509cSHans Verkuil 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
948cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
949cb7a01acSMauro Carvalho Chehab 
95010d5509cSHans Verkuil 	switch (sel->target) {
95110d5509cSHans Verkuil 	case V4L2_SEL_TGT_CROP_BOUNDS:
95210d5509cSHans Verkuil 	case V4L2_SEL_TGT_CROP_DEFAULT:
95310d5509cSHans Verkuil 		sel->r.left = 0;
95410d5509cSHans Verkuil 		sel->r.top = 0;
95510d5509cSHans Verkuil 		sel->r.width = TVP5150_H_MAX;
956cb7a01acSMauro Carvalho Chehab 
957cb7a01acSMauro Carvalho Chehab 		/* Calculate height based on current standard */
958cb7a01acSMauro Carvalho Chehab 		if (decoder->norm == V4L2_STD_ALL)
959cb7a01acSMauro Carvalho Chehab 			std = tvp5150_read_std(sd);
960cb7a01acSMauro Carvalho Chehab 		else
961cb7a01acSMauro Carvalho Chehab 			std = decoder->norm;
962cb7a01acSMauro Carvalho Chehab 		if (std & V4L2_STD_525_60)
96310d5509cSHans Verkuil 			sel->r.height = TVP5150_V_MAX_525_60;
964cb7a01acSMauro Carvalho Chehab 		else
96510d5509cSHans Verkuil 			sel->r.height = TVP5150_V_MAX_OTHERS;
966cb7a01acSMauro Carvalho Chehab 		return 0;
96710d5509cSHans Verkuil 	case V4L2_SEL_TGT_CROP:
96810d5509cSHans Verkuil 		sel->r = decoder->rect;
96910d5509cSHans Verkuil 		return 0;
97010d5509cSHans Verkuil 	default:
97110d5509cSHans Verkuil 		return -EINVAL;
97210d5509cSHans Verkuil 	}
973cb7a01acSMauro Carvalho Chehab }
974cb7a01acSMauro Carvalho Chehab 
975dd3a46bbSLaurent Pinchart static int tvp5150_g_mbus_config(struct v4l2_subdev *sd,
976dd3a46bbSLaurent Pinchart 				 struct v4l2_mbus_config *cfg)
977dd3a46bbSLaurent Pinchart {
978a2e5f1b3SJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
979a2e5f1b3SJavier Martinez Canillas 
980a2e5f1b3SJavier Martinez Canillas 	cfg->type = decoder->mbus_type;
981dd3a46bbSLaurent Pinchart 	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING
982dd3a46bbSLaurent Pinchart 		   | V4L2_MBUS_FIELD_EVEN_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH;
983dd3a46bbSLaurent Pinchart 
984dd3a46bbSLaurent Pinchart 	return 0;
985dd3a46bbSLaurent Pinchart }
986dd3a46bbSLaurent Pinchart 
987cb7a01acSMauro Carvalho Chehab /****************************************************************************
988e545ac87SLaurent Pinchart 			V4L2 subdev pad ops
989e545ac87SLaurent Pinchart  ****************************************************************************/
990e545ac87SLaurent Pinchart static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
991e545ac87SLaurent Pinchart 		struct v4l2_subdev_pad_config *cfg,
992e545ac87SLaurent Pinchart 		struct v4l2_subdev_mbus_code_enum *code)
993e545ac87SLaurent Pinchart {
994e545ac87SLaurent Pinchart 	if (code->pad || code->index)
995e545ac87SLaurent Pinchart 		return -EINVAL;
996e545ac87SLaurent Pinchart 
997e545ac87SLaurent Pinchart 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
998e545ac87SLaurent Pinchart 	return 0;
999e545ac87SLaurent Pinchart }
1000e545ac87SLaurent Pinchart 
1001e545ac87SLaurent Pinchart static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
1002e545ac87SLaurent Pinchart 				   struct v4l2_subdev_pad_config *cfg,
1003e545ac87SLaurent Pinchart 				   struct v4l2_subdev_frame_size_enum *fse)
1004e545ac87SLaurent Pinchart {
1005e545ac87SLaurent Pinchart 	struct tvp5150 *decoder = to_tvp5150(sd);
1006e545ac87SLaurent Pinchart 
1007e545ac87SLaurent Pinchart 	if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_UYVY8_2X8)
1008e545ac87SLaurent Pinchart 		return -EINVAL;
1009e545ac87SLaurent Pinchart 
1010e545ac87SLaurent Pinchart 	fse->code = MEDIA_BUS_FMT_UYVY8_2X8;
1011e545ac87SLaurent Pinchart 	fse->min_width = decoder->rect.width;
1012e545ac87SLaurent Pinchart 	fse->max_width = decoder->rect.width;
1013e545ac87SLaurent Pinchart 	fse->min_height = decoder->rect.height / 2;
1014e545ac87SLaurent Pinchart 	fse->max_height = decoder->rect.height / 2;
1015e545ac87SLaurent Pinchart 
1016e545ac87SLaurent Pinchart 	return 0;
1017e545ac87SLaurent Pinchart }
1018e545ac87SLaurent Pinchart 
1019e545ac87SLaurent Pinchart /****************************************************************************
1020f7b4b54eSJavier Martinez Canillas 			Media entity ops
1021f7b4b54eSJavier Martinez Canillas  ****************************************************************************/
1022f7b4b54eSJavier Martinez Canillas 
1023406ff67dSLaurent Pinchart #ifdef CONFIG_MEDIA_CONTROLLER
1024f7b4b54eSJavier Martinez Canillas static int tvp5150_link_setup(struct media_entity *entity,
1025f7b4b54eSJavier Martinez Canillas 			      const struct media_pad *local,
1026f7b4b54eSJavier Martinez Canillas 			      const struct media_pad *remote, u32 flags)
1027f7b4b54eSJavier Martinez Canillas {
1028f7b4b54eSJavier Martinez Canillas 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
1029f7b4b54eSJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
1030f7b4b54eSJavier Martinez Canillas 	int i;
1031f7b4b54eSJavier Martinez Canillas 
1032f7b4b54eSJavier Martinez Canillas 	for (i = 0; i < TVP5150_INPUT_NUM; i++) {
1033f7b4b54eSJavier Martinez Canillas 		if (remote->entity == &decoder->input_ent[i])
1034f7b4b54eSJavier Martinez Canillas 			break;
1035f7b4b54eSJavier Martinez Canillas 	}
1036f7b4b54eSJavier Martinez Canillas 
1037f7b4b54eSJavier Martinez Canillas 	/* Do nothing for entities that are not input connectors */
1038f7b4b54eSJavier Martinez Canillas 	if (i == TVP5150_INPUT_NUM)
1039f7b4b54eSJavier Martinez Canillas 		return 0;
1040f7b4b54eSJavier Martinez Canillas 
1041f7b4b54eSJavier Martinez Canillas 	decoder->input = i;
1042f7b4b54eSJavier Martinez Canillas 
1043f7b4b54eSJavier Martinez Canillas 	tvp5150_selmux(sd);
1044f7b4b54eSJavier Martinez Canillas 
1045f7b4b54eSJavier Martinez Canillas 	return 0;
1046f7b4b54eSJavier Martinez Canillas }
1047f7b4b54eSJavier Martinez Canillas 
1048f7b4b54eSJavier Martinez Canillas static const struct media_entity_operations tvp5150_sd_media_ops = {
1049f7b4b54eSJavier Martinez Canillas 	.link_setup = tvp5150_link_setup,
1050f7b4b54eSJavier Martinez Canillas };
1051406ff67dSLaurent Pinchart #endif
1052f7b4b54eSJavier Martinez Canillas 
1053f7b4b54eSJavier Martinez Canillas /****************************************************************************
1054cb7a01acSMauro Carvalho Chehab 			I2C Command
1055cb7a01acSMauro Carvalho Chehab  ****************************************************************************/
1056cb7a01acSMauro Carvalho Chehab 
1057460b6c08SLaurent Pinchart static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
1058460b6c08SLaurent Pinchart {
1059a2e5f1b3SJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
106079d6205aSLaurent Pinchart 	int val;
1061a2e5f1b3SJavier Martinez Canillas 
106279d6205aSLaurent Pinchart 	/* Enable or disable the video output signals. */
106379d6205aSLaurent Pinchart 	val = tvp5150_read(sd, TVP5150_MISC_CTL);
106479d6205aSLaurent Pinchart 	if (val < 0)
106579d6205aSLaurent Pinchart 		return val;
106679d6205aSLaurent Pinchart 
106779d6205aSLaurent Pinchart 	val &= ~(TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE |
106879d6205aSLaurent Pinchart 		 TVP5150_MISC_CTL_CLOCK_OE);
106979d6205aSLaurent Pinchart 
107079d6205aSLaurent Pinchart 	if (enable) {
107179d6205aSLaurent Pinchart 		/*
107279d6205aSLaurent Pinchart 		 * Enable the YCbCr and clock outputs. In discrete sync mode
107379d6205aSLaurent Pinchart 		 * (non-BT.656) additionally enable the the sync outputs.
107479d6205aSLaurent Pinchart 		 */
107579d6205aSLaurent Pinchart 		val |= TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE;
1076841502d7SMauro Carvalho Chehab 		if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
107779d6205aSLaurent Pinchart 			val |= TVP5150_MISC_CTL_SYNC_OE;
107879d6205aSLaurent Pinchart 	}
1079a2e5f1b3SJavier Martinez Canillas 
1080841502d7SMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_MISC_CTL, val);
1081460b6c08SLaurent Pinchart 
1082460b6c08SLaurent Pinchart 	return 0;
1083460b6c08SLaurent Pinchart }
1084460b6c08SLaurent Pinchart 
1085cb7a01acSMauro Carvalho Chehab static int tvp5150_s_routing(struct v4l2_subdev *sd,
1086cb7a01acSMauro Carvalho Chehab 			     u32 input, u32 output, u32 config)
1087cb7a01acSMauro Carvalho Chehab {
1088cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
1089cb7a01acSMauro Carvalho Chehab 
1090cb7a01acSMauro Carvalho Chehab 	decoder->input = input;
1091cb7a01acSMauro Carvalho Chehab 	decoder->output = output;
1092c43875f6SMauro Carvalho Chehab 
1093c43875f6SMauro Carvalho Chehab 	if (output == TVP5150_BLACK_SCREEN)
1094c43875f6SMauro Carvalho Chehab 		decoder->enable = false;
1095c43875f6SMauro Carvalho Chehab 	else
1096c43875f6SMauro Carvalho Chehab 		decoder->enable = true;
1097c43875f6SMauro Carvalho Chehab 
1098cb7a01acSMauro Carvalho Chehab 	tvp5150_selmux(sd);
1099cb7a01acSMauro Carvalho Chehab 	return 0;
1100cb7a01acSMauro Carvalho Chehab }
1101cb7a01acSMauro Carvalho Chehab 
1102cb7a01acSMauro Carvalho Chehab static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
1103cb7a01acSMauro Carvalho Chehab {
1104cb7a01acSMauro Carvalho Chehab 	/* this is for capturing 36 raw vbi lines
1105cb7a01acSMauro Carvalho Chehab 	   if there's a way to cut off the beginning 2 vbi lines
1106cb7a01acSMauro Carvalho Chehab 	   with the tvp5150 then the vbi line count could be lowered
1107cb7a01acSMauro Carvalho Chehab 	   to 17 lines/field again, although I couldn't find a register
1108cb7a01acSMauro Carvalho Chehab 	   which could do that cropping */
1109cb7a01acSMauro Carvalho Chehab 	if (fmt->sample_format == V4L2_PIX_FMT_GREY)
1110cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
1111cb7a01acSMauro Carvalho Chehab 	if (fmt->count[0] == 18 && fmt->count[1] == 18) {
1112cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
1113cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
1114cb7a01acSMauro Carvalho Chehab 	}
1115cb7a01acSMauro Carvalho Chehab 	return 0;
1116cb7a01acSMauro Carvalho Chehab }
1117cb7a01acSMauro Carvalho Chehab 
1118cb7a01acSMauro Carvalho Chehab static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1119cb7a01acSMauro Carvalho Chehab {
1120cb7a01acSMauro Carvalho Chehab 	int i;
1121cb7a01acSMauro Carvalho Chehab 
1122cb7a01acSMauro Carvalho Chehab 	if (svbi->service_set != 0) {
1123cb7a01acSMauro Carvalho Chehab 		for (i = 0; i <= 23; i++) {
1124cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[1][i] = 0;
1125cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] =
11263dd6b560SMauro Carvalho Chehab 				tvp5150_set_vbi(sd, svbi->service_lines[0][i],
11273dd6b560SMauro Carvalho Chehab 						0xf0, i, 3);
1128cb7a01acSMauro Carvalho Chehab 		}
1129cb7a01acSMauro Carvalho Chehab 		/* Enables FIFO */
1130cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
1131cb7a01acSMauro Carvalho Chehab 	} else {
1132cb7a01acSMauro Carvalho Chehab 		/* Disables FIFO*/
1133cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
1134cb7a01acSMauro Carvalho Chehab 
1135cb7a01acSMauro Carvalho Chehab 		/* Disable Full Field */
1136cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
1137cb7a01acSMauro Carvalho Chehab 
1138cb7a01acSMauro Carvalho Chehab 		/* Disable Line modes */
1139cb7a01acSMauro Carvalho Chehab 		for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
1140cb7a01acSMauro Carvalho Chehab 			tvp5150_write(sd, i, 0xff);
1141cb7a01acSMauro Carvalho Chehab 	}
1142cb7a01acSMauro Carvalho Chehab 	return 0;
1143cb7a01acSMauro Carvalho Chehab }
1144cb7a01acSMauro Carvalho Chehab 
1145cb7a01acSMauro Carvalho Chehab static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1146cb7a01acSMauro Carvalho Chehab {
1147cb7a01acSMauro Carvalho Chehab 	int i, mask = 0;
1148cb7a01acSMauro Carvalho Chehab 
114930634e8eSHans Verkuil 	memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
1150cb7a01acSMauro Carvalho Chehab 
1151cb7a01acSMauro Carvalho Chehab 	for (i = 0; i <= 23; i++) {
1152cb7a01acSMauro Carvalho Chehab 		svbi->service_lines[0][i] =
11533dd6b560SMauro Carvalho Chehab 			tvp5150_get_vbi(sd, i);
1154cb7a01acSMauro Carvalho Chehab 		mask |= svbi->service_lines[0][i];
1155cb7a01acSMauro Carvalho Chehab 	}
1156cb7a01acSMauro Carvalho Chehab 	svbi->service_set = mask;
1157cb7a01acSMauro Carvalho Chehab 	return 0;
1158cb7a01acSMauro Carvalho Chehab }
1159cb7a01acSMauro Carvalho Chehab 
1160cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1161cb7a01acSMauro Carvalho Chehab static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1162cb7a01acSMauro Carvalho Chehab {
1163cb7a01acSMauro Carvalho Chehab 	int res;
1164cb7a01acSMauro Carvalho Chehab 
1165cb7a01acSMauro Carvalho Chehab 	res = tvp5150_read(sd, reg->reg & 0xff);
1166cb7a01acSMauro Carvalho Chehab 	if (res < 0) {
1167257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "%s: failed with error = %d\n", __func__, res);
1168cb7a01acSMauro Carvalho Chehab 		return res;
1169cb7a01acSMauro Carvalho Chehab 	}
1170cb7a01acSMauro Carvalho Chehab 
1171cb7a01acSMauro Carvalho Chehab 	reg->val = res;
1172cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
1173cb7a01acSMauro Carvalho Chehab 	return 0;
1174cb7a01acSMauro Carvalho Chehab }
1175cb7a01acSMauro Carvalho Chehab 
1176977ba3b1SHans Verkuil static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
1177cb7a01acSMauro Carvalho Chehab {
1178eca4ca84SJavier Martinez Canillas 	return tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
1179cb7a01acSMauro Carvalho Chehab }
1180cb7a01acSMauro Carvalho Chehab #endif
1181cb7a01acSMauro Carvalho Chehab 
1182cb7a01acSMauro Carvalho Chehab static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
1183cb7a01acSMauro Carvalho Chehab {
1184cb7a01acSMauro Carvalho Chehab 	int status = tvp5150_read(sd, 0x88);
1185cb7a01acSMauro Carvalho Chehab 
1186cb7a01acSMauro Carvalho Chehab 	vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
1187cb7a01acSMauro Carvalho Chehab 	return 0;
1188cb7a01acSMauro Carvalho Chehab }
1189cb7a01acSMauro Carvalho Chehab 
11905a08bc00SJavier Martinez Canillas static int tvp5150_registered(struct v4l2_subdev *sd)
1191f7b4b54eSJavier Martinez Canillas {
1192f7b4b54eSJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
1193f7b4b54eSJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
1194f7b4b54eSJavier Martinez Canillas 	int ret = 0;
1195f7b4b54eSJavier Martinez Canillas 	int i;
1196f7b4b54eSJavier Martinez Canillas 
1197f7b4b54eSJavier Martinez Canillas 	for (i = 0; i < TVP5150_INPUT_NUM; i++) {
1198f7b4b54eSJavier Martinez Canillas 		struct media_entity *input = &decoder->input_ent[i];
1199f7b4b54eSJavier Martinez Canillas 		struct media_pad *pad = &decoder->input_pad[i];
1200f7b4b54eSJavier Martinez Canillas 
1201f7b4b54eSJavier Martinez Canillas 		if (!input->name)
1202f7b4b54eSJavier Martinez Canillas 			continue;
1203f7b4b54eSJavier Martinez Canillas 
1204f7b4b54eSJavier Martinez Canillas 		decoder->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
1205f7b4b54eSJavier Martinez Canillas 
1206f7b4b54eSJavier Martinez Canillas 		ret = media_entity_pads_init(input, 1, pad);
1207f7b4b54eSJavier Martinez Canillas 		if (ret < 0)
1208f7b4b54eSJavier Martinez Canillas 			return ret;
1209f7b4b54eSJavier Martinez Canillas 
1210f7b4b54eSJavier Martinez Canillas 		ret = media_device_register_entity(sd->v4l2_dev->mdev, input);
1211f7b4b54eSJavier Martinez Canillas 		if (ret < 0)
1212f7b4b54eSJavier Martinez Canillas 			return ret;
1213f7b4b54eSJavier Martinez Canillas 
1214f7b4b54eSJavier Martinez Canillas 		ret = media_create_pad_link(input, 0, &sd->entity,
1215f7b4b54eSJavier Martinez Canillas 					    DEMOD_PAD_IF_INPUT, 0);
1216f7b4b54eSJavier Martinez Canillas 		if (ret < 0) {
1217f7b4b54eSJavier Martinez Canillas 			media_device_unregister_entity(input);
1218f7b4b54eSJavier Martinez Canillas 			return ret;
1219f7b4b54eSJavier Martinez Canillas 		}
1220f7b4b54eSJavier Martinez Canillas 	}
1221f7b4b54eSJavier Martinez Canillas #endif
1222f7b4b54eSJavier Martinez Canillas 
1223f7b4b54eSJavier Martinez Canillas 	return 0;
1224f7b4b54eSJavier Martinez Canillas }
1225f7b4b54eSJavier Martinez Canillas 
1226cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1227cb7a01acSMauro Carvalho Chehab 
1228cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
1229cb7a01acSMauro Carvalho Chehab 	.s_ctrl = tvp5150_s_ctrl,
1230cb7a01acSMauro Carvalho Chehab };
1231cb7a01acSMauro Carvalho Chehab 
1232cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
1233cb7a01acSMauro Carvalho Chehab 	.log_status = tvp5150_log_status,
1234cb7a01acSMauro Carvalho Chehab 	.reset = tvp5150_reset,
1235cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1236cb7a01acSMauro Carvalho Chehab 	.g_register = tvp5150_g_register,
1237cb7a01acSMauro Carvalho Chehab 	.s_register = tvp5150_s_register,
1238cb7a01acSMauro Carvalho Chehab #endif
1239cb7a01acSMauro Carvalho Chehab };
1240cb7a01acSMauro Carvalho Chehab 
1241cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
1242cb7a01acSMauro Carvalho Chehab 	.g_tuner = tvp5150_g_tuner,
1243cb7a01acSMauro Carvalho Chehab };
1244cb7a01acSMauro Carvalho Chehab 
1245cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
12468774bed9SLaurent Pinchart 	.s_std = tvp5150_s_std,
1247460b6c08SLaurent Pinchart 	.s_stream = tvp5150_s_stream,
1248cb7a01acSMauro Carvalho Chehab 	.s_routing = tvp5150_s_routing,
1249dd3a46bbSLaurent Pinchart 	.g_mbus_config = tvp5150_g_mbus_config,
1250cb7a01acSMauro Carvalho Chehab };
1251cb7a01acSMauro Carvalho Chehab 
1252cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
1253cb7a01acSMauro Carvalho Chehab 	.g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
1254cb7a01acSMauro Carvalho Chehab 	.g_sliced_fmt = tvp5150_g_sliced_fmt,
1255cb7a01acSMauro Carvalho Chehab 	.s_sliced_fmt = tvp5150_s_sliced_fmt,
1256cb7a01acSMauro Carvalho Chehab 	.s_raw_fmt = tvp5150_s_raw_fmt,
1257cb7a01acSMauro Carvalho Chehab };
1258cb7a01acSMauro Carvalho Chehab 
1259ebcff5fcSHans Verkuil static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
1260ebcff5fcSHans Verkuil 	.enum_mbus_code = tvp5150_enum_mbus_code,
1261e545ac87SLaurent Pinchart 	.enum_frame_size = tvp5150_enum_frame_size,
1262da298c6dSHans Verkuil 	.set_fmt = tvp5150_fill_fmt,
1263da298c6dSHans Verkuil 	.get_fmt = tvp5150_fill_fmt,
126410d5509cSHans Verkuil 	.get_selection = tvp5150_get_selection,
126510d5509cSHans Verkuil 	.set_selection = tvp5150_set_selection,
1266ebcff5fcSHans Verkuil };
1267ebcff5fcSHans Verkuil 
1268cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops tvp5150_ops = {
1269cb7a01acSMauro Carvalho Chehab 	.core = &tvp5150_core_ops,
1270cb7a01acSMauro Carvalho Chehab 	.tuner = &tvp5150_tuner_ops,
1271cb7a01acSMauro Carvalho Chehab 	.video = &tvp5150_video_ops,
1272cb7a01acSMauro Carvalho Chehab 	.vbi = &tvp5150_vbi_ops,
1273ebcff5fcSHans Verkuil 	.pad = &tvp5150_pad_ops,
1274cb7a01acSMauro Carvalho Chehab };
1275cb7a01acSMauro Carvalho Chehab 
12765a08bc00SJavier Martinez Canillas static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
12775a08bc00SJavier Martinez Canillas 	.registered = tvp5150_registered,
12785a08bc00SJavier Martinez Canillas };
12795a08bc00SJavier Martinez Canillas 
1280cb7a01acSMauro Carvalho Chehab 
1281cb7a01acSMauro Carvalho Chehab /****************************************************************************
1282cb7a01acSMauro Carvalho Chehab 			I2C Client & Driver
1283cb7a01acSMauro Carvalho Chehab  ****************************************************************************/
1284cb7a01acSMauro Carvalho Chehab 
12857871597aSLaurent Pinchart static int tvp5150_detect_version(struct tvp5150 *core)
12867871597aSLaurent Pinchart {
12877871597aSLaurent Pinchart 	struct v4l2_subdev *sd = &core->sd;
12887871597aSLaurent Pinchart 	struct i2c_client *c = v4l2_get_subdevdata(sd);
12897871597aSLaurent Pinchart 	unsigned int i;
12907871597aSLaurent Pinchart 	u8 regs[4];
12917871597aSLaurent Pinchart 	int res;
12927871597aSLaurent Pinchart 
12937871597aSLaurent Pinchart 	/*
12947871597aSLaurent Pinchart 	 * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
12957871597aSLaurent Pinchart 	 * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
12967871597aSLaurent Pinchart 	 */
12977871597aSLaurent Pinchart 	for (i = 0; i < 4; i++) {
12987871597aSLaurent Pinchart 		res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
12997871597aSLaurent Pinchart 		if (res < 0)
13007871597aSLaurent Pinchart 			return res;
13017871597aSLaurent Pinchart 		regs[i] = res;
13027871597aSLaurent Pinchart 	}
13037871597aSLaurent Pinchart 
130482275133SJavier Martinez Canillas 	core->dev_id = (regs[0] << 8) | regs[1];
130582275133SJavier Martinez Canillas 	core->rom_ver = (regs[2] << 8) | regs[3];
13067871597aSLaurent Pinchart 
1307257e29f8SMauro Carvalho Chehab 	dev_info(sd->dev, "tvp%04x (%u.%u) chip found @ 0x%02x (%s)\n",
130882275133SJavier Martinez Canillas 		  core->dev_id, regs[2], regs[3], c->addr << 1,
130982275133SJavier Martinez Canillas 		  c->adapter->name);
13107871597aSLaurent Pinchart 
131182275133SJavier Martinez Canillas 	if (core->dev_id == 0x5150 && core->rom_ver == 0x0321) {
1312257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "tvp5150a detected.\n");
131382275133SJavier Martinez Canillas 	} else if (core->dev_id == 0x5150 && core->rom_ver == 0x0400) {
1314257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "tvp5150am1 detected.\n");
13157871597aSLaurent Pinchart 
13167871597aSLaurent Pinchart 		/* ITU-T BT.656.4 timing */
13177871597aSLaurent Pinchart 		tvp5150_write(sd, TVP5150_REV_SELECT, 0);
131882275133SJavier Martinez Canillas 	} else if (core->dev_id == 0x5151 && core->rom_ver == 0x0100) {
1319257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "tvp5151 detected.\n");
13207871597aSLaurent Pinchart 	} else {
1321257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "*** unknown tvp%04x chip detected.\n",
132282275133SJavier Martinez Canillas 			  core->dev_id);
13237871597aSLaurent Pinchart 	}
13247871597aSLaurent Pinchart 
13257871597aSLaurent Pinchart 	return 0;
13267871597aSLaurent Pinchart }
13277871597aSLaurent Pinchart 
132809aa2609SJavier Martinez Canillas static int tvp5150_init(struct i2c_client *c)
132909aa2609SJavier Martinez Canillas {
133009aa2609SJavier Martinez Canillas 	struct gpio_desc *pdn_gpio;
133109aa2609SJavier Martinez Canillas 	struct gpio_desc *reset_gpio;
133209aa2609SJavier Martinez Canillas 
133309aa2609SJavier Martinez Canillas 	pdn_gpio = devm_gpiod_get_optional(&c->dev, "pdn", GPIOD_OUT_HIGH);
133409aa2609SJavier Martinez Canillas 	if (IS_ERR(pdn_gpio))
133509aa2609SJavier Martinez Canillas 		return PTR_ERR(pdn_gpio);
133609aa2609SJavier Martinez Canillas 
133709aa2609SJavier Martinez Canillas 	if (pdn_gpio) {
133809aa2609SJavier Martinez Canillas 		gpiod_set_value_cansleep(pdn_gpio, 0);
133909aa2609SJavier Martinez Canillas 		/* Delay time between power supplies active and reset */
134009aa2609SJavier Martinez Canillas 		msleep(20);
134109aa2609SJavier Martinez Canillas 	}
134209aa2609SJavier Martinez Canillas 
134309aa2609SJavier Martinez Canillas 	reset_gpio = devm_gpiod_get_optional(&c->dev, "reset", GPIOD_OUT_HIGH);
134409aa2609SJavier Martinez Canillas 	if (IS_ERR(reset_gpio))
134509aa2609SJavier Martinez Canillas 		return PTR_ERR(reset_gpio);
134609aa2609SJavier Martinez Canillas 
134709aa2609SJavier Martinez Canillas 	if (reset_gpio) {
134809aa2609SJavier Martinez Canillas 		/* RESETB pulse duration */
134909aa2609SJavier Martinez Canillas 		ndelay(500);
135009aa2609SJavier Martinez Canillas 		gpiod_set_value_cansleep(reset_gpio, 0);
135109aa2609SJavier Martinez Canillas 		/* Delay time between end of reset to I2C active */
135209aa2609SJavier Martinez Canillas 		usleep_range(200, 250);
135309aa2609SJavier Martinez Canillas 	}
135409aa2609SJavier Martinez Canillas 
135509aa2609SJavier Martinez Canillas 	return 0;
135609aa2609SJavier Martinez Canillas }
135709aa2609SJavier Martinez Canillas 
1358a2e5f1b3SJavier Martinez Canillas static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
1359a2e5f1b3SJavier Martinez Canillas {
1360859969b3SSakari Ailus 	struct v4l2_fwnode_endpoint bus_cfg;
1361a2e5f1b3SJavier Martinez Canillas 	struct device_node *ep;
1362f7b4b54eSJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
1363f7b4b54eSJavier Martinez Canillas 	struct device_node *connectors, *child;
1364f7b4b54eSJavier Martinez Canillas 	struct media_entity *input;
1365f7b4b54eSJavier Martinez Canillas 	const char *name;
1366f7b4b54eSJavier Martinez Canillas 	u32 input_type;
1367f7b4b54eSJavier Martinez Canillas #endif
1368a2e5f1b3SJavier Martinez Canillas 	unsigned int flags;
1369a2e5f1b3SJavier Martinez Canillas 	int ret = 0;
1370a2e5f1b3SJavier Martinez Canillas 
1371a2e5f1b3SJavier Martinez Canillas 	ep = of_graph_get_next_endpoint(np, NULL);
1372a2e5f1b3SJavier Martinez Canillas 	if (!ep)
1373a2e5f1b3SJavier Martinez Canillas 		return -EINVAL;
1374a2e5f1b3SJavier Martinez Canillas 
1375859969b3SSakari Ailus 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
1376a2e5f1b3SJavier Martinez Canillas 	if (ret)
1377a2e5f1b3SJavier Martinez Canillas 		goto err;
1378a2e5f1b3SJavier Martinez Canillas 
1379a2e5f1b3SJavier Martinez Canillas 	flags = bus_cfg.bus.parallel.flags;
1380a2e5f1b3SJavier Martinez Canillas 
1381a2e5f1b3SJavier Martinez Canillas 	if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL &&
1382a2e5f1b3SJavier Martinez Canillas 	    !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH &&
1383a2e5f1b3SJavier Martinez Canillas 	      flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH &&
13842bd5e437SJavier Martinez Canillas 	      flags & V4L2_MBUS_FIELD_EVEN_LOW)) {
13852bd5e437SJavier Martinez Canillas 		ret = -EINVAL;
13862bd5e437SJavier Martinez Canillas 		goto err;
13872bd5e437SJavier Martinez Canillas 	}
1388a2e5f1b3SJavier Martinez Canillas 
1389a2e5f1b3SJavier Martinez Canillas 	decoder->mbus_type = bus_cfg.bus_type;
1390a2e5f1b3SJavier Martinez Canillas 
1391f7b4b54eSJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
1392f7b4b54eSJavier Martinez Canillas 	connectors = of_get_child_by_name(np, "connectors");
1393f7b4b54eSJavier Martinez Canillas 
1394f7b4b54eSJavier Martinez Canillas 	if (!connectors)
1395f7b4b54eSJavier Martinez Canillas 		goto err;
1396f7b4b54eSJavier Martinez Canillas 
1397f7b4b54eSJavier Martinez Canillas 	for_each_available_child_of_node(connectors, child) {
1398f7b4b54eSJavier Martinez Canillas 		ret = of_property_read_u32(child, "input", &input_type);
1399f7b4b54eSJavier Martinez Canillas 		if (ret) {
1400257e29f8SMauro Carvalho Chehab 			dev_err(decoder->sd.dev,
1401f7b4b54eSJavier Martinez Canillas 				 "missing type property in node %s\n",
1402f7b4b54eSJavier Martinez Canillas 				 child->name);
1403f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1404f7b4b54eSJavier Martinez Canillas 		}
1405f7b4b54eSJavier Martinez Canillas 
140660ad7689SMauro Carvalho Chehab 		if (input_type >= TVP5150_INPUT_NUM) {
1407f7b4b54eSJavier Martinez Canillas 			ret = -EINVAL;
1408f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1409f7b4b54eSJavier Martinez Canillas 		}
1410f7b4b54eSJavier Martinez Canillas 
1411f7b4b54eSJavier Martinez Canillas 		input = &decoder->input_ent[input_type];
1412f7b4b54eSJavier Martinez Canillas 
1413f7b4b54eSJavier Martinez Canillas 		/* Each input connector can only be defined once */
1414f7b4b54eSJavier Martinez Canillas 		if (input->name) {
1415257e29f8SMauro Carvalho Chehab 			dev_err(decoder->sd.dev,
1416f7b4b54eSJavier Martinez Canillas 				 "input %s with same type already exists\n",
1417f7b4b54eSJavier Martinez Canillas 				 input->name);
1418f7b4b54eSJavier Martinez Canillas 			ret = -EINVAL;
1419f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1420f7b4b54eSJavier Martinez Canillas 		}
1421f7b4b54eSJavier Martinez Canillas 
1422f7b4b54eSJavier Martinez Canillas 		switch (input_type) {
1423f7b4b54eSJavier Martinez Canillas 		case TVP5150_COMPOSITE0:
1424f7b4b54eSJavier Martinez Canillas 		case TVP5150_COMPOSITE1:
1425f7b4b54eSJavier Martinez Canillas 			input->function = MEDIA_ENT_F_CONN_COMPOSITE;
1426f7b4b54eSJavier Martinez Canillas 			break;
1427f7b4b54eSJavier Martinez Canillas 		case TVP5150_SVIDEO:
1428f7b4b54eSJavier Martinez Canillas 			input->function = MEDIA_ENT_F_CONN_SVIDEO;
1429f7b4b54eSJavier Martinez Canillas 			break;
1430f7b4b54eSJavier Martinez Canillas 		}
1431f7b4b54eSJavier Martinez Canillas 
1432f7b4b54eSJavier Martinez Canillas 		input->flags = MEDIA_ENT_FL_CONNECTOR;
1433f7b4b54eSJavier Martinez Canillas 
1434f7b4b54eSJavier Martinez Canillas 		ret = of_property_read_string(child, "label", &name);
1435f7b4b54eSJavier Martinez Canillas 		if (ret < 0) {
1436257e29f8SMauro Carvalho Chehab 			dev_err(decoder->sd.dev,
1437f7b4b54eSJavier Martinez Canillas 				 "missing label property in node %s\n",
1438f7b4b54eSJavier Martinez Canillas 				 child->name);
1439f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1440f7b4b54eSJavier Martinez Canillas 		}
1441f7b4b54eSJavier Martinez Canillas 
1442f7b4b54eSJavier Martinez Canillas 		input->name = name;
1443f7b4b54eSJavier Martinez Canillas 	}
1444f7b4b54eSJavier Martinez Canillas 
1445f7b4b54eSJavier Martinez Canillas err_connector:
1446f7b4b54eSJavier Martinez Canillas 	of_node_put(connectors);
1447f7b4b54eSJavier Martinez Canillas #endif
1448a2e5f1b3SJavier Martinez Canillas err:
1449a2e5f1b3SJavier Martinez Canillas 	of_node_put(ep);
1450a2e5f1b3SJavier Martinez Canillas 	return ret;
1451a2e5f1b3SJavier Martinez Canillas }
1452a2e5f1b3SJavier Martinez Canillas 
1453c43875f6SMauro Carvalho Chehab static const char * const tvp5150_test_patterns[2] = {
1454c43875f6SMauro Carvalho Chehab 	"Disabled",
1455c43875f6SMauro Carvalho Chehab 	"Black screen"
1456c43875f6SMauro Carvalho Chehab };
1457c43875f6SMauro Carvalho Chehab 
1458cb7a01acSMauro Carvalho Chehab static int tvp5150_probe(struct i2c_client *c,
1459cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1460cb7a01acSMauro Carvalho Chehab {
1461cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *core;
1462cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1463a2e5f1b3SJavier Martinez Canillas 	struct device_node *np = c->dev.of_node;
14647871597aSLaurent Pinchart 	int res;
1465cb7a01acSMauro Carvalho Chehab 
1466cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1467cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(c->adapter,
1468cb7a01acSMauro Carvalho Chehab 	     I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
1469cb7a01acSMauro Carvalho Chehab 		return -EIO;
1470cb7a01acSMauro Carvalho Chehab 
147109aa2609SJavier Martinez Canillas 	res = tvp5150_init(c);
147209aa2609SJavier Martinez Canillas 	if (res)
147309aa2609SJavier Martinez Canillas 		return res;
147409aa2609SJavier Martinez Canillas 
1475c02b211dSLaurent Pinchart 	core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL);
1476c02b211dSLaurent Pinchart 	if (!core)
1477cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1478a2e5f1b3SJavier Martinez Canillas 
1479cb7a01acSMauro Carvalho Chehab 	sd = &core->sd;
1480a2e5f1b3SJavier Martinez Canillas 
1481a2e5f1b3SJavier Martinez Canillas 	if (IS_ENABLED(CONFIG_OF) && np) {
1482a2e5f1b3SJavier Martinez Canillas 		res = tvp5150_parse_dt(core, np);
1483a2e5f1b3SJavier Martinez Canillas 		if (res) {
1484257e29f8SMauro Carvalho Chehab 			dev_err(sd->dev, "DT parsing error: %d\n", res);
1485a2e5f1b3SJavier Martinez Canillas 			return res;
1486a2e5f1b3SJavier Martinez Canillas 		}
1487a2e5f1b3SJavier Martinez Canillas 	} else {
1488a2e5f1b3SJavier Martinez Canillas 		/* Default to BT.656 embedded sync */
1489a2e5f1b3SJavier Martinez Canillas 		core->mbus_type = V4L2_MBUS_BT656;
1490a2e5f1b3SJavier Martinez Canillas 	}
1491a2e5f1b3SJavier Martinez Canillas 
1492cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
14935a08bc00SJavier Martinez Canillas 	sd->internal_ops = &tvp5150_internal_ops;
1494e545ac87SLaurent Pinchart 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1495e545ac87SLaurent Pinchart 
1496e545ac87SLaurent Pinchart #if defined(CONFIG_MEDIA_CONTROLLER)
149755606310SMauro Carvalho Chehab 	core->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
149855606310SMauro Carvalho Chehab 	core->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
149955606310SMauro Carvalho Chehab 	core->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
1500f92c70adSMauro Carvalho Chehab 
1501f92c70adSMauro Carvalho Chehab 	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
1502f92c70adSMauro Carvalho Chehab 
150355606310SMauro Carvalho Chehab 	res = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, core->pads);
1504e545ac87SLaurent Pinchart 	if (res < 0)
1505e545ac87SLaurent Pinchart 		return res;
1506f7b4b54eSJavier Martinez Canillas 
1507f7b4b54eSJavier Martinez Canillas 	sd->entity.ops = &tvp5150_sd_media_ops;
1508e545ac87SLaurent Pinchart #endif
1509cb7a01acSMauro Carvalho Chehab 
15107871597aSLaurent Pinchart 	res = tvp5150_detect_version(core);
1511cb7a01acSMauro Carvalho Chehab 	if (res < 0)
1512c02b211dSLaurent Pinchart 		return res;
1513cb7a01acSMauro Carvalho Chehab 
1514cb7a01acSMauro Carvalho Chehab 	core->norm = V4L2_STD_ALL;	/* Default is autodetect */
1515cb7a01acSMauro Carvalho Chehab 	core->input = TVP5150_COMPOSITE1;
1516c43875f6SMauro Carvalho Chehab 	core->enable = true;
1517cb7a01acSMauro Carvalho Chehab 
1518b1950b8dSLaurent Pinchart 	v4l2_ctrl_handler_init(&core->hdl, 5);
1519cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1520cb7a01acSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
1521cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1522cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 255, 1, 128);
1523cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1524cb7a01acSMauro Carvalho Chehab 			V4L2_CID_SATURATION, 0, 255, 1, 128);
1525cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1526cb7a01acSMauro Carvalho Chehab 			V4L2_CID_HUE, -128, 127, 1, 0);
1527b1950b8dSLaurent Pinchart 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1528b1950b8dSLaurent Pinchart 			V4L2_CID_PIXEL_RATE, 27000000,
1529b1950b8dSLaurent Pinchart 			27000000, 1, 27000000);
1530c43875f6SMauro Carvalho Chehab 	v4l2_ctrl_new_std_menu_items(&core->hdl, &tvp5150_ctrl_ops,
1531c43875f6SMauro Carvalho Chehab 				     V4L2_CID_TEST_PATTERN,
1532c43875f6SMauro Carvalho Chehab 				     ARRAY_SIZE(tvp5150_test_patterns),
1533c43875f6SMauro Carvalho Chehab 				     0, 0, tvp5150_test_patterns);
1534cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = &core->hdl;
1535cb7a01acSMauro Carvalho Chehab 	if (core->hdl.error) {
1536cb7a01acSMauro Carvalho Chehab 		res = core->hdl.error;
1537c7d97499SJavier Martinez Canillas 		goto err;
1538cb7a01acSMauro Carvalho Chehab 	}
1539cb7a01acSMauro Carvalho Chehab 
1540cb7a01acSMauro Carvalho Chehab 	/* Default is no cropping */
1541cb7a01acSMauro Carvalho Chehab 	core->rect.top = 0;
1542cb7a01acSMauro Carvalho Chehab 	if (tvp5150_read_std(sd) & V4L2_STD_525_60)
1543cb7a01acSMauro Carvalho Chehab 		core->rect.height = TVP5150_V_MAX_525_60;
1544cb7a01acSMauro Carvalho Chehab 	else
1545cb7a01acSMauro Carvalho Chehab 		core->rect.height = TVP5150_V_MAX_OTHERS;
1546cb7a01acSMauro Carvalho Chehab 	core->rect.left = 0;
1547cb7a01acSMauro Carvalho Chehab 	core->rect.width = TVP5150_H_MAX;
1548cb7a01acSMauro Carvalho Chehab 
1549aff808e8SLaurent Pinchart 	tvp5150_reset(sd, 0);	/* Calls v4l2_ctrl_handler_setup() */
1550aff808e8SLaurent Pinchart 
1551c7d97499SJavier Martinez Canillas 	res = v4l2_async_register_subdev(sd);
1552c7d97499SJavier Martinez Canillas 	if (res < 0)
1553c7d97499SJavier Martinez Canillas 		goto err;
1554c7d97499SJavier Martinez Canillas 
1555cb7a01acSMauro Carvalho Chehab 	if (debug > 1)
1556cb7a01acSMauro Carvalho Chehab 		tvp5150_log_status(sd);
1557cb7a01acSMauro Carvalho Chehab 	return 0;
1558c7d97499SJavier Martinez Canillas 
1559c7d97499SJavier Martinez Canillas err:
1560c7d97499SJavier Martinez Canillas 	v4l2_ctrl_handler_free(&core->hdl);
1561c7d97499SJavier Martinez Canillas 	return res;
1562cb7a01acSMauro Carvalho Chehab }
1563cb7a01acSMauro Carvalho Chehab 
1564cb7a01acSMauro Carvalho Chehab static int tvp5150_remove(struct i2c_client *c)
1565cb7a01acSMauro Carvalho Chehab {
1566cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(c);
1567cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
1568cb7a01acSMauro Carvalho Chehab 
1569257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug,
1570cb7a01acSMauro Carvalho Chehab 		"tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
1571cb7a01acSMauro Carvalho Chehab 		c->addr << 1);
1572cb7a01acSMauro Carvalho Chehab 
1573c7d97499SJavier Martinez Canillas 	v4l2_async_unregister_subdev(sd);
1574cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&decoder->hdl);
1575cb7a01acSMauro Carvalho Chehab 	return 0;
1576cb7a01acSMauro Carvalho Chehab }
1577cb7a01acSMauro Carvalho Chehab 
1578cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1579cb7a01acSMauro Carvalho Chehab 
1580cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id tvp5150_id[] = {
1581cb7a01acSMauro Carvalho Chehab 	{ "tvp5150", 0 },
1582cb7a01acSMauro Carvalho Chehab 	{ }
1583cb7a01acSMauro Carvalho Chehab };
1584cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, tvp5150_id);
1585cb7a01acSMauro Carvalho Chehab 
15867ef930a7SEduard Gavin #if IS_ENABLED(CONFIG_OF)
15877ef930a7SEduard Gavin static const struct of_device_id tvp5150_of_match[] = {
15887ef930a7SEduard Gavin 	{ .compatible = "ti,tvp5150", },
15897ef930a7SEduard Gavin 	{ /* sentinel */ },
15907ef930a7SEduard Gavin };
15917ef930a7SEduard Gavin MODULE_DEVICE_TABLE(of, tvp5150_of_match);
15927ef930a7SEduard Gavin #endif
15937ef930a7SEduard Gavin 
1594cb7a01acSMauro Carvalho Chehab static struct i2c_driver tvp5150_driver = {
1595cb7a01acSMauro Carvalho Chehab 	.driver = {
15967ef930a7SEduard Gavin 		.of_match_table = of_match_ptr(tvp5150_of_match),
1597cb7a01acSMauro Carvalho Chehab 		.name	= "tvp5150",
1598cb7a01acSMauro Carvalho Chehab 	},
1599cb7a01acSMauro Carvalho Chehab 	.probe		= tvp5150_probe,
1600cb7a01acSMauro Carvalho Chehab 	.remove		= tvp5150_remove,
1601cb7a01acSMauro Carvalho Chehab 	.id_table	= tvp5150_id,
1602cb7a01acSMauro Carvalho Chehab };
1603cb7a01acSMauro Carvalho Chehab 
1604cb7a01acSMauro Carvalho Chehab module_i2c_driver(tvp5150_driver);
1605