xref: /openbmc/linux/drivers/media/i2c/tvp5150.c (revision bd24db04)
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 
41bc322c0dSMauro Carvalho Chehab enum tvp5150_pads {
42bc322c0dSMauro Carvalho Chehab 	TVP5150_PAD_IF_INPUT,
43bc322c0dSMauro Carvalho Chehab 	TVP5150_PAD_VID_OUT,
44bc322c0dSMauro Carvalho Chehab 	TVP5150_NUM_PADS
45bc322c0dSMauro Carvalho Chehab };
46bc322c0dSMauro Carvalho Chehab 
47cb7a01acSMauro Carvalho Chehab struct tvp5150 {
48cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
4955606310SMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
50bc322c0dSMauro Carvalho Chehab 	struct media_pad pads[TVP5150_NUM_PADS];
51f7b4b54eSJavier Martinez Canillas 	struct media_entity input_ent[TVP5150_INPUT_NUM];
52f7b4b54eSJavier Martinez Canillas 	struct media_pad input_pad[TVP5150_INPUT_NUM];
5355606310SMauro Carvalho Chehab #endif
54cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
55cb7a01acSMauro Carvalho Chehab 	struct v4l2_rect rect;
56cb7a01acSMauro Carvalho Chehab 
57cb7a01acSMauro Carvalho Chehab 	v4l2_std_id norm;	/* Current set standard */
58cb7a01acSMauro Carvalho Chehab 	u32 input;
59cb7a01acSMauro Carvalho Chehab 	u32 output;
60cb7a01acSMauro Carvalho Chehab 	int enable;
61a2e5f1b3SJavier Martinez Canillas 
6282275133SJavier Martinez Canillas 	u16 dev_id;
6382275133SJavier Martinez Canillas 	u16 rom_ver;
6482275133SJavier Martinez Canillas 
65a2e5f1b3SJavier Martinez Canillas 	enum v4l2_mbus_type mbus_type;
66cb7a01acSMauro Carvalho Chehab };
67cb7a01acSMauro Carvalho Chehab 
68cb7a01acSMauro Carvalho Chehab static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
69cb7a01acSMauro Carvalho Chehab {
70cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct tvp5150, sd);
71cb7a01acSMauro Carvalho Chehab }
72cb7a01acSMauro Carvalho Chehab 
73cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
74cb7a01acSMauro Carvalho Chehab {
75cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct tvp5150, hdl)->sd;
76cb7a01acSMauro Carvalho Chehab }
77cb7a01acSMauro Carvalho Chehab 
78cb7a01acSMauro Carvalho Chehab static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
79cb7a01acSMauro Carvalho Chehab {
80cb7a01acSMauro Carvalho Chehab 	struct i2c_client *c = v4l2_get_subdevdata(sd);
81cb7a01acSMauro Carvalho Chehab 	int rc;
82cb7a01acSMauro Carvalho Chehab 
83e35ce2e4SLaurent Pinchart 	rc = i2c_smbus_read_byte_data(c, addr);
84e35ce2e4SLaurent Pinchart 	if (rc < 0) {
85257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "i2c i/o error: rc == %d\n", rc);
86e35ce2e4SLaurent Pinchart 		return rc;
87cb7a01acSMauro Carvalho Chehab 	}
88cb7a01acSMauro Carvalho Chehab 
89257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 2, debug, "tvp5150: read 0x%02x = %02x\n", addr, rc);
90cb7a01acSMauro Carvalho Chehab 
91e35ce2e4SLaurent Pinchart 	return rc;
92cb7a01acSMauro Carvalho Chehab }
93cb7a01acSMauro Carvalho Chehab 
94cacdd6a4SJavier Martinez Canillas static int tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
95cb7a01acSMauro Carvalho Chehab 				 unsigned char value)
96cb7a01acSMauro Carvalho Chehab {
97cb7a01acSMauro Carvalho Chehab 	struct i2c_client *c = v4l2_get_subdevdata(sd);
98cb7a01acSMauro Carvalho Chehab 	int rc;
99cb7a01acSMauro Carvalho Chehab 
100257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 2, debug, "tvp5150: writing %02x %02x\n", addr, value);
101e35ce2e4SLaurent Pinchart 	rc = i2c_smbus_write_byte_data(c, addr, value);
102e35ce2e4SLaurent Pinchart 	if (rc < 0)
103257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "i2c i/o error: rc == %d\n", rc);
104cacdd6a4SJavier Martinez Canillas 
105cacdd6a4SJavier Martinez Canillas 	return rc;
106cb7a01acSMauro Carvalho Chehab }
107cb7a01acSMauro Carvalho Chehab 
108cb7a01acSMauro Carvalho Chehab static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
109cb7a01acSMauro Carvalho Chehab 				const u8 end, int max_line)
110cb7a01acSMauro Carvalho Chehab {
111e5134114SMauro Carvalho Chehab 	u8 buf[16];
112e5134114SMauro Carvalho Chehab 	int i = 0, j, len;
113cb7a01acSMauro Carvalho Chehab 
114e5134114SMauro Carvalho Chehab 	if (max_line > 16) {
115e5134114SMauro Carvalho Chehab 		dprintk0(sd->dev, "too much data to dump\n");
116e5134114SMauro Carvalho Chehab 		return;
117cb7a01acSMauro Carvalho Chehab 	}
118cb7a01acSMauro Carvalho Chehab 
119e5134114SMauro Carvalho Chehab 	for (i = init; i < end; i += max_line) {
120e5134114SMauro Carvalho Chehab 		len = (end - i > max_line) ? max_line : end - i;
121e5134114SMauro Carvalho Chehab 
122e5134114SMauro Carvalho Chehab 		for (j = 0; j < len; j++)
123e5134114SMauro Carvalho Chehab 			buf[j] = tvp5150_read(sd, i + j);
124e5134114SMauro Carvalho Chehab 
125e5134114SMauro Carvalho Chehab 		dprintk0(sd->dev, "%s reg %02x = %*ph\n", s, i, len, buf);
126cb7a01acSMauro Carvalho Chehab 	}
127cb7a01acSMauro Carvalho Chehab }
128cb7a01acSMauro Carvalho Chehab 
129cb7a01acSMauro Carvalho Chehab static int tvp5150_log_status(struct v4l2_subdev *sd)
130cb7a01acSMauro Carvalho Chehab {
131ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Video input source selection #1 = 0x%02x\n",
132cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
133ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Analog channel controls = 0x%02x\n",
134cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
135ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Operation mode controls = 0x%02x\n",
136cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_OP_MODE_CTL));
137ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Miscellaneous controls = 0x%02x\n",
138cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MISC_CTL));
139ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Autoswitch mask= 0x%02x\n",
140cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_AUTOSW_MSK));
141ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Color killer threshold control = 0x%02x\n",
142cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
143ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
144cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
145cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
146cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
147ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Brightness control = 0x%02x\n",
148cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_BRIGHT_CTL));
149ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Color saturation control = 0x%02x\n",
150cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_SATURATION_CTL));
151ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Hue control = 0x%02x\n",
152cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_HUE_CTL));
153ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Contrast control = 0x%02x\n",
154cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CONTRAST_CTL));
155ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Outputs and data rates select = 0x%02x\n",
156cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
157ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Configuration shared pins = 0x%02x\n",
158cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
159ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Active video cropping start = 0x%02x%02x\n",
160cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
161cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
162ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Active video cropping stop  = 0x%02x%02x\n",
163cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
164cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
165ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Genlock/RTC = 0x%02x\n",
166cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_GENLOCK));
167ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Horizontal sync start = 0x%02x\n",
168cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
169ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Vertical blanking start = 0x%02x\n",
170cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
171ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Vertical blanking stop = 0x%02x\n",
172cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
173ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
174cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
175cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
176ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt reset register B = 0x%02x\n",
177cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
178ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt enable register B = 0x%02x\n",
179cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
180ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt configuration register B = 0x%02x\n",
181cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
182ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Video standard = 0x%02x\n",
183cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VIDEO_STD));
184ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
185cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
186cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
187ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Macrovision on counter = 0x%02x\n",
188cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
189ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Macrovision off counter = 0x%02x\n",
190cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
191ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
192cb7a01acSMauro Carvalho Chehab 		(tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
193ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Device ID = %02x%02x\n",
194cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_MSB_DEV_ID),
195cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LSB_DEV_ID));
196ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: ROM version = (hex) %02x.%02x\n",
197cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
198cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
199ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Vertical line count = 0x%02x%02x\n",
200cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
201cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
202ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt status register B = 0x%02x\n",
203cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
204ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt active register B = 0x%02x\n",
205cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
206ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
207cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_1),
208cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_2),
209cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_3),
210cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_4),
211cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_STATUS_REG_5));
212cb7a01acSMauro Carvalho Chehab 
213cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "Teletext filter 1",   TVP5150_TELETEXT_FIL1_INI,
214cb7a01acSMauro Carvalho Chehab 			TVP5150_TELETEXT_FIL1_END, 8);
215cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "Teletext filter 2",   TVP5150_TELETEXT_FIL2_INI,
216cb7a01acSMauro Carvalho Chehab 			TVP5150_TELETEXT_FIL2_END, 8);
217cb7a01acSMauro Carvalho Chehab 
218ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Teletext filter enable = 0x%02x\n",
219cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
220ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt status register A = 0x%02x\n",
221cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
222ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt enable register A = 0x%02x\n",
223cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
224ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Interrupt configuration = 0x%02x\n",
225cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_INT_CONF));
226ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: VDP status register = 0x%02x\n",
227cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
228ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO word count = 0x%02x\n",
229cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
230ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO interrupt threshold = 0x%02x\n",
231cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
232ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO reset = 0x%02x\n",
233cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_RESET));
234ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Line number interrupt = 0x%02x\n",
235cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
236ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Pixel alignment register = 0x%02x%02x\n",
237cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
238cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
239ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: FIFO output control = 0x%02x\n",
240cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
241ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Full field enable = 0x%02x\n",
242cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
243ad0e3744SMauro Carvalho Chehab 	dprintk0(sd->dev, "tvp5150: Full field mode register = 0x%02x\n",
244cb7a01acSMauro Carvalho Chehab 		tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
245cb7a01acSMauro Carvalho Chehab 
246cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "CC   data",   TVP5150_CC_DATA_INI,
247cb7a01acSMauro Carvalho Chehab 			TVP5150_CC_DATA_END, 8);
248cb7a01acSMauro Carvalho Chehab 
249cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "WSS  data",   TVP5150_WSS_DATA_INI,
250cb7a01acSMauro Carvalho Chehab 			TVP5150_WSS_DATA_END, 8);
251cb7a01acSMauro Carvalho Chehab 
252cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "VPS  data",   TVP5150_VPS_DATA_INI,
253cb7a01acSMauro Carvalho Chehab 			TVP5150_VPS_DATA_END, 8);
254cb7a01acSMauro Carvalho Chehab 
255cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "VITC data",   TVP5150_VITC_DATA_INI,
256cb7a01acSMauro Carvalho Chehab 			TVP5150_VITC_DATA_END, 10);
257cb7a01acSMauro Carvalho Chehab 
258cb7a01acSMauro Carvalho Chehab 	dump_reg_range(sd, "Line mode",   TVP5150_LINE_MODE_INI,
259cb7a01acSMauro Carvalho Chehab 			TVP5150_LINE_MODE_END, 8);
260cb7a01acSMauro Carvalho Chehab 	return 0;
261cb7a01acSMauro Carvalho Chehab }
262cb7a01acSMauro Carvalho Chehab 
263cb7a01acSMauro Carvalho Chehab /****************************************************************************
264cb7a01acSMauro Carvalho Chehab 			Basic functions
265cb7a01acSMauro Carvalho Chehab  ****************************************************************************/
266cb7a01acSMauro Carvalho Chehab 
2676e98bee2SLaurent Pinchart static void tvp5150_selmux(struct v4l2_subdev *sd)
268cb7a01acSMauro Carvalho Chehab {
269cb7a01acSMauro Carvalho Chehab 	int opmode = 0;
270cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
271cb7a01acSMauro Carvalho Chehab 	int input = 0;
272cb7a01acSMauro Carvalho Chehab 	int val;
273cb7a01acSMauro Carvalho Chehab 
274c43875f6SMauro Carvalho Chehab 	/* Only tvp5150am1 and tvp5151 have signal generator support */
275c43875f6SMauro Carvalho Chehab 	if ((decoder->dev_id == 0x5150 && decoder->rom_ver == 0x0400) ||
276c43875f6SMauro Carvalho Chehab 	    (decoder->dev_id == 0x5151 && decoder->rom_ver == 0x0100)) {
277c43875f6SMauro Carvalho Chehab 		if (!decoder->enable)
278cb7a01acSMauro Carvalho Chehab 			input = 8;
279c43875f6SMauro Carvalho Chehab 	}
280cb7a01acSMauro Carvalho Chehab 
281cb7a01acSMauro Carvalho Chehab 	switch (decoder->input) {
282cb7a01acSMauro Carvalho Chehab 	case TVP5150_COMPOSITE1:
283cb7a01acSMauro Carvalho Chehab 		input |= 2;
284cb7a01acSMauro Carvalho Chehab 		/* fall through */
285cb7a01acSMauro Carvalho Chehab 	case TVP5150_COMPOSITE0:
286cb7a01acSMauro Carvalho Chehab 		break;
287cb7a01acSMauro Carvalho Chehab 	case TVP5150_SVIDEO:
288cb7a01acSMauro Carvalho Chehab 	default:
289cb7a01acSMauro Carvalho Chehab 		input |= 1;
290cb7a01acSMauro Carvalho Chehab 		break;
291cb7a01acSMauro Carvalho Chehab 	}
292cb7a01acSMauro Carvalho Chehab 
293257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "Selecting video route: route input=%i, output=%i => tvp5150 input=%i, opmode=%i\n",
294cb7a01acSMauro Carvalho Chehab 			decoder->input, decoder->output,
295cb7a01acSMauro Carvalho Chehab 			input, opmode);
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
298cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
299cb7a01acSMauro Carvalho Chehab 
300b4b2de38SLaurent Pinchart 	/*
301b4b2de38SLaurent Pinchart 	 * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For
302b4b2de38SLaurent Pinchart 	 * S-Video we output the vertical lock (VLK) signal on FID/GLCO/VLK/HVLK
303b4b2de38SLaurent Pinchart 	 * and set INTREQ/GPCL/VBLK to logic 0. For composite we output the
304b4b2de38SLaurent Pinchart 	 * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set
305b4b2de38SLaurent Pinchart 	 * INTREQ/GPCL/VBLK to logic 1.
306cb7a01acSMauro Carvalho Chehab 	 */
307cb7a01acSMauro Carvalho Chehab 	val = tvp5150_read(sd, TVP5150_MISC_CTL);
308cb7a01acSMauro Carvalho Chehab 	if (val < 0) {
309257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "%s: failed with error = %d\n", __func__, val);
310cb7a01acSMauro Carvalho Chehab 		return;
311cb7a01acSMauro Carvalho Chehab 	}
312cb7a01acSMauro Carvalho Chehab 
313cb7a01acSMauro Carvalho Chehab 	if (decoder->input == TVP5150_SVIDEO)
314b4b2de38SLaurent Pinchart 		val = (val & ~TVP5150_MISC_CTL_GPCL) | TVP5150_MISC_CTL_HVLK;
315cb7a01acSMauro Carvalho Chehab 	else
316b4b2de38SLaurent Pinchart 		val = (val & ~TVP5150_MISC_CTL_HVLK) | TVP5150_MISC_CTL_GPCL;
317cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_MISC_CTL, val);
318cb7a01acSMauro Carvalho Chehab };
319cb7a01acSMauro Carvalho Chehab 
320cb7a01acSMauro Carvalho Chehab struct i2c_reg_value {
321cb7a01acSMauro Carvalho Chehab 	unsigned char reg;
322cb7a01acSMauro Carvalho Chehab 	unsigned char value;
323cb7a01acSMauro Carvalho Chehab };
324cb7a01acSMauro Carvalho Chehab 
325cb7a01acSMauro Carvalho Chehab /* Default values as sugested at TVP5150AM1 datasheet */
326cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp5150_init_default[] = {
327cb7a01acSMauro Carvalho Chehab 	{ /* 0x00 */
328cb7a01acSMauro Carvalho Chehab 		TVP5150_VD_IN_SRC_SEL_1, 0x00
329cb7a01acSMauro Carvalho Chehab 	},
330cb7a01acSMauro Carvalho Chehab 	{ /* 0x01 */
331cb7a01acSMauro Carvalho Chehab 		TVP5150_ANAL_CHL_CTL, 0x15
332cb7a01acSMauro Carvalho Chehab 	},
333cb7a01acSMauro Carvalho Chehab 	{ /* 0x02 */
334cb7a01acSMauro Carvalho Chehab 		TVP5150_OP_MODE_CTL, 0x00
335cb7a01acSMauro Carvalho Chehab 	},
336cb7a01acSMauro Carvalho Chehab 	{ /* 0x03 */
337cb7a01acSMauro Carvalho Chehab 		TVP5150_MISC_CTL, 0x01
338cb7a01acSMauro Carvalho Chehab 	},
339cb7a01acSMauro Carvalho Chehab 	{ /* 0x06 */
340cb7a01acSMauro Carvalho Chehab 		TVP5150_COLOR_KIL_THSH_CTL, 0x10
341cb7a01acSMauro Carvalho Chehab 	},
342cb7a01acSMauro Carvalho Chehab 	{ /* 0x07 */
343cb7a01acSMauro Carvalho Chehab 		TVP5150_LUMA_PROC_CTL_1, 0x60
344cb7a01acSMauro Carvalho Chehab 	},
345cb7a01acSMauro Carvalho Chehab 	{ /* 0x08 */
346cb7a01acSMauro Carvalho Chehab 		TVP5150_LUMA_PROC_CTL_2, 0x00
347cb7a01acSMauro Carvalho Chehab 	},
348cb7a01acSMauro Carvalho Chehab 	{ /* 0x09 */
349cb7a01acSMauro Carvalho Chehab 		TVP5150_BRIGHT_CTL, 0x80
350cb7a01acSMauro Carvalho Chehab 	},
351cb7a01acSMauro Carvalho Chehab 	{ /* 0x0a */
352cb7a01acSMauro Carvalho Chehab 		TVP5150_SATURATION_CTL, 0x80
353cb7a01acSMauro Carvalho Chehab 	},
354cb7a01acSMauro Carvalho Chehab 	{ /* 0x0b */
355cb7a01acSMauro Carvalho Chehab 		TVP5150_HUE_CTL, 0x00
356cb7a01acSMauro Carvalho Chehab 	},
357cb7a01acSMauro Carvalho Chehab 	{ /* 0x0c */
358cb7a01acSMauro Carvalho Chehab 		TVP5150_CONTRAST_CTL, 0x80
359cb7a01acSMauro Carvalho Chehab 	},
360cb7a01acSMauro Carvalho Chehab 	{ /* 0x0d */
361cb7a01acSMauro Carvalho Chehab 		TVP5150_DATA_RATE_SEL, 0x47
362cb7a01acSMauro Carvalho Chehab 	},
363cb7a01acSMauro Carvalho Chehab 	{ /* 0x0e */
364cb7a01acSMauro Carvalho Chehab 		TVP5150_LUMA_PROC_CTL_3, 0x00
365cb7a01acSMauro Carvalho Chehab 	},
366cb7a01acSMauro Carvalho Chehab 	{ /* 0x0f */
367cb7a01acSMauro Carvalho Chehab 		TVP5150_CONF_SHARED_PIN, 0x08
368cb7a01acSMauro Carvalho Chehab 	},
369cb7a01acSMauro Carvalho Chehab 	{ /* 0x11 */
370cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_ST_MSB, 0x00
371cb7a01acSMauro Carvalho Chehab 	},
372cb7a01acSMauro Carvalho Chehab 	{ /* 0x12 */
373cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_ST_LSB, 0x00
374cb7a01acSMauro Carvalho Chehab 	},
375cb7a01acSMauro Carvalho Chehab 	{ /* 0x13 */
376cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_STP_MSB, 0x00
377cb7a01acSMauro Carvalho Chehab 	},
378cb7a01acSMauro Carvalho Chehab 	{ /* 0x14 */
379cb7a01acSMauro Carvalho Chehab 		TVP5150_ACT_VD_CROP_STP_LSB, 0x00
380cb7a01acSMauro Carvalho Chehab 	},
381cb7a01acSMauro Carvalho Chehab 	{ /* 0x15 */
382cb7a01acSMauro Carvalho Chehab 		TVP5150_GENLOCK, 0x01
383cb7a01acSMauro Carvalho Chehab 	},
384cb7a01acSMauro Carvalho Chehab 	{ /* 0x16 */
385cb7a01acSMauro Carvalho Chehab 		TVP5150_HORIZ_SYNC_START, 0x80
386cb7a01acSMauro Carvalho Chehab 	},
387cb7a01acSMauro Carvalho Chehab 	{ /* 0x18 */
388cb7a01acSMauro Carvalho Chehab 		TVP5150_VERT_BLANKING_START, 0x00
389cb7a01acSMauro Carvalho Chehab 	},
390cb7a01acSMauro Carvalho Chehab 	{ /* 0x19 */
391cb7a01acSMauro Carvalho Chehab 		TVP5150_VERT_BLANKING_STOP, 0x00
392cb7a01acSMauro Carvalho Chehab 	},
393cb7a01acSMauro Carvalho Chehab 	{ /* 0x1a */
394cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_1, 0x0c
395cb7a01acSMauro Carvalho Chehab 	},
396cb7a01acSMauro Carvalho Chehab 	{ /* 0x1b */
397cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_2, 0x14
398cb7a01acSMauro Carvalho Chehab 	},
399cb7a01acSMauro Carvalho Chehab 	{ /* 0x1c */
400cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_RESET_REG_B, 0x00
401cb7a01acSMauro Carvalho Chehab 	},
402cb7a01acSMauro Carvalho Chehab 	{ /* 0x1d */
403cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_ENABLE_REG_B, 0x00
404cb7a01acSMauro Carvalho Chehab 	},
405cb7a01acSMauro Carvalho Chehab 	{ /* 0x1e */
406cb7a01acSMauro Carvalho Chehab 		TVP5150_INTT_CONFIG_REG_B, 0x00
407cb7a01acSMauro Carvalho Chehab 	},
408cb7a01acSMauro Carvalho Chehab 	{ /* 0x28 */
409cb7a01acSMauro Carvalho Chehab 		TVP5150_VIDEO_STD, 0x00
410cb7a01acSMauro Carvalho Chehab 	},
411cb7a01acSMauro Carvalho Chehab 	{ /* 0x2e */
412cb7a01acSMauro Carvalho Chehab 		TVP5150_MACROVISION_ON_CTR, 0x0f
413cb7a01acSMauro Carvalho Chehab 	},
414cb7a01acSMauro Carvalho Chehab 	{ /* 0x2f */
415cb7a01acSMauro Carvalho Chehab 		TVP5150_MACROVISION_OFF_CTR, 0x01
416cb7a01acSMauro Carvalho Chehab 	},
417cb7a01acSMauro Carvalho Chehab 	{ /* 0xbb */
418cb7a01acSMauro Carvalho Chehab 		TVP5150_TELETEXT_FIL_ENA, 0x00
419cb7a01acSMauro Carvalho Chehab 	},
420cb7a01acSMauro Carvalho Chehab 	{ /* 0xc0 */
421cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_STATUS_REG_A, 0x00
422cb7a01acSMauro Carvalho Chehab 	},
423cb7a01acSMauro Carvalho Chehab 	{ /* 0xc1 */
424cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_ENABLE_REG_A, 0x00
425cb7a01acSMauro Carvalho Chehab 	},
426cb7a01acSMauro Carvalho Chehab 	{ /* 0xc2 */
427cb7a01acSMauro Carvalho Chehab 		TVP5150_INT_CONF, 0x04
428cb7a01acSMauro Carvalho Chehab 	},
429cb7a01acSMauro Carvalho Chehab 	{ /* 0xc8 */
430cb7a01acSMauro Carvalho Chehab 		TVP5150_FIFO_INT_THRESHOLD, 0x80
431cb7a01acSMauro Carvalho Chehab 	},
432cb7a01acSMauro Carvalho Chehab 	{ /* 0xc9 */
433cb7a01acSMauro Carvalho Chehab 		TVP5150_FIFO_RESET, 0x00
434cb7a01acSMauro Carvalho Chehab 	},
435cb7a01acSMauro Carvalho Chehab 	{ /* 0xca */
436cb7a01acSMauro Carvalho Chehab 		TVP5150_LINE_NUMBER_INT, 0x00
437cb7a01acSMauro Carvalho Chehab 	},
438cb7a01acSMauro Carvalho Chehab 	{ /* 0xcb */
439cb7a01acSMauro Carvalho Chehab 		TVP5150_PIX_ALIGN_REG_LOW, 0x4e
440cb7a01acSMauro Carvalho Chehab 	},
441cb7a01acSMauro Carvalho Chehab 	{ /* 0xcc */
442cb7a01acSMauro Carvalho Chehab 		TVP5150_PIX_ALIGN_REG_HIGH, 0x00
443cb7a01acSMauro Carvalho Chehab 	},
444cb7a01acSMauro Carvalho Chehab 	{ /* 0xcd */
445cb7a01acSMauro Carvalho Chehab 		TVP5150_FIFO_OUT_CTRL, 0x01
446cb7a01acSMauro Carvalho Chehab 	},
447cb7a01acSMauro Carvalho Chehab 	{ /* 0xcf */
448cb7a01acSMauro Carvalho Chehab 		TVP5150_FULL_FIELD_ENA, 0x00
449cb7a01acSMauro Carvalho Chehab 	},
450cb7a01acSMauro Carvalho Chehab 	{ /* 0xd0 */
451cb7a01acSMauro Carvalho Chehab 		TVP5150_LINE_MODE_INI, 0x00
452cb7a01acSMauro Carvalho Chehab 	},
453cb7a01acSMauro Carvalho Chehab 	{ /* 0xfc */
454cb7a01acSMauro Carvalho Chehab 		TVP5150_FULL_FIELD_MODE_REG, 0x7f
455cb7a01acSMauro Carvalho Chehab 	},
456cb7a01acSMauro Carvalho Chehab 	{ /* end of data */
457cb7a01acSMauro Carvalho Chehab 		0xff, 0xff
458cb7a01acSMauro Carvalho Chehab 	}
459cb7a01acSMauro Carvalho Chehab };
460cb7a01acSMauro Carvalho Chehab 
461cb7a01acSMauro Carvalho Chehab /* Default values as sugested at TVP5150AM1 datasheet */
462cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp5150_init_enable[] = {
463cb7a01acSMauro Carvalho Chehab 	{
464cb7a01acSMauro Carvalho Chehab 		TVP5150_CONF_SHARED_PIN, 2
465cb7a01acSMauro Carvalho Chehab 	}, {	/* Automatic offset and AGC enabled */
466cb7a01acSMauro Carvalho Chehab 		TVP5150_ANAL_CHL_CTL, 0x15
467cb7a01acSMauro Carvalho Chehab 	}, {	/* Activate YCrCb output 0x9 or 0xd ? */
468b4b2de38SLaurent Pinchart 		TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL |
469b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_INTREQ_OE |
470b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_YCBCR_OE |
471b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_SYNC_OE |
472b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_VBLANK |
473b4b2de38SLaurent Pinchart 				  TVP5150_MISC_CTL_CLOCK_OE,
474cb7a01acSMauro Carvalho Chehab 	}, {	/* Activates video std autodetection for all standards */
475cb7a01acSMauro Carvalho Chehab 		TVP5150_AUTOSW_MSK, 0x0
476cb7a01acSMauro Carvalho Chehab 	}, {	/* Default format: 0x47. For 4:2:2: 0x40 */
477cb7a01acSMauro Carvalho Chehab 		TVP5150_DATA_RATE_SEL, 0x47
478cb7a01acSMauro Carvalho Chehab 	}, {
479cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_1, 0x0c
480cb7a01acSMauro Carvalho Chehab 	}, {
481cb7a01acSMauro Carvalho Chehab 		TVP5150_CHROMA_PROC_CTL_2, 0x54
482cb7a01acSMauro Carvalho Chehab 	}, {	/* Non documented, but initialized on WinTV USB2 */
483cb7a01acSMauro Carvalho Chehab 		0x27, 0x20
484cb7a01acSMauro Carvalho Chehab 	}, {
485cb7a01acSMauro Carvalho Chehab 		0xff, 0xff
486cb7a01acSMauro Carvalho Chehab 	}
487cb7a01acSMauro Carvalho Chehab };
488cb7a01acSMauro Carvalho Chehab 
489cb7a01acSMauro Carvalho Chehab struct tvp5150_vbi_type {
490cb7a01acSMauro Carvalho Chehab 	unsigned int vbi_type;
491cb7a01acSMauro Carvalho Chehab 	unsigned int ini_line;
492cb7a01acSMauro Carvalho Chehab 	unsigned int end_line;
493cb7a01acSMauro Carvalho Chehab 	unsigned int by_field :1;
494cb7a01acSMauro Carvalho Chehab };
495cb7a01acSMauro Carvalho Chehab 
496cb7a01acSMauro Carvalho Chehab struct i2c_vbi_ram_value {
497cb7a01acSMauro Carvalho Chehab 	u16 reg;
498cb7a01acSMauro Carvalho Chehab 	struct tvp5150_vbi_type type;
499cb7a01acSMauro Carvalho Chehab 	unsigned char values[16];
500cb7a01acSMauro Carvalho Chehab };
501cb7a01acSMauro Carvalho Chehab 
502cb7a01acSMauro Carvalho Chehab /* This struct have the values for each supported VBI Standard
503cb7a01acSMauro Carvalho Chehab  * by
504cb7a01acSMauro Carvalho Chehab  tvp5150_vbi_types should follow the same order as vbi_ram_default
505cb7a01acSMauro Carvalho Chehab  * value 0 means rom position 0x10, value 1 means rom position 0x30
506cb7a01acSMauro Carvalho Chehab  * and so on. There are 16 possible locations from 0 to 15.
507cb7a01acSMauro Carvalho Chehab  */
508cb7a01acSMauro Carvalho Chehab 
50965dc760bSNasser Afshin static struct i2c_vbi_ram_value vbi_ram_default[] = {
51065dc760bSNasser Afshin 
5119bfd8f88SNasser Afshin 	/*
5129bfd8f88SNasser Afshin 	 * FIXME: Current api doesn't handle all VBI types, those not
5139bfd8f88SNasser Afshin 	 * yet supported are placed under #if 0
5149bfd8f88SNasser Afshin 	 */
515cb7a01acSMauro Carvalho Chehab #if 0
5163dd6b560SMauro Carvalho Chehab 	[0] = {0x010, /* Teletext, SECAM, WST System A */
517cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1},
518cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
519cb7a01acSMauro Carvalho Chehab 		  0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
520cb7a01acSMauro Carvalho Chehab 	},
521cb7a01acSMauro Carvalho Chehab #endif
5223dd6b560SMauro Carvalho Chehab 	[1] = {0x030, /* Teletext, PAL, WST System B */
523cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_B, 6, 22, 1},
524cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
525cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
526cb7a01acSMauro Carvalho Chehab 	},
527cb7a01acSMauro Carvalho Chehab #if 0
5283dd6b560SMauro Carvalho Chehab 	[2] = {0x050, /* Teletext, PAL, WST System C */
529cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_PAL_C, 6, 22, 1},
530cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
531cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
532cb7a01acSMauro Carvalho Chehab 	},
5333dd6b560SMauro Carvalho Chehab 	[3] = {0x070, /* Teletext, NTSC, WST System B */
534cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_NTSC_B, 10, 21, 1},
535cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
536cb7a01acSMauro Carvalho Chehab 		  0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
537cb7a01acSMauro Carvalho Chehab 	},
5383dd6b560SMauro Carvalho Chehab 	[4] = {0x090, /* Tetetext, NTSC NABTS System C */
539cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_NTSC_C, 10, 21, 1},
540cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
541cb7a01acSMauro Carvalho Chehab 		  0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
542cb7a01acSMauro Carvalho Chehab 	},
5433dd6b560SMauro Carvalho Chehab 	[5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */
544cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_TELETEXT_NTSC_D, 10, 21, 1},
545cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
546cb7a01acSMauro Carvalho Chehab 		  0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
547cb7a01acSMauro Carvalho Chehab 	},
5483dd6b560SMauro Carvalho Chehab 	[6] = {0x0d0, /* Closed Caption, PAL/SECAM */
549cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_CAPTION_625, 22, 22, 1},
550cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
551cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
552cb7a01acSMauro Carvalho Chehab 	},
553cb7a01acSMauro Carvalho Chehab #endif
5543dd6b560SMauro Carvalho Chehab 	[7] = {0x0f0, /* Closed Caption, NTSC */
555cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_CAPTION_525, 21, 21, 1},
556cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
557cb7a01acSMauro Carvalho Chehab 		  0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
558cb7a01acSMauro Carvalho Chehab 	},
5593dd6b560SMauro Carvalho Chehab 	[8] = {0x110, /* Wide Screen Signal, PAL/SECAM */
560cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_WSS_625, 23, 23, 1},
561cb7a01acSMauro Carvalho Chehab 		{ 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
562cb7a01acSMauro Carvalho Chehab 		  0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
563cb7a01acSMauro Carvalho Chehab 	},
564cb7a01acSMauro Carvalho Chehab #if 0
5653dd6b560SMauro Carvalho Chehab 	[9] = {0x130, /* Wide Screen Signal, NTSC C */
566cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_WSS_525, 20, 20, 1},
567cb7a01acSMauro Carvalho Chehab 		{ 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
568cb7a01acSMauro Carvalho Chehab 		  0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
569cb7a01acSMauro Carvalho Chehab 	},
5703dd6b560SMauro Carvalho Chehab 	[10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
571cb7a01acSMauro Carvalho Chehab 		{V4l2_SLICED_VITC_625, 6, 22, 0},
572cb7a01acSMauro Carvalho Chehab 		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
573cb7a01acSMauro Carvalho Chehab 		  0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
574cb7a01acSMauro Carvalho Chehab 	},
5753dd6b560SMauro Carvalho Chehab 	[11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */
576cb7a01acSMauro Carvalho Chehab 		{V4l2_SLICED_VITC_525, 10, 20, 0},
577cb7a01acSMauro Carvalho Chehab 		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
578cb7a01acSMauro Carvalho Chehab 		  0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
579cb7a01acSMauro Carvalho Chehab 	},
580cb7a01acSMauro Carvalho Chehab #endif
5813dd6b560SMauro Carvalho Chehab 	[12] = {0x190, /* Video Program System (VPS), PAL */
582cb7a01acSMauro Carvalho Chehab 		{V4L2_SLICED_VPS, 16, 16, 0},
583cb7a01acSMauro Carvalho Chehab 		{ 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
584cb7a01acSMauro Carvalho Chehab 		  0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
585cb7a01acSMauro Carvalho Chehab 	},
586cb7a01acSMauro Carvalho Chehab 	/* 0x1d0 User programmable */
587cb7a01acSMauro Carvalho Chehab };
588cb7a01acSMauro Carvalho Chehab 
589cb7a01acSMauro Carvalho Chehab static int tvp5150_write_inittab(struct v4l2_subdev *sd,
590cb7a01acSMauro Carvalho Chehab 				const struct i2c_reg_value *regs)
591cb7a01acSMauro Carvalho Chehab {
592cb7a01acSMauro Carvalho Chehab 	while (regs->reg != 0xff) {
593cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, regs->reg, regs->value);
594cb7a01acSMauro Carvalho Chehab 		regs++;
595cb7a01acSMauro Carvalho Chehab 	}
596cb7a01acSMauro Carvalho Chehab 	return 0;
597cb7a01acSMauro Carvalho Chehab }
598cb7a01acSMauro Carvalho Chehab 
5993dd6b560SMauro Carvalho Chehab static int tvp5150_vdp_init(struct v4l2_subdev *sd)
600cb7a01acSMauro Carvalho Chehab {
601cb7a01acSMauro Carvalho Chehab 	unsigned int i;
6023dd6b560SMauro Carvalho Chehab 	int j;
603cb7a01acSMauro Carvalho Chehab 
604cb7a01acSMauro Carvalho Chehab 	/* Disable Full Field */
605cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
606cb7a01acSMauro Carvalho Chehab 
607cb7a01acSMauro Carvalho Chehab 	/* Before programming, Line mode should be at 0xff */
608cb7a01acSMauro Carvalho Chehab 	for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
609cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, i, 0xff);
610cb7a01acSMauro Carvalho Chehab 
611cb7a01acSMauro Carvalho Chehab 	/* Load Ram Table */
6123dd6b560SMauro Carvalho Chehab 	for (j = 0; j < ARRAY_SIZE(vbi_ram_default); j++) {
6133dd6b560SMauro Carvalho Chehab 		const struct i2c_vbi_ram_value *regs = &vbi_ram_default[j];
6143dd6b560SMauro Carvalho Chehab 
6153dd6b560SMauro Carvalho Chehab 		if (!regs->type.vbi_type)
6163dd6b560SMauro Carvalho Chehab 			continue;
6173dd6b560SMauro Carvalho Chehab 
618cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
619cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
620cb7a01acSMauro Carvalho Chehab 
621cb7a01acSMauro Carvalho Chehab 		for (i = 0; i < 16; i++)
622cb7a01acSMauro Carvalho Chehab 			tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
623cb7a01acSMauro Carvalho Chehab 	}
624cb7a01acSMauro Carvalho Chehab 	return 0;
625cb7a01acSMauro Carvalho Chehab }
626cb7a01acSMauro Carvalho Chehab 
627cb7a01acSMauro Carvalho Chehab /* Fills VBI capabilities based on i2c_vbi_ram_value struct */
628cb7a01acSMauro Carvalho Chehab static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
629cb7a01acSMauro Carvalho Chehab 				struct v4l2_sliced_vbi_cap *cap)
630cb7a01acSMauro Carvalho Chehab {
6313dd6b560SMauro Carvalho Chehab 	int line, i;
632cb7a01acSMauro Carvalho Chehab 
633257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n");
634bb7a3681SNasser Afshin 	memset(cap, 0, sizeof(*cap));
635cb7a01acSMauro Carvalho Chehab 
6363dd6b560SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
6373dd6b560SMauro Carvalho Chehab 		const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i];
6383dd6b560SMauro Carvalho Chehab 
6393dd6b560SMauro Carvalho Chehab 		if (!regs->type.vbi_type)
6403dd6b560SMauro Carvalho Chehab 			continue;
6413dd6b560SMauro Carvalho Chehab 
6423dd6b560SMauro Carvalho Chehab 		for (line = regs->type.ini_line;
6433dd6b560SMauro Carvalho Chehab 		     line <= regs->type.end_line;
6443dd6b560SMauro Carvalho Chehab 		     line++) {
645cb7a01acSMauro Carvalho Chehab 			cap->service_lines[0][line] |= regs->type.vbi_type;
646cb7a01acSMauro Carvalho Chehab 		}
647cb7a01acSMauro Carvalho Chehab 		cap->service_set |= regs->type.vbi_type;
648cb7a01acSMauro Carvalho Chehab 	}
649cb7a01acSMauro Carvalho Chehab 	return 0;
650cb7a01acSMauro Carvalho Chehab }
651cb7a01acSMauro Carvalho Chehab 
652cb7a01acSMauro Carvalho Chehab /* Set vbi processing
653cb7a01acSMauro Carvalho Chehab  * type - one of tvp5150_vbi_types
654cb7a01acSMauro Carvalho Chehab  * line - line to gather data
655cb7a01acSMauro Carvalho Chehab  * fields: bit 0 field1, bit 1, field2
656cb7a01acSMauro Carvalho Chehab  * flags (default=0xf0) is a bitmask, were set means:
657cb7a01acSMauro Carvalho Chehab  *	bit 7: enable filtering null bytes on CC
658cb7a01acSMauro Carvalho Chehab  *	bit 6: send data also to FIFO
659cb7a01acSMauro Carvalho Chehab  *	bit 5: don't allow data with errors on FIFO
660cb7a01acSMauro Carvalho Chehab  *	bit 4: enable ECC when possible
661cb7a01acSMauro Carvalho Chehab  * pix_align = pix alignment:
662cb7a01acSMauro Carvalho Chehab  *	LSB = field1
663cb7a01acSMauro Carvalho Chehab  *	MSB = field2
664cb7a01acSMauro Carvalho Chehab  */
665cb7a01acSMauro Carvalho Chehab static int tvp5150_set_vbi(struct v4l2_subdev *sd,
666cb7a01acSMauro Carvalho Chehab 			unsigned int type, u8 flags, int line,
667cb7a01acSMauro Carvalho Chehab 			const int fields)
668cb7a01acSMauro Carvalho Chehab {
669cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
670cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std = decoder->norm;
671cb7a01acSMauro Carvalho Chehab 	u8 reg;
6723dd6b560SMauro Carvalho Chehab 	int i, pos = 0;
673cb7a01acSMauro Carvalho Chehab 
674cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_ALL) {
675257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
676cb7a01acSMauro Carvalho Chehab 		return 0;
677cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_625_50) {
678cb7a01acSMauro Carvalho Chehab 		/* Don't follow NTSC Line number convension */
679cb7a01acSMauro Carvalho Chehab 		line += 3;
680cb7a01acSMauro Carvalho Chehab 	}
681cb7a01acSMauro Carvalho Chehab 
682cb7a01acSMauro Carvalho Chehab 	if (line < 6 || line > 27)
683cb7a01acSMauro Carvalho Chehab 		return 0;
684cb7a01acSMauro Carvalho Chehab 
6853dd6b560SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
6863dd6b560SMauro Carvalho Chehab 		const struct i2c_vbi_ram_value *regs =  &vbi_ram_default[i];
6873dd6b560SMauro Carvalho Chehab 
6883dd6b560SMauro Carvalho Chehab 		if (!regs->type.vbi_type)
6893dd6b560SMauro Carvalho Chehab 			continue;
6903dd6b560SMauro Carvalho Chehab 
691cb7a01acSMauro Carvalho Chehab 		if ((type & regs->type.vbi_type) &&
692cb7a01acSMauro Carvalho Chehab 		    (line >= regs->type.ini_line) &&
693b3d930aaSGustavo A. R. Silva 		    (line <= regs->type.end_line))
694cb7a01acSMauro Carvalho Chehab 			break;
695cb7a01acSMauro Carvalho Chehab 		pos++;
696cb7a01acSMauro Carvalho Chehab 	}
697b3d930aaSGustavo A. R. Silva 
698cb7a01acSMauro Carvalho Chehab 	type = pos | (flags & 0xf0);
699cb7a01acSMauro Carvalho Chehab 	reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
700cb7a01acSMauro Carvalho Chehab 
701b3d930aaSGustavo A. R. Silva 	if (fields & 1)
702cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, reg, type);
703cb7a01acSMauro Carvalho Chehab 
704b3d930aaSGustavo A. R. Silva 	if (fields & 2)
705cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, reg + 1, type);
706cb7a01acSMauro Carvalho Chehab 
707cb7a01acSMauro Carvalho Chehab 	return type;
708cb7a01acSMauro Carvalho Chehab }
709cb7a01acSMauro Carvalho Chehab 
7103dd6b560SMauro Carvalho Chehab static int tvp5150_get_vbi(struct v4l2_subdev *sd, int line)
711cb7a01acSMauro Carvalho Chehab {
712cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
713cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std = decoder->norm;
714cb7a01acSMauro Carvalho Chehab 	u8 reg;
715cb7a01acSMauro Carvalho Chehab 	int pos, type = 0;
716cb7a01acSMauro Carvalho Chehab 	int i, ret = 0;
717cb7a01acSMauro Carvalho Chehab 
718cb7a01acSMauro Carvalho Chehab 	if (std == V4L2_STD_ALL) {
719257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
720cb7a01acSMauro Carvalho Chehab 		return 0;
721cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_625_50) {
722cb7a01acSMauro Carvalho Chehab 		/* Don't follow NTSC Line number convension */
723cb7a01acSMauro Carvalho Chehab 		line += 3;
724cb7a01acSMauro Carvalho Chehab 	}
725cb7a01acSMauro Carvalho Chehab 
726cb7a01acSMauro Carvalho Chehab 	if (line < 6 || line > 27)
727cb7a01acSMauro Carvalho Chehab 		return 0;
728cb7a01acSMauro Carvalho Chehab 
729cb7a01acSMauro Carvalho Chehab 	reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
730cb7a01acSMauro Carvalho Chehab 
731cb7a01acSMauro Carvalho Chehab 	for (i = 0; i <= 1; i++) {
732cb7a01acSMauro Carvalho Chehab 		ret = tvp5150_read(sd, reg + i);
733cb7a01acSMauro Carvalho Chehab 		if (ret < 0) {
734257e29f8SMauro Carvalho Chehab 			dev_err(sd->dev, "%s: failed with error = %d\n",
735cb7a01acSMauro Carvalho Chehab 				 __func__, ret);
736cb7a01acSMauro Carvalho Chehab 			return 0;
737cb7a01acSMauro Carvalho Chehab 		}
738cb7a01acSMauro Carvalho Chehab 		pos = ret & 0x0f;
7393dd6b560SMauro Carvalho Chehab 		if (pos < ARRAY_SIZE(vbi_ram_default))
7403dd6b560SMauro Carvalho Chehab 			type |= vbi_ram_default[pos].type.vbi_type;
741cb7a01acSMauro Carvalho Chehab 	}
742cb7a01acSMauro Carvalho Chehab 
743cb7a01acSMauro Carvalho Chehab 	return type;
744cb7a01acSMauro Carvalho Chehab }
745cb7a01acSMauro Carvalho Chehab 
746cb7a01acSMauro Carvalho Chehab static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
747cb7a01acSMauro Carvalho Chehab {
748cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
749cb7a01acSMauro Carvalho Chehab 	int fmt = 0;
750cb7a01acSMauro Carvalho Chehab 
751cb7a01acSMauro Carvalho Chehab 	decoder->norm = std;
752cb7a01acSMauro Carvalho Chehab 
753cb7a01acSMauro Carvalho Chehab 	/* First tests should be against specific std */
754cb7a01acSMauro Carvalho Chehab 
75526811ae0SHans Verkuil 	if (std == V4L2_STD_NTSC_443) {
756cb7a01acSMauro Carvalho Chehab 		fmt = VIDEO_STD_NTSC_4_43_BIT;
75726811ae0SHans Verkuil 	} else if (std == V4L2_STD_PAL_M) {
758cb7a01acSMauro Carvalho Chehab 		fmt = VIDEO_STD_PAL_M_BIT;
75926811ae0SHans Verkuil 	} else if (std == V4L2_STD_PAL_N || std == V4L2_STD_PAL_Nc) {
760cb7a01acSMauro Carvalho Chehab 		fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
761cb7a01acSMauro Carvalho Chehab 	} else {
762cb7a01acSMauro Carvalho Chehab 		/* Then, test against generic ones */
763cb7a01acSMauro Carvalho Chehab 		if (std & V4L2_STD_NTSC)
764cb7a01acSMauro Carvalho Chehab 			fmt = VIDEO_STD_NTSC_MJ_BIT;
765cb7a01acSMauro Carvalho Chehab 		else if (std & V4L2_STD_PAL)
766cb7a01acSMauro Carvalho Chehab 			fmt = VIDEO_STD_PAL_BDGHIN_BIT;
767cb7a01acSMauro Carvalho Chehab 		else if (std & V4L2_STD_SECAM)
768cb7a01acSMauro Carvalho Chehab 			fmt = VIDEO_STD_SECAM_BIT;
769cb7a01acSMauro Carvalho Chehab 	}
770cb7a01acSMauro Carvalho Chehab 
771257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "Set video std register to %d.\n", fmt);
772cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
773cb7a01acSMauro Carvalho Chehab 	return 0;
774cb7a01acSMauro Carvalho Chehab }
775cb7a01acSMauro Carvalho Chehab 
776cb7a01acSMauro Carvalho Chehab static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
777cb7a01acSMauro Carvalho Chehab {
778cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
779cb7a01acSMauro Carvalho Chehab 
780cb7a01acSMauro Carvalho Chehab 	if (decoder->norm == std)
781cb7a01acSMauro Carvalho Chehab 		return 0;
782cb7a01acSMauro Carvalho Chehab 
783cb7a01acSMauro Carvalho Chehab 	/* Change cropping height limits */
784cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_525_60)
785cb7a01acSMauro Carvalho Chehab 		decoder->rect.height = TVP5150_V_MAX_525_60;
786cb7a01acSMauro Carvalho Chehab 	else
787cb7a01acSMauro Carvalho Chehab 		decoder->rect.height = TVP5150_V_MAX_OTHERS;
788cb7a01acSMauro Carvalho Chehab 
789cb7a01acSMauro Carvalho Chehab 
790cb7a01acSMauro Carvalho Chehab 	return tvp5150_set_std(sd, std);
791cb7a01acSMauro Carvalho Chehab }
792cb7a01acSMauro Carvalho Chehab 
793cb7a01acSMauro Carvalho Chehab static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
794cb7a01acSMauro Carvalho Chehab {
795cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
796cb7a01acSMauro Carvalho Chehab 
797cb7a01acSMauro Carvalho Chehab 	/* Initializes TVP5150 to its default values */
798cb7a01acSMauro Carvalho Chehab 	tvp5150_write_inittab(sd, tvp5150_init_default);
799cb7a01acSMauro Carvalho Chehab 
800cb7a01acSMauro Carvalho Chehab 	/* Initializes VDP registers */
8013dd6b560SMauro Carvalho Chehab 	tvp5150_vdp_init(sd);
802cb7a01acSMauro Carvalho Chehab 
803cb7a01acSMauro Carvalho Chehab 	/* Selects decoder input */
804cb7a01acSMauro Carvalho Chehab 	tvp5150_selmux(sd);
805cb7a01acSMauro Carvalho Chehab 
806cb7a01acSMauro Carvalho Chehab 	/* Initializes TVP5150 to stream enabled values */
807cb7a01acSMauro Carvalho Chehab 	tvp5150_write_inittab(sd, tvp5150_init_enable);
808cb7a01acSMauro Carvalho Chehab 
809cb7a01acSMauro Carvalho Chehab 	/* Initialize image preferences */
810cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&decoder->hdl);
811cb7a01acSMauro Carvalho Chehab 
812cb7a01acSMauro Carvalho Chehab 	tvp5150_set_std(sd, decoder->norm);
813a2e5f1b3SJavier Martinez Canillas 
814a2e5f1b3SJavier Martinez Canillas 	if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
815a2e5f1b3SJavier Martinez Canillas 		tvp5150_write(sd, TVP5150_DATA_RATE_SEL, 0x40);
816a2e5f1b3SJavier Martinez Canillas 
817cb7a01acSMauro Carvalho Chehab 	return 0;
818cb7a01acSMauro Carvalho Chehab };
819cb7a01acSMauro Carvalho Chehab 
820cb7a01acSMauro Carvalho Chehab static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
821cb7a01acSMauro Carvalho Chehab {
822cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
823c43875f6SMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
824cb7a01acSMauro Carvalho Chehab 
825cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
826cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
827cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
828cb7a01acSMauro Carvalho Chehab 		return 0;
829cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
830cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
831cb7a01acSMauro Carvalho Chehab 		return 0;
832cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
833cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
834cb7a01acSMauro Carvalho Chehab 		return 0;
835cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
836cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
837d183e4efSLaurent Pinchart 		break;
838c43875f6SMauro Carvalho Chehab 	case V4L2_CID_TEST_PATTERN:
839c43875f6SMauro Carvalho Chehab 		decoder->enable = ctrl->val ? false : true;
840c43875f6SMauro Carvalho Chehab 		tvp5150_selmux(sd);
841cb7a01acSMauro Carvalho Chehab 		return 0;
842cb7a01acSMauro Carvalho Chehab 	}
843cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
844cb7a01acSMauro Carvalho Chehab }
845cb7a01acSMauro Carvalho Chehab 
846cb7a01acSMauro Carvalho Chehab static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
847cb7a01acSMauro Carvalho Chehab {
848cb7a01acSMauro Carvalho Chehab 	int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
849cb7a01acSMauro Carvalho Chehab 
850cb7a01acSMauro Carvalho Chehab 	switch (val & 0x0F) {
851cb7a01acSMauro Carvalho Chehab 	case 0x01:
852cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC;
853cb7a01acSMauro Carvalho Chehab 	case 0x03:
854cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL;
855cb7a01acSMauro Carvalho Chehab 	case 0x05:
856cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_M;
857cb7a01acSMauro Carvalho Chehab 	case 0x07:
858cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
859cb7a01acSMauro Carvalho Chehab 	case 0x09:
860cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_NTSC_443;
861cb7a01acSMauro Carvalho Chehab 	case 0xb:
862cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_SECAM;
863cb7a01acSMauro Carvalho Chehab 	default:
864cb7a01acSMauro Carvalho Chehab 		return V4L2_STD_UNKNOWN;
865cb7a01acSMauro Carvalho Chehab 	}
866cb7a01acSMauro Carvalho Chehab }
867cb7a01acSMauro Carvalho Chehab 
868da298c6dSHans Verkuil static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
869da298c6dSHans Verkuil 		struct v4l2_subdev_pad_config *cfg,
870da298c6dSHans Verkuil 		struct v4l2_subdev_format *format)
871cb7a01acSMauro Carvalho Chehab {
872da298c6dSHans Verkuil 	struct v4l2_mbus_framefmt *f;
873cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
874cb7a01acSMauro Carvalho Chehab 
875bc322c0dSMauro Carvalho Chehab 	if (!format || (format->pad != TVP5150_PAD_VID_OUT))
876cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
877cb7a01acSMauro Carvalho Chehab 
878da298c6dSHans Verkuil 	f = &format->format;
879da298c6dSHans Verkuil 
880cb7a01acSMauro Carvalho Chehab 	f->width = decoder->rect.width;
8811831af09SJavier Martinez Canillas 	f->height = decoder->rect.height / 2;
882cb7a01acSMauro Carvalho Chehab 
883f5fe58fdSBoris BREZILLON 	f->code = MEDIA_BUS_FMT_UYVY8_2X8;
8844f57d27bSLaurent Pinchart 	f->field = V4L2_FIELD_ALTERNATE;
885cb7a01acSMauro Carvalho Chehab 	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
886cb7a01acSMauro Carvalho Chehab 
887257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "width = %d, height = %d\n", f->width,
888cb7a01acSMauro Carvalho Chehab 			f->height);
889cb7a01acSMauro Carvalho Chehab 	return 0;
890cb7a01acSMauro Carvalho Chehab }
891cb7a01acSMauro Carvalho Chehab 
89210d5509cSHans Verkuil static int tvp5150_set_selection(struct v4l2_subdev *sd,
89310d5509cSHans Verkuil 				 struct v4l2_subdev_pad_config *cfg,
89410d5509cSHans Verkuil 				 struct v4l2_subdev_selection *sel)
895cb7a01acSMauro Carvalho Chehab {
896cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
89710d5509cSHans Verkuil 	struct v4l2_rect rect = sel->r;
898cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std;
89910d5509cSHans Verkuil 	int hmax;
90010d5509cSHans Verkuil 
90110d5509cSHans Verkuil 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
90210d5509cSHans Verkuil 	    sel->target != V4L2_SEL_TGT_CROP)
90310d5509cSHans Verkuil 		return -EINVAL;
904cb7a01acSMauro Carvalho Chehab 
905257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
906cb7a01acSMauro Carvalho Chehab 		__func__, rect.left, rect.top, rect.width, rect.height);
907cb7a01acSMauro Carvalho Chehab 
908cb7a01acSMauro Carvalho Chehab 	/* tvp5150 has some special limits */
909cb7a01acSMauro Carvalho Chehab 	rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
910cb7a01acSMauro Carvalho Chehab 	rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
911cb7a01acSMauro Carvalho Chehab 
912cb7a01acSMauro Carvalho Chehab 	/* Calculate height based on current standard */
913cb7a01acSMauro Carvalho Chehab 	if (decoder->norm == V4L2_STD_ALL)
914cb7a01acSMauro Carvalho Chehab 		std = tvp5150_read_std(sd);
915cb7a01acSMauro Carvalho Chehab 	else
916cb7a01acSMauro Carvalho Chehab 		std = decoder->norm;
917cb7a01acSMauro Carvalho Chehab 
918cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_525_60)
919cb7a01acSMauro Carvalho Chehab 		hmax = TVP5150_V_MAX_525_60;
920cb7a01acSMauro Carvalho Chehab 	else
921cb7a01acSMauro Carvalho Chehab 		hmax = TVP5150_V_MAX_OTHERS;
922cb7a01acSMauro Carvalho Chehab 
923bd24db04SMarco Felsch 	/*
924bd24db04SMarco Felsch 	 * alignments:
925bd24db04SMarco Felsch 	 *  - width = 2 due to UYVY colorspace
926bd24db04SMarco Felsch 	 *  - height, image = no special alignment
927bd24db04SMarco Felsch 	 */
928bd24db04SMarco Felsch 	v4l_bound_align_image(&rect.width,
929bd24db04SMarco Felsch 			      TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
930bd24db04SMarco Felsch 			      TVP5150_H_MAX - rect.left, 1, &rect.height,
931cb7a01acSMauro Carvalho Chehab 			      hmax - TVP5150_MAX_CROP_TOP - rect.top,
932bd24db04SMarco Felsch 			      hmax - rect.top, 0, 0);
933cb7a01acSMauro Carvalho Chehab 
934cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
935cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
936cb7a01acSMauro Carvalho Chehab 		      rect.top + rect.height - hmax);
937cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
938cb7a01acSMauro Carvalho Chehab 		      rect.left >> TVP5150_CROP_SHIFT);
939cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
940cb7a01acSMauro Carvalho Chehab 		      rect.left | (1 << TVP5150_CROP_SHIFT));
941cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
942cb7a01acSMauro Carvalho Chehab 		      (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
943cb7a01acSMauro Carvalho Chehab 		      TVP5150_CROP_SHIFT);
944cb7a01acSMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
945cb7a01acSMauro Carvalho Chehab 		      rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
946cb7a01acSMauro Carvalho Chehab 
947cb7a01acSMauro Carvalho Chehab 	decoder->rect = rect;
948cb7a01acSMauro Carvalho Chehab 
949cb7a01acSMauro Carvalho Chehab 	return 0;
950cb7a01acSMauro Carvalho Chehab }
951cb7a01acSMauro Carvalho Chehab 
95210d5509cSHans Verkuil static int tvp5150_get_selection(struct v4l2_subdev *sd,
95310d5509cSHans Verkuil 				 struct v4l2_subdev_pad_config *cfg,
95410d5509cSHans Verkuil 				 struct v4l2_subdev_selection *sel)
955cb7a01acSMauro Carvalho Chehab {
95610d5509cSHans Verkuil 	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
957cb7a01acSMauro Carvalho Chehab 	v4l2_std_id std;
958cb7a01acSMauro Carvalho Chehab 
95910d5509cSHans Verkuil 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
960cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
961cb7a01acSMauro Carvalho Chehab 
96210d5509cSHans Verkuil 	switch (sel->target) {
96310d5509cSHans Verkuil 	case V4L2_SEL_TGT_CROP_BOUNDS:
96410d5509cSHans Verkuil 	case V4L2_SEL_TGT_CROP_DEFAULT:
96510d5509cSHans Verkuil 		sel->r.left = 0;
96610d5509cSHans Verkuil 		sel->r.top = 0;
96710d5509cSHans Verkuil 		sel->r.width = TVP5150_H_MAX;
968cb7a01acSMauro Carvalho Chehab 
969cb7a01acSMauro Carvalho Chehab 		/* Calculate height based on current standard */
970cb7a01acSMauro Carvalho Chehab 		if (decoder->norm == V4L2_STD_ALL)
971cb7a01acSMauro Carvalho Chehab 			std = tvp5150_read_std(sd);
972cb7a01acSMauro Carvalho Chehab 		else
973cb7a01acSMauro Carvalho Chehab 			std = decoder->norm;
974cb7a01acSMauro Carvalho Chehab 		if (std & V4L2_STD_525_60)
97510d5509cSHans Verkuil 			sel->r.height = TVP5150_V_MAX_525_60;
976cb7a01acSMauro Carvalho Chehab 		else
97710d5509cSHans Verkuil 			sel->r.height = TVP5150_V_MAX_OTHERS;
978cb7a01acSMauro Carvalho Chehab 		return 0;
97910d5509cSHans Verkuil 	case V4L2_SEL_TGT_CROP:
98010d5509cSHans Verkuil 		sel->r = decoder->rect;
98110d5509cSHans Verkuil 		return 0;
98210d5509cSHans Verkuil 	default:
98310d5509cSHans Verkuil 		return -EINVAL;
98410d5509cSHans Verkuil 	}
985cb7a01acSMauro Carvalho Chehab }
986cb7a01acSMauro Carvalho Chehab 
987dd3a46bbSLaurent Pinchart static int tvp5150_g_mbus_config(struct v4l2_subdev *sd,
988dd3a46bbSLaurent Pinchart 				 struct v4l2_mbus_config *cfg)
989dd3a46bbSLaurent Pinchart {
990a2e5f1b3SJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
991a2e5f1b3SJavier Martinez Canillas 
992a2e5f1b3SJavier Martinez Canillas 	cfg->type = decoder->mbus_type;
993dd3a46bbSLaurent Pinchart 	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING
994dd3a46bbSLaurent Pinchart 		   | V4L2_MBUS_FIELD_EVEN_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH;
995dd3a46bbSLaurent Pinchart 
996dd3a46bbSLaurent Pinchart 	return 0;
997dd3a46bbSLaurent Pinchart }
998dd3a46bbSLaurent Pinchart 
999cb7a01acSMauro Carvalho Chehab /****************************************************************************
1000e545ac87SLaurent Pinchart 			V4L2 subdev pad ops
1001e545ac87SLaurent Pinchart  ****************************************************************************/
1002e545ac87SLaurent Pinchart static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
1003e545ac87SLaurent Pinchart 		struct v4l2_subdev_pad_config *cfg,
1004e545ac87SLaurent Pinchart 		struct v4l2_subdev_mbus_code_enum *code)
1005e545ac87SLaurent Pinchart {
1006e545ac87SLaurent Pinchart 	if (code->pad || code->index)
1007e545ac87SLaurent Pinchart 		return -EINVAL;
1008e545ac87SLaurent Pinchart 
1009e545ac87SLaurent Pinchart 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
1010e545ac87SLaurent Pinchart 	return 0;
1011e545ac87SLaurent Pinchart }
1012e545ac87SLaurent Pinchart 
1013e545ac87SLaurent Pinchart static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
1014e545ac87SLaurent Pinchart 				   struct v4l2_subdev_pad_config *cfg,
1015e545ac87SLaurent Pinchart 				   struct v4l2_subdev_frame_size_enum *fse)
1016e545ac87SLaurent Pinchart {
1017e545ac87SLaurent Pinchart 	struct tvp5150 *decoder = to_tvp5150(sd);
1018e545ac87SLaurent Pinchart 
1019e545ac87SLaurent Pinchart 	if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_UYVY8_2X8)
1020e545ac87SLaurent Pinchart 		return -EINVAL;
1021e545ac87SLaurent Pinchart 
1022e545ac87SLaurent Pinchart 	fse->code = MEDIA_BUS_FMT_UYVY8_2X8;
1023e545ac87SLaurent Pinchart 	fse->min_width = decoder->rect.width;
1024e545ac87SLaurent Pinchart 	fse->max_width = decoder->rect.width;
1025e545ac87SLaurent Pinchart 	fse->min_height = decoder->rect.height / 2;
1026e545ac87SLaurent Pinchart 	fse->max_height = decoder->rect.height / 2;
1027e545ac87SLaurent Pinchart 
1028e545ac87SLaurent Pinchart 	return 0;
1029e545ac87SLaurent Pinchart }
1030e545ac87SLaurent Pinchart 
1031e545ac87SLaurent Pinchart /****************************************************************************
1032f7b4b54eSJavier Martinez Canillas 			Media entity ops
1033f7b4b54eSJavier Martinez Canillas  ****************************************************************************/
1034f7b4b54eSJavier Martinez Canillas 
1035406ff67dSLaurent Pinchart #ifdef CONFIG_MEDIA_CONTROLLER
1036f7b4b54eSJavier Martinez Canillas static int tvp5150_link_setup(struct media_entity *entity,
1037f7b4b54eSJavier Martinez Canillas 			      const struct media_pad *local,
1038f7b4b54eSJavier Martinez Canillas 			      const struct media_pad *remote, u32 flags)
1039f7b4b54eSJavier Martinez Canillas {
1040f7b4b54eSJavier Martinez Canillas 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
1041f7b4b54eSJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
1042f7b4b54eSJavier Martinez Canillas 	int i;
1043f7b4b54eSJavier Martinez Canillas 
1044f7b4b54eSJavier Martinez Canillas 	for (i = 0; i < TVP5150_INPUT_NUM; i++) {
1045f7b4b54eSJavier Martinez Canillas 		if (remote->entity == &decoder->input_ent[i])
1046f7b4b54eSJavier Martinez Canillas 			break;
1047f7b4b54eSJavier Martinez Canillas 	}
1048f7b4b54eSJavier Martinez Canillas 
1049f7b4b54eSJavier Martinez Canillas 	/* Do nothing for entities that are not input connectors */
1050f7b4b54eSJavier Martinez Canillas 	if (i == TVP5150_INPUT_NUM)
1051f7b4b54eSJavier Martinez Canillas 		return 0;
1052f7b4b54eSJavier Martinez Canillas 
1053f7b4b54eSJavier Martinez Canillas 	decoder->input = i;
1054f7b4b54eSJavier Martinez Canillas 
1055f7b4b54eSJavier Martinez Canillas 	tvp5150_selmux(sd);
1056f7b4b54eSJavier Martinez Canillas 
1057f7b4b54eSJavier Martinez Canillas 	return 0;
1058f7b4b54eSJavier Martinez Canillas }
1059f7b4b54eSJavier Martinez Canillas 
1060f7b4b54eSJavier Martinez Canillas static const struct media_entity_operations tvp5150_sd_media_ops = {
1061f7b4b54eSJavier Martinez Canillas 	.link_setup = tvp5150_link_setup,
1062f7b4b54eSJavier Martinez Canillas };
1063406ff67dSLaurent Pinchart #endif
1064f7b4b54eSJavier Martinez Canillas 
1065f7b4b54eSJavier Martinez Canillas /****************************************************************************
1066cb7a01acSMauro Carvalho Chehab 			I2C Command
1067cb7a01acSMauro Carvalho Chehab  ****************************************************************************/
1068cb7a01acSMauro Carvalho Chehab 
1069460b6c08SLaurent Pinchart static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
1070460b6c08SLaurent Pinchart {
1071a2e5f1b3SJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
107279d6205aSLaurent Pinchart 	int val;
1073a2e5f1b3SJavier Martinez Canillas 
107479d6205aSLaurent Pinchart 	/* Enable or disable the video output signals. */
107579d6205aSLaurent Pinchart 	val = tvp5150_read(sd, TVP5150_MISC_CTL);
107679d6205aSLaurent Pinchart 	if (val < 0)
107779d6205aSLaurent Pinchart 		return val;
107879d6205aSLaurent Pinchart 
107979d6205aSLaurent Pinchart 	val &= ~(TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE |
108079d6205aSLaurent Pinchart 		 TVP5150_MISC_CTL_CLOCK_OE);
108179d6205aSLaurent Pinchart 
108279d6205aSLaurent Pinchart 	if (enable) {
108379d6205aSLaurent Pinchart 		/*
108479d6205aSLaurent Pinchart 		 * Enable the YCbCr and clock outputs. In discrete sync mode
108579d6205aSLaurent Pinchart 		 * (non-BT.656) additionally enable the the sync outputs.
108679d6205aSLaurent Pinchart 		 */
108779d6205aSLaurent Pinchart 		val |= TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE;
1088841502d7SMauro Carvalho Chehab 		if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
108979d6205aSLaurent Pinchart 			val |= TVP5150_MISC_CTL_SYNC_OE;
109079d6205aSLaurent Pinchart 	}
1091a2e5f1b3SJavier Martinez Canillas 
1092841502d7SMauro Carvalho Chehab 	tvp5150_write(sd, TVP5150_MISC_CTL, val);
1093460b6c08SLaurent Pinchart 
1094460b6c08SLaurent Pinchart 	return 0;
1095460b6c08SLaurent Pinchart }
1096460b6c08SLaurent Pinchart 
1097cb7a01acSMauro Carvalho Chehab static int tvp5150_s_routing(struct v4l2_subdev *sd,
1098cb7a01acSMauro Carvalho Chehab 			     u32 input, u32 output, u32 config)
1099cb7a01acSMauro Carvalho Chehab {
1100cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
1101cb7a01acSMauro Carvalho Chehab 
1102cb7a01acSMauro Carvalho Chehab 	decoder->input = input;
1103cb7a01acSMauro Carvalho Chehab 	decoder->output = output;
1104c43875f6SMauro Carvalho Chehab 
1105c43875f6SMauro Carvalho Chehab 	if (output == TVP5150_BLACK_SCREEN)
1106c43875f6SMauro Carvalho Chehab 		decoder->enable = false;
1107c43875f6SMauro Carvalho Chehab 	else
1108c43875f6SMauro Carvalho Chehab 		decoder->enable = true;
1109c43875f6SMauro Carvalho Chehab 
1110cb7a01acSMauro Carvalho Chehab 	tvp5150_selmux(sd);
1111cb7a01acSMauro Carvalho Chehab 	return 0;
1112cb7a01acSMauro Carvalho Chehab }
1113cb7a01acSMauro Carvalho Chehab 
1114cb7a01acSMauro Carvalho Chehab static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
1115cb7a01acSMauro Carvalho Chehab {
11169bfd8f88SNasser Afshin 	/*
11179bfd8f88SNasser Afshin 	 * this is for capturing 36 raw vbi lines
11189bfd8f88SNasser Afshin 	 * if there's a way to cut off the beginning 2 vbi lines
11199bfd8f88SNasser Afshin 	 * with the tvp5150 then the vbi line count could be lowered
11209bfd8f88SNasser Afshin 	 * to 17 lines/field again, although I couldn't find a register
11219bfd8f88SNasser Afshin 	 * which could do that cropping
11229bfd8f88SNasser Afshin 	 */
11239bfd8f88SNasser Afshin 
1124cb7a01acSMauro Carvalho Chehab 	if (fmt->sample_format == V4L2_PIX_FMT_GREY)
1125cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
1126cb7a01acSMauro Carvalho Chehab 	if (fmt->count[0] == 18 && fmt->count[1] == 18) {
1127cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
1128cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
1129cb7a01acSMauro Carvalho Chehab 	}
1130cb7a01acSMauro Carvalho Chehab 	return 0;
1131cb7a01acSMauro Carvalho Chehab }
1132cb7a01acSMauro Carvalho Chehab 
1133cb7a01acSMauro Carvalho Chehab static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1134cb7a01acSMauro Carvalho Chehab {
1135cb7a01acSMauro Carvalho Chehab 	int i;
1136cb7a01acSMauro Carvalho Chehab 
1137cb7a01acSMauro Carvalho Chehab 	if (svbi->service_set != 0) {
1138cb7a01acSMauro Carvalho Chehab 		for (i = 0; i <= 23; i++) {
1139cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[1][i] = 0;
1140cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] =
11413dd6b560SMauro Carvalho Chehab 				tvp5150_set_vbi(sd, svbi->service_lines[0][i],
11423dd6b560SMauro Carvalho Chehab 						0xf0, i, 3);
1143cb7a01acSMauro Carvalho Chehab 		}
1144cb7a01acSMauro Carvalho Chehab 		/* Enables FIFO */
1145cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
1146cb7a01acSMauro Carvalho Chehab 	} else {
1147cb7a01acSMauro Carvalho Chehab 		/* Disables FIFO*/
1148cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
1149cb7a01acSMauro Carvalho Chehab 
1150cb7a01acSMauro Carvalho Chehab 		/* Disable Full Field */
1151cb7a01acSMauro Carvalho Chehab 		tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
1152cb7a01acSMauro Carvalho Chehab 
1153cb7a01acSMauro Carvalho Chehab 		/* Disable Line modes */
1154cb7a01acSMauro Carvalho Chehab 		for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
1155cb7a01acSMauro Carvalho Chehab 			tvp5150_write(sd, i, 0xff);
1156cb7a01acSMauro Carvalho Chehab 	}
1157cb7a01acSMauro Carvalho Chehab 	return 0;
1158cb7a01acSMauro Carvalho Chehab }
1159cb7a01acSMauro Carvalho Chehab 
1160cb7a01acSMauro Carvalho Chehab static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1161cb7a01acSMauro Carvalho Chehab {
1162cb7a01acSMauro Carvalho Chehab 	int i, mask = 0;
1163cb7a01acSMauro Carvalho Chehab 
116430634e8eSHans Verkuil 	memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
1165cb7a01acSMauro Carvalho Chehab 
1166cb7a01acSMauro Carvalho Chehab 	for (i = 0; i <= 23; i++) {
1167cb7a01acSMauro Carvalho Chehab 		svbi->service_lines[0][i] =
11683dd6b560SMauro Carvalho Chehab 			tvp5150_get_vbi(sd, i);
1169cb7a01acSMauro Carvalho Chehab 		mask |= svbi->service_lines[0][i];
1170cb7a01acSMauro Carvalho Chehab 	}
1171cb7a01acSMauro Carvalho Chehab 	svbi->service_set = mask;
1172cb7a01acSMauro Carvalho Chehab 	return 0;
1173cb7a01acSMauro Carvalho Chehab }
1174cb7a01acSMauro Carvalho Chehab 
1175cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1176cb7a01acSMauro Carvalho Chehab static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1177cb7a01acSMauro Carvalho Chehab {
1178cb7a01acSMauro Carvalho Chehab 	int res;
1179cb7a01acSMauro Carvalho Chehab 
1180cb7a01acSMauro Carvalho Chehab 	res = tvp5150_read(sd, reg->reg & 0xff);
1181cb7a01acSMauro Carvalho Chehab 	if (res < 0) {
1182257e29f8SMauro Carvalho Chehab 		dev_err(sd->dev, "%s: failed with error = %d\n", __func__, res);
1183cb7a01acSMauro Carvalho Chehab 		return res;
1184cb7a01acSMauro Carvalho Chehab 	}
1185cb7a01acSMauro Carvalho Chehab 
1186cb7a01acSMauro Carvalho Chehab 	reg->val = res;
1187cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
1188cb7a01acSMauro Carvalho Chehab 	return 0;
1189cb7a01acSMauro Carvalho Chehab }
1190cb7a01acSMauro Carvalho Chehab 
1191977ba3b1SHans Verkuil static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
1192cb7a01acSMauro Carvalho Chehab {
1193eca4ca84SJavier Martinez Canillas 	return tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
1194cb7a01acSMauro Carvalho Chehab }
1195cb7a01acSMauro Carvalho Chehab #endif
1196cb7a01acSMauro Carvalho Chehab 
1197cb7a01acSMauro Carvalho Chehab static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
1198cb7a01acSMauro Carvalho Chehab {
1199cb7a01acSMauro Carvalho Chehab 	int status = tvp5150_read(sd, 0x88);
1200cb7a01acSMauro Carvalho Chehab 
1201cb7a01acSMauro Carvalho Chehab 	vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
1202cb7a01acSMauro Carvalho Chehab 	return 0;
1203cb7a01acSMauro Carvalho Chehab }
1204cb7a01acSMauro Carvalho Chehab 
12055a08bc00SJavier Martinez Canillas static int tvp5150_registered(struct v4l2_subdev *sd)
1206f7b4b54eSJavier Martinez Canillas {
1207f7b4b54eSJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
1208f7b4b54eSJavier Martinez Canillas 	struct tvp5150 *decoder = to_tvp5150(sd);
1209f7b4b54eSJavier Martinez Canillas 	int ret = 0;
1210f7b4b54eSJavier Martinez Canillas 	int i;
1211f7b4b54eSJavier Martinez Canillas 
1212f7b4b54eSJavier Martinez Canillas 	for (i = 0; i < TVP5150_INPUT_NUM; i++) {
1213f7b4b54eSJavier Martinez Canillas 		struct media_entity *input = &decoder->input_ent[i];
1214f7b4b54eSJavier Martinez Canillas 		struct media_pad *pad = &decoder->input_pad[i];
1215f7b4b54eSJavier Martinez Canillas 
1216f7b4b54eSJavier Martinez Canillas 		if (!input->name)
1217f7b4b54eSJavier Martinez Canillas 			continue;
1218f7b4b54eSJavier Martinez Canillas 
1219f7b4b54eSJavier Martinez Canillas 		decoder->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
1220f7b4b54eSJavier Martinez Canillas 
1221f7b4b54eSJavier Martinez Canillas 		ret = media_entity_pads_init(input, 1, pad);
1222f7b4b54eSJavier Martinez Canillas 		if (ret < 0)
1223f7b4b54eSJavier Martinez Canillas 			return ret;
1224f7b4b54eSJavier Martinez Canillas 
1225f7b4b54eSJavier Martinez Canillas 		ret = media_device_register_entity(sd->v4l2_dev->mdev, input);
1226f7b4b54eSJavier Martinez Canillas 		if (ret < 0)
1227f7b4b54eSJavier Martinez Canillas 			return ret;
1228f7b4b54eSJavier Martinez Canillas 
1229f7b4b54eSJavier Martinez Canillas 		ret = media_create_pad_link(input, 0, &sd->entity,
1230bc322c0dSMauro Carvalho Chehab 					    TVP5150_PAD_IF_INPUT, 0);
1231f7b4b54eSJavier Martinez Canillas 		if (ret < 0) {
1232f7b4b54eSJavier Martinez Canillas 			media_device_unregister_entity(input);
1233f7b4b54eSJavier Martinez Canillas 			return ret;
1234f7b4b54eSJavier Martinez Canillas 		}
1235f7b4b54eSJavier Martinez Canillas 	}
1236f7b4b54eSJavier Martinez Canillas #endif
1237f7b4b54eSJavier Martinez Canillas 
1238f7b4b54eSJavier Martinez Canillas 	return 0;
1239f7b4b54eSJavier Martinez Canillas }
1240f7b4b54eSJavier Martinez Canillas 
1241cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1242cb7a01acSMauro Carvalho Chehab 
1243cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
1244cb7a01acSMauro Carvalho Chehab 	.s_ctrl = tvp5150_s_ctrl,
1245cb7a01acSMauro Carvalho Chehab };
1246cb7a01acSMauro Carvalho Chehab 
1247cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
1248cb7a01acSMauro Carvalho Chehab 	.log_status = tvp5150_log_status,
1249cb7a01acSMauro Carvalho Chehab 	.reset = tvp5150_reset,
1250cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1251cb7a01acSMauro Carvalho Chehab 	.g_register = tvp5150_g_register,
1252cb7a01acSMauro Carvalho Chehab 	.s_register = tvp5150_s_register,
1253cb7a01acSMauro Carvalho Chehab #endif
1254cb7a01acSMauro Carvalho Chehab };
1255cb7a01acSMauro Carvalho Chehab 
1256cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
1257cb7a01acSMauro Carvalho Chehab 	.g_tuner = tvp5150_g_tuner,
1258cb7a01acSMauro Carvalho Chehab };
1259cb7a01acSMauro Carvalho Chehab 
1260cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
12618774bed9SLaurent Pinchart 	.s_std = tvp5150_s_std,
1262460b6c08SLaurent Pinchart 	.s_stream = tvp5150_s_stream,
1263cb7a01acSMauro Carvalho Chehab 	.s_routing = tvp5150_s_routing,
1264dd3a46bbSLaurent Pinchart 	.g_mbus_config = tvp5150_g_mbus_config,
1265cb7a01acSMauro Carvalho Chehab };
1266cb7a01acSMauro Carvalho Chehab 
1267cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
1268cb7a01acSMauro Carvalho Chehab 	.g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
1269cb7a01acSMauro Carvalho Chehab 	.g_sliced_fmt = tvp5150_g_sliced_fmt,
1270cb7a01acSMauro Carvalho Chehab 	.s_sliced_fmt = tvp5150_s_sliced_fmt,
1271cb7a01acSMauro Carvalho Chehab 	.s_raw_fmt = tvp5150_s_raw_fmt,
1272cb7a01acSMauro Carvalho Chehab };
1273cb7a01acSMauro Carvalho Chehab 
1274ebcff5fcSHans Verkuil static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
1275ebcff5fcSHans Verkuil 	.enum_mbus_code = tvp5150_enum_mbus_code,
1276e545ac87SLaurent Pinchart 	.enum_frame_size = tvp5150_enum_frame_size,
1277da298c6dSHans Verkuil 	.set_fmt = tvp5150_fill_fmt,
1278da298c6dSHans Verkuil 	.get_fmt = tvp5150_fill_fmt,
127910d5509cSHans Verkuil 	.get_selection = tvp5150_get_selection,
128010d5509cSHans Verkuil 	.set_selection = tvp5150_set_selection,
1281ebcff5fcSHans Verkuil };
1282ebcff5fcSHans Verkuil 
1283cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops tvp5150_ops = {
1284cb7a01acSMauro Carvalho Chehab 	.core = &tvp5150_core_ops,
1285cb7a01acSMauro Carvalho Chehab 	.tuner = &tvp5150_tuner_ops,
1286cb7a01acSMauro Carvalho Chehab 	.video = &tvp5150_video_ops,
1287cb7a01acSMauro Carvalho Chehab 	.vbi = &tvp5150_vbi_ops,
1288ebcff5fcSHans Verkuil 	.pad = &tvp5150_pad_ops,
1289cb7a01acSMauro Carvalho Chehab };
1290cb7a01acSMauro Carvalho Chehab 
12915a08bc00SJavier Martinez Canillas static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
12925a08bc00SJavier Martinez Canillas 	.registered = tvp5150_registered,
12935a08bc00SJavier Martinez Canillas };
12945a08bc00SJavier Martinez Canillas 
1295cb7a01acSMauro Carvalho Chehab 
1296cb7a01acSMauro Carvalho Chehab /****************************************************************************
1297cb7a01acSMauro Carvalho Chehab 			I2C Client & Driver
1298cb7a01acSMauro Carvalho Chehab  ****************************************************************************/
1299cb7a01acSMauro Carvalho Chehab 
13007871597aSLaurent Pinchart static int tvp5150_detect_version(struct tvp5150 *core)
13017871597aSLaurent Pinchart {
13027871597aSLaurent Pinchart 	struct v4l2_subdev *sd = &core->sd;
13037871597aSLaurent Pinchart 	struct i2c_client *c = v4l2_get_subdevdata(sd);
13047871597aSLaurent Pinchart 	unsigned int i;
13057871597aSLaurent Pinchart 	u8 regs[4];
13067871597aSLaurent Pinchart 	int res;
13077871597aSLaurent Pinchart 
13087871597aSLaurent Pinchart 	/*
13097871597aSLaurent Pinchart 	 * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
13107871597aSLaurent Pinchart 	 * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
13117871597aSLaurent Pinchart 	 */
13127871597aSLaurent Pinchart 	for (i = 0; i < 4; i++) {
13137871597aSLaurent Pinchart 		res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
13147871597aSLaurent Pinchart 		if (res < 0)
13157871597aSLaurent Pinchart 			return res;
13167871597aSLaurent Pinchart 		regs[i] = res;
13177871597aSLaurent Pinchart 	}
13187871597aSLaurent Pinchart 
131982275133SJavier Martinez Canillas 	core->dev_id = (regs[0] << 8) | regs[1];
132082275133SJavier Martinez Canillas 	core->rom_ver = (regs[2] << 8) | regs[3];
13217871597aSLaurent Pinchart 
1322257e29f8SMauro Carvalho Chehab 	dev_info(sd->dev, "tvp%04x (%u.%u) chip found @ 0x%02x (%s)\n",
132382275133SJavier Martinez Canillas 		  core->dev_id, regs[2], regs[3], c->addr << 1,
132482275133SJavier Martinez Canillas 		  c->adapter->name);
13257871597aSLaurent Pinchart 
132682275133SJavier Martinez Canillas 	if (core->dev_id == 0x5150 && core->rom_ver == 0x0321) {
1327257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "tvp5150a detected.\n");
132882275133SJavier Martinez Canillas 	} else if (core->dev_id == 0x5150 && core->rom_ver == 0x0400) {
1329257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "tvp5150am1 detected.\n");
13307871597aSLaurent Pinchart 
13317871597aSLaurent Pinchart 		/* ITU-T BT.656.4 timing */
13327871597aSLaurent Pinchart 		tvp5150_write(sd, TVP5150_REV_SELECT, 0);
133382275133SJavier Martinez Canillas 	} else if (core->dev_id == 0x5151 && core->rom_ver == 0x0100) {
1334257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "tvp5151 detected.\n");
13357871597aSLaurent Pinchart 	} else {
1336257e29f8SMauro Carvalho Chehab 		dev_info(sd->dev, "*** unknown tvp%04x chip detected.\n",
133782275133SJavier Martinez Canillas 			  core->dev_id);
13387871597aSLaurent Pinchart 	}
13397871597aSLaurent Pinchart 
13407871597aSLaurent Pinchart 	return 0;
13417871597aSLaurent Pinchart }
13427871597aSLaurent Pinchart 
134309aa2609SJavier Martinez Canillas static int tvp5150_init(struct i2c_client *c)
134409aa2609SJavier Martinez Canillas {
134509aa2609SJavier Martinez Canillas 	struct gpio_desc *pdn_gpio;
134609aa2609SJavier Martinez Canillas 	struct gpio_desc *reset_gpio;
134709aa2609SJavier Martinez Canillas 
134809aa2609SJavier Martinez Canillas 	pdn_gpio = devm_gpiod_get_optional(&c->dev, "pdn", GPIOD_OUT_HIGH);
134909aa2609SJavier Martinez Canillas 	if (IS_ERR(pdn_gpio))
135009aa2609SJavier Martinez Canillas 		return PTR_ERR(pdn_gpio);
135109aa2609SJavier Martinez Canillas 
135209aa2609SJavier Martinez Canillas 	if (pdn_gpio) {
135309aa2609SJavier Martinez Canillas 		gpiod_set_value_cansleep(pdn_gpio, 0);
135409aa2609SJavier Martinez Canillas 		/* Delay time between power supplies active and reset */
135509aa2609SJavier Martinez Canillas 		msleep(20);
135609aa2609SJavier Martinez Canillas 	}
135709aa2609SJavier Martinez Canillas 
135809aa2609SJavier Martinez Canillas 	reset_gpio = devm_gpiod_get_optional(&c->dev, "reset", GPIOD_OUT_HIGH);
135909aa2609SJavier Martinez Canillas 	if (IS_ERR(reset_gpio))
136009aa2609SJavier Martinez Canillas 		return PTR_ERR(reset_gpio);
136109aa2609SJavier Martinez Canillas 
136209aa2609SJavier Martinez Canillas 	if (reset_gpio) {
136309aa2609SJavier Martinez Canillas 		/* RESETB pulse duration */
136409aa2609SJavier Martinez Canillas 		ndelay(500);
136509aa2609SJavier Martinez Canillas 		gpiod_set_value_cansleep(reset_gpio, 0);
136609aa2609SJavier Martinez Canillas 		/* Delay time between end of reset to I2C active */
136709aa2609SJavier Martinez Canillas 		usleep_range(200, 250);
136809aa2609SJavier Martinez Canillas 	}
136909aa2609SJavier Martinez Canillas 
137009aa2609SJavier Martinez Canillas 	return 0;
137109aa2609SJavier Martinez Canillas }
137209aa2609SJavier Martinez Canillas 
1373a2e5f1b3SJavier Martinez Canillas static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
1374a2e5f1b3SJavier Martinez Canillas {
1375859969b3SSakari Ailus 	struct v4l2_fwnode_endpoint bus_cfg;
1376a2e5f1b3SJavier Martinez Canillas 	struct device_node *ep;
1377f7b4b54eSJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
1378f7b4b54eSJavier Martinez Canillas 	struct device_node *connectors, *child;
1379f7b4b54eSJavier Martinez Canillas 	struct media_entity *input;
1380f7b4b54eSJavier Martinez Canillas 	const char *name;
1381f7b4b54eSJavier Martinez Canillas 	u32 input_type;
1382f7b4b54eSJavier Martinez Canillas #endif
1383a2e5f1b3SJavier Martinez Canillas 	unsigned int flags;
1384a2e5f1b3SJavier Martinez Canillas 	int ret = 0;
1385a2e5f1b3SJavier Martinez Canillas 
1386a2e5f1b3SJavier Martinez Canillas 	ep = of_graph_get_next_endpoint(np, NULL);
1387a2e5f1b3SJavier Martinez Canillas 	if (!ep)
1388a2e5f1b3SJavier Martinez Canillas 		return -EINVAL;
1389a2e5f1b3SJavier Martinez Canillas 
1390859969b3SSakari Ailus 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
1391a2e5f1b3SJavier Martinez Canillas 	if (ret)
1392a2e5f1b3SJavier Martinez Canillas 		goto err;
1393a2e5f1b3SJavier Martinez Canillas 
1394a2e5f1b3SJavier Martinez Canillas 	flags = bus_cfg.bus.parallel.flags;
1395a2e5f1b3SJavier Martinez Canillas 
1396a2e5f1b3SJavier Martinez Canillas 	if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL &&
1397a2e5f1b3SJavier Martinez Canillas 	    !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH &&
1398a2e5f1b3SJavier Martinez Canillas 	      flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH &&
13992bd5e437SJavier Martinez Canillas 	      flags & V4L2_MBUS_FIELD_EVEN_LOW)) {
14002bd5e437SJavier Martinez Canillas 		ret = -EINVAL;
14012bd5e437SJavier Martinez Canillas 		goto err;
14022bd5e437SJavier Martinez Canillas 	}
1403a2e5f1b3SJavier Martinez Canillas 
1404a2e5f1b3SJavier Martinez Canillas 	decoder->mbus_type = bus_cfg.bus_type;
1405a2e5f1b3SJavier Martinez Canillas 
1406f7b4b54eSJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
1407f7b4b54eSJavier Martinez Canillas 	connectors = of_get_child_by_name(np, "connectors");
1408f7b4b54eSJavier Martinez Canillas 
1409f7b4b54eSJavier Martinez Canillas 	if (!connectors)
1410f7b4b54eSJavier Martinez Canillas 		goto err;
1411f7b4b54eSJavier Martinez Canillas 
1412f7b4b54eSJavier Martinez Canillas 	for_each_available_child_of_node(connectors, child) {
1413f7b4b54eSJavier Martinez Canillas 		ret = of_property_read_u32(child, "input", &input_type);
1414f7b4b54eSJavier Martinez Canillas 		if (ret) {
1415257e29f8SMauro Carvalho Chehab 			dev_err(decoder->sd.dev,
1416f764e6d6SRob Herring 				 "missing type property in node %pOFn\n",
1417f764e6d6SRob Herring 				 child);
1418f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1419f7b4b54eSJavier Martinez Canillas 		}
1420f7b4b54eSJavier Martinez Canillas 
142160ad7689SMauro Carvalho Chehab 		if (input_type >= TVP5150_INPUT_NUM) {
1422f7b4b54eSJavier Martinez Canillas 			ret = -EINVAL;
1423f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1424f7b4b54eSJavier Martinez Canillas 		}
1425f7b4b54eSJavier Martinez Canillas 
1426f7b4b54eSJavier Martinez Canillas 		input = &decoder->input_ent[input_type];
1427f7b4b54eSJavier Martinez Canillas 
1428f7b4b54eSJavier Martinez Canillas 		/* Each input connector can only be defined once */
1429f7b4b54eSJavier Martinez Canillas 		if (input->name) {
1430257e29f8SMauro Carvalho Chehab 			dev_err(decoder->sd.dev,
1431f7b4b54eSJavier Martinez Canillas 				 "input %s with same type already exists\n",
1432f7b4b54eSJavier Martinez Canillas 				 input->name);
1433f7b4b54eSJavier Martinez Canillas 			ret = -EINVAL;
1434f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1435f7b4b54eSJavier Martinez Canillas 		}
1436f7b4b54eSJavier Martinez Canillas 
1437f7b4b54eSJavier Martinez Canillas 		switch (input_type) {
1438f7b4b54eSJavier Martinez Canillas 		case TVP5150_COMPOSITE0:
1439f7b4b54eSJavier Martinez Canillas 		case TVP5150_COMPOSITE1:
1440f7b4b54eSJavier Martinez Canillas 			input->function = MEDIA_ENT_F_CONN_COMPOSITE;
1441f7b4b54eSJavier Martinez Canillas 			break;
1442f7b4b54eSJavier Martinez Canillas 		case TVP5150_SVIDEO:
1443f7b4b54eSJavier Martinez Canillas 			input->function = MEDIA_ENT_F_CONN_SVIDEO;
1444f7b4b54eSJavier Martinez Canillas 			break;
1445f7b4b54eSJavier Martinez Canillas 		}
1446f7b4b54eSJavier Martinez Canillas 
1447f7b4b54eSJavier Martinez Canillas 		input->flags = MEDIA_ENT_FL_CONNECTOR;
1448f7b4b54eSJavier Martinez Canillas 
1449f7b4b54eSJavier Martinez Canillas 		ret = of_property_read_string(child, "label", &name);
1450f7b4b54eSJavier Martinez Canillas 		if (ret < 0) {
1451257e29f8SMauro Carvalho Chehab 			dev_err(decoder->sd.dev,
1452f764e6d6SRob Herring 				 "missing label property in node %pOFn\n",
1453f764e6d6SRob Herring 				 child);
1454f7b4b54eSJavier Martinez Canillas 			goto err_connector;
1455f7b4b54eSJavier Martinez Canillas 		}
1456f7b4b54eSJavier Martinez Canillas 
1457f7b4b54eSJavier Martinez Canillas 		input->name = name;
1458f7b4b54eSJavier Martinez Canillas 	}
1459f7b4b54eSJavier Martinez Canillas 
1460f7b4b54eSJavier Martinez Canillas err_connector:
1461f7b4b54eSJavier Martinez Canillas 	of_node_put(connectors);
1462f7b4b54eSJavier Martinez Canillas #endif
1463a2e5f1b3SJavier Martinez Canillas err:
1464a2e5f1b3SJavier Martinez Canillas 	of_node_put(ep);
1465a2e5f1b3SJavier Martinez Canillas 	return ret;
1466a2e5f1b3SJavier Martinez Canillas }
1467a2e5f1b3SJavier Martinez Canillas 
1468c43875f6SMauro Carvalho Chehab static const char * const tvp5150_test_patterns[2] = {
1469c43875f6SMauro Carvalho Chehab 	"Disabled",
1470c43875f6SMauro Carvalho Chehab 	"Black screen"
1471c43875f6SMauro Carvalho Chehab };
1472c43875f6SMauro Carvalho Chehab 
1473cb7a01acSMauro Carvalho Chehab static int tvp5150_probe(struct i2c_client *c,
1474cb7a01acSMauro Carvalho Chehab 			 const struct i2c_device_id *id)
1475cb7a01acSMauro Carvalho Chehab {
1476cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *core;
1477cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1478a2e5f1b3SJavier Martinez Canillas 	struct device_node *np = c->dev.of_node;
14797871597aSLaurent Pinchart 	int res;
1480cb7a01acSMauro Carvalho Chehab 
1481cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
1482cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(c->adapter,
1483cb7a01acSMauro Carvalho Chehab 	     I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
1484cb7a01acSMauro Carvalho Chehab 		return -EIO;
1485cb7a01acSMauro Carvalho Chehab 
148609aa2609SJavier Martinez Canillas 	res = tvp5150_init(c);
148709aa2609SJavier Martinez Canillas 	if (res)
148809aa2609SJavier Martinez Canillas 		return res;
148909aa2609SJavier Martinez Canillas 
1490c02b211dSLaurent Pinchart 	core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL);
1491c02b211dSLaurent Pinchart 	if (!core)
1492cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1493a2e5f1b3SJavier Martinez Canillas 
1494cb7a01acSMauro Carvalho Chehab 	sd = &core->sd;
1495a2e5f1b3SJavier Martinez Canillas 
1496a2e5f1b3SJavier Martinez Canillas 	if (IS_ENABLED(CONFIG_OF) && np) {
1497a2e5f1b3SJavier Martinez Canillas 		res = tvp5150_parse_dt(core, np);
1498a2e5f1b3SJavier Martinez Canillas 		if (res) {
1499257e29f8SMauro Carvalho Chehab 			dev_err(sd->dev, "DT parsing error: %d\n", res);
1500a2e5f1b3SJavier Martinez Canillas 			return res;
1501a2e5f1b3SJavier Martinez Canillas 		}
1502a2e5f1b3SJavier Martinez Canillas 	} else {
1503a2e5f1b3SJavier Martinez Canillas 		/* Default to BT.656 embedded sync */
1504a2e5f1b3SJavier Martinez Canillas 		core->mbus_type = V4L2_MBUS_BT656;
1505a2e5f1b3SJavier Martinez Canillas 	}
1506a2e5f1b3SJavier Martinez Canillas 
1507cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
15085a08bc00SJavier Martinez Canillas 	sd->internal_ops = &tvp5150_internal_ops;
1509e545ac87SLaurent Pinchart 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1510e545ac87SLaurent Pinchart 
1511e545ac87SLaurent Pinchart #if defined(CONFIG_MEDIA_CONTROLLER)
1512bc322c0dSMauro Carvalho Chehab 	core->pads[TVP5150_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
1513bc322c0dSMauro Carvalho Chehab 	core->pads[TVP5150_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG;
1514bc322c0dSMauro Carvalho Chehab 	core->pads[TVP5150_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
1515bc322c0dSMauro Carvalho Chehab 	core->pads[TVP5150_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV;
1516f92c70adSMauro Carvalho Chehab 
1517f92c70adSMauro Carvalho Chehab 	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
1518f92c70adSMauro Carvalho Chehab 
1519bc322c0dSMauro Carvalho Chehab 	res = media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, core->pads);
1520e545ac87SLaurent Pinchart 	if (res < 0)
1521e545ac87SLaurent Pinchart 		return res;
1522f7b4b54eSJavier Martinez Canillas 
1523f7b4b54eSJavier Martinez Canillas 	sd->entity.ops = &tvp5150_sd_media_ops;
1524e545ac87SLaurent Pinchart #endif
1525cb7a01acSMauro Carvalho Chehab 
15267871597aSLaurent Pinchart 	res = tvp5150_detect_version(core);
1527cb7a01acSMauro Carvalho Chehab 	if (res < 0)
1528c02b211dSLaurent Pinchart 		return res;
1529cb7a01acSMauro Carvalho Chehab 
1530cb7a01acSMauro Carvalho Chehab 	core->norm = V4L2_STD_ALL;	/* Default is autodetect */
1531cb7a01acSMauro Carvalho Chehab 	core->input = TVP5150_COMPOSITE1;
1532c43875f6SMauro Carvalho Chehab 	core->enable = true;
1533cb7a01acSMauro Carvalho Chehab 
1534b1950b8dSLaurent Pinchart 	v4l2_ctrl_handler_init(&core->hdl, 5);
1535cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1536cb7a01acSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
1537cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1538cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 255, 1, 128);
1539cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1540cb7a01acSMauro Carvalho Chehab 			V4L2_CID_SATURATION, 0, 255, 1, 128);
1541cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1542cb7a01acSMauro Carvalho Chehab 			V4L2_CID_HUE, -128, 127, 1, 0);
1543b1950b8dSLaurent Pinchart 	v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
1544b1950b8dSLaurent Pinchart 			V4L2_CID_PIXEL_RATE, 27000000,
1545b1950b8dSLaurent Pinchart 			27000000, 1, 27000000);
1546c43875f6SMauro Carvalho Chehab 	v4l2_ctrl_new_std_menu_items(&core->hdl, &tvp5150_ctrl_ops,
1547c43875f6SMauro Carvalho Chehab 				     V4L2_CID_TEST_PATTERN,
15485c4c4505SMauro Carvalho Chehab 				     ARRAY_SIZE(tvp5150_test_patterns) - 1,
1549c43875f6SMauro Carvalho Chehab 				     0, 0, tvp5150_test_patterns);
1550cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = &core->hdl;
1551cb7a01acSMauro Carvalho Chehab 	if (core->hdl.error) {
1552cb7a01acSMauro Carvalho Chehab 		res = core->hdl.error;
1553c7d97499SJavier Martinez Canillas 		goto err;
1554cb7a01acSMauro Carvalho Chehab 	}
1555cb7a01acSMauro Carvalho Chehab 
1556cb7a01acSMauro Carvalho Chehab 	/* Default is no cropping */
1557cb7a01acSMauro Carvalho Chehab 	core->rect.top = 0;
1558cb7a01acSMauro Carvalho Chehab 	if (tvp5150_read_std(sd) & V4L2_STD_525_60)
1559cb7a01acSMauro Carvalho Chehab 		core->rect.height = TVP5150_V_MAX_525_60;
1560cb7a01acSMauro Carvalho Chehab 	else
1561cb7a01acSMauro Carvalho Chehab 		core->rect.height = TVP5150_V_MAX_OTHERS;
1562cb7a01acSMauro Carvalho Chehab 	core->rect.left = 0;
1563cb7a01acSMauro Carvalho Chehab 	core->rect.width = TVP5150_H_MAX;
1564cb7a01acSMauro Carvalho Chehab 
1565aff808e8SLaurent Pinchart 	tvp5150_reset(sd, 0);	/* Calls v4l2_ctrl_handler_setup() */
1566aff808e8SLaurent Pinchart 
1567c7d97499SJavier Martinez Canillas 	res = v4l2_async_register_subdev(sd);
1568c7d97499SJavier Martinez Canillas 	if (res < 0)
1569c7d97499SJavier Martinez Canillas 		goto err;
1570c7d97499SJavier Martinez Canillas 
1571cb7a01acSMauro Carvalho Chehab 	if (debug > 1)
1572cb7a01acSMauro Carvalho Chehab 		tvp5150_log_status(sd);
1573cb7a01acSMauro Carvalho Chehab 	return 0;
1574c7d97499SJavier Martinez Canillas 
1575c7d97499SJavier Martinez Canillas err:
1576c7d97499SJavier Martinez Canillas 	v4l2_ctrl_handler_free(&core->hdl);
1577c7d97499SJavier Martinez Canillas 	return res;
1578cb7a01acSMauro Carvalho Chehab }
1579cb7a01acSMauro Carvalho Chehab 
1580cb7a01acSMauro Carvalho Chehab static int tvp5150_remove(struct i2c_client *c)
1581cb7a01acSMauro Carvalho Chehab {
1582cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(c);
1583cb7a01acSMauro Carvalho Chehab 	struct tvp5150 *decoder = to_tvp5150(sd);
1584cb7a01acSMauro Carvalho Chehab 
1585257e29f8SMauro Carvalho Chehab 	dev_dbg_lvl(sd->dev, 1, debug,
1586cb7a01acSMauro Carvalho Chehab 		"tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
1587cb7a01acSMauro Carvalho Chehab 		c->addr << 1);
1588cb7a01acSMauro Carvalho Chehab 
1589c7d97499SJavier Martinez Canillas 	v4l2_async_unregister_subdev(sd);
1590cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&decoder->hdl);
1591cb7a01acSMauro Carvalho Chehab 	return 0;
1592cb7a01acSMauro Carvalho Chehab }
1593cb7a01acSMauro Carvalho Chehab 
1594cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1595cb7a01acSMauro Carvalho Chehab 
1596cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id tvp5150_id[] = {
1597cb7a01acSMauro Carvalho Chehab 	{ "tvp5150", 0 },
1598cb7a01acSMauro Carvalho Chehab 	{ }
1599cb7a01acSMauro Carvalho Chehab };
1600cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, tvp5150_id);
1601cb7a01acSMauro Carvalho Chehab 
16027ef930a7SEduard Gavin #if IS_ENABLED(CONFIG_OF)
16037ef930a7SEduard Gavin static const struct of_device_id tvp5150_of_match[] = {
16047ef930a7SEduard Gavin 	{ .compatible = "ti,tvp5150", },
16057ef930a7SEduard Gavin 	{ /* sentinel */ },
16067ef930a7SEduard Gavin };
16077ef930a7SEduard Gavin MODULE_DEVICE_TABLE(of, tvp5150_of_match);
16087ef930a7SEduard Gavin #endif
16097ef930a7SEduard Gavin 
1610cb7a01acSMauro Carvalho Chehab static struct i2c_driver tvp5150_driver = {
1611cb7a01acSMauro Carvalho Chehab 	.driver = {
16127ef930a7SEduard Gavin 		.of_match_table = of_match_ptr(tvp5150_of_match),
1613cb7a01acSMauro Carvalho Chehab 		.name	= "tvp5150",
1614cb7a01acSMauro Carvalho Chehab 	},
1615cb7a01acSMauro Carvalho Chehab 	.probe		= tvp5150_probe,
1616cb7a01acSMauro Carvalho Chehab 	.remove		= tvp5150_remove,
1617cb7a01acSMauro Carvalho Chehab 	.id_table	= tvp5150_id,
1618cb7a01acSMauro Carvalho Chehab };
1619cb7a01acSMauro Carvalho Chehab 
1620cb7a01acSMauro Carvalho Chehab module_i2c_driver(tvp5150_driver);
1621