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>
138e4c97e0SPhilipp Zabel #include <linux/interrupt.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
15859969b3SSakari Ailus #include <linux/of_graph.h>
1673549a69SMarco Felsch #include <linux/pm_runtime.h>
1728b9e227SPhilipp Zabel #include <linux/regmap.h>
18c7d97499SJavier Martinez Canillas #include <media/v4l2-async.h>
19cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
20e953c103SMarco Felsch #include <media/v4l2-event.h>
21cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
22859969b3SSakari Ailus #include <media/v4l2-fwnode.h>
2355606310SMauro Carvalho Chehab #include <media/v4l2-mc.h>
2446fe6e7dSMarco Felsch #include <media/v4l2-rect.h>
25cb7a01acSMauro Carvalho Chehab
26cb7a01acSMauro Carvalho Chehab #include "tvp5150_reg.h"
27cb7a01acSMauro Carvalho Chehab
28785a3de1SPhilipp Zabel #define TVP5150_H_MAX 720U
29785a3de1SPhilipp Zabel #define TVP5150_V_MAX_525_60 480U
30785a3de1SPhilipp Zabel #define TVP5150_V_MAX_OTHERS 576U
31cb7a01acSMauro Carvalho Chehab #define TVP5150_MAX_CROP_LEFT 511
32cb7a01acSMauro Carvalho Chehab #define TVP5150_MAX_CROP_TOP 127
33cb7a01acSMauro Carvalho Chehab #define TVP5150_CROP_SHIFT 2
345cb82940SMarco Felsch #define TVP5150_MBUS_FMT MEDIA_BUS_FMT_UYVY8_2X8
355cb82940SMarco Felsch #define TVP5150_FIELD V4L2_FIELD_ALTERNATE
365cb82940SMarco Felsch #define TVP5150_COLORSPACE V4L2_COLORSPACE_SMPTE170M
37baf17821SMarco Felsch #define TVP5150_STD_MASK (V4L2_STD_NTSC | \
38baf17821SMarco Felsch V4L2_STD_NTSC_443 | \
39baf17821SMarco Felsch V4L2_STD_PAL | \
40baf17821SMarco Felsch V4L2_STD_PAL_M | \
41baf17821SMarco Felsch V4L2_STD_PAL_N | \
42baf17821SMarco Felsch V4L2_STD_PAL_Nc | \
43baf17821SMarco Felsch V4L2_STD_SECAM)
44cb7a01acSMauro Carvalho Chehab
450556f1d5SMarco Felsch #define TVP5150_MAX_CONNECTORS 3 /* Check dt-bindings for more information */
460556f1d5SMarco Felsch
47c43875f6SMauro Carvalho Chehab MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver");
48cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
49459ee17cSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
50cb7a01acSMauro Carvalho Chehab
51cb7a01acSMauro Carvalho Chehab
52cb7a01acSMauro Carvalho Chehab static int debug;
532a0489d3SPhilipp Zabel module_param(debug, int, 0644);
54cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-2)");
55cb7a01acSMauro Carvalho Chehab
56ad0e3744SMauro Carvalho Chehab #define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg)
57ad0e3744SMauro Carvalho Chehab
58bc322c0dSMauro Carvalho Chehab enum tvp5150_pads {
590556f1d5SMarco Felsch TVP5150_PAD_AIP1A,
600556f1d5SMarco Felsch TVP5150_PAD_AIP1B,
61bc322c0dSMauro Carvalho Chehab TVP5150_PAD_VID_OUT,
62bc322c0dSMauro Carvalho Chehab TVP5150_NUM_PADS
63bc322c0dSMauro Carvalho Chehab };
64bc322c0dSMauro Carvalho Chehab
650556f1d5SMarco Felsch struct tvp5150_connector {
660556f1d5SMarco Felsch struct v4l2_fwnode_connector base;
670556f1d5SMarco Felsch struct media_entity ent;
680556f1d5SMarco Felsch struct media_pad pad;
690556f1d5SMarco Felsch };
700556f1d5SMarco Felsch
71cb7a01acSMauro Carvalho Chehab struct tvp5150 {
72cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd;
730556f1d5SMarco Felsch
74bc322c0dSMauro Carvalho Chehab struct media_pad pads[TVP5150_NUM_PADS];
750556f1d5SMarco Felsch struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS];
76baf17821SMarco Felsch struct tvp5150_connector *cur_connector;
770556f1d5SMarco Felsch unsigned int connectors_num;
780556f1d5SMarco Felsch
79cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler hdl;
80cb7a01acSMauro Carvalho Chehab struct v4l2_rect rect;
8128b9e227SPhilipp Zabel struct regmap *regmap;
828e4c97e0SPhilipp Zabel int irq;
83cb7a01acSMauro Carvalho Chehab
84cb7a01acSMauro Carvalho Chehab v4l2_std_id norm; /* Current set standard */
85b440b733SPhilipp Zabel v4l2_std_id detected_norm;
86cb7a01acSMauro Carvalho Chehab u32 input;
87cb7a01acSMauro Carvalho Chehab u32 output;
8862a764e1SPhilipp Zabel u32 oe;
89cb7a01acSMauro Carvalho Chehab int enable;
908e4c97e0SPhilipp Zabel bool lock;
91a2e5f1b3SJavier Martinez Canillas
9282275133SJavier Martinez Canillas u16 dev_id;
9382275133SJavier Martinez Canillas u16 rom_ver;
9482275133SJavier Martinez Canillas
95a2e5f1b3SJavier Martinez Canillas enum v4l2_mbus_type mbus_type;
96cb7a01acSMauro Carvalho Chehab };
97cb7a01acSMauro Carvalho Chehab
to_tvp5150(struct v4l2_subdev * sd)98cb7a01acSMauro Carvalho Chehab static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
99cb7a01acSMauro Carvalho Chehab {
100cb7a01acSMauro Carvalho Chehab return container_of(sd, struct tvp5150, sd);
101cb7a01acSMauro Carvalho Chehab }
102cb7a01acSMauro Carvalho Chehab
to_sd(struct v4l2_ctrl * ctrl)103cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
104cb7a01acSMauro Carvalho Chehab {
105cb7a01acSMauro Carvalho Chehab return &container_of(ctrl->handler, struct tvp5150, hdl)->sd;
106cb7a01acSMauro Carvalho Chehab }
107cb7a01acSMauro Carvalho Chehab
tvp5150_read(struct v4l2_subdev * sd,unsigned char addr)108cb7a01acSMauro Carvalho Chehab static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
109cb7a01acSMauro Carvalho Chehab {
11028b9e227SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
11128b9e227SPhilipp Zabel int ret, val;
112cb7a01acSMauro Carvalho Chehab
11328b9e227SPhilipp Zabel ret = regmap_read(decoder->regmap, addr, &val);
11428b9e227SPhilipp Zabel if (ret < 0)
11528b9e227SPhilipp Zabel return ret;
116cb7a01acSMauro Carvalho Chehab
11728b9e227SPhilipp Zabel return val;
118cb7a01acSMauro Carvalho Chehab }
119cb7a01acSMauro Carvalho Chehab
dump_reg_range(struct v4l2_subdev * sd,char * s,u8 init,const u8 end,int max_line)120cb7a01acSMauro Carvalho Chehab static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
121cb7a01acSMauro Carvalho Chehab const u8 end, int max_line)
122cb7a01acSMauro Carvalho Chehab {
123e5134114SMauro Carvalho Chehab u8 buf[16];
124e5134114SMauro Carvalho Chehab int i = 0, j, len;
125cb7a01acSMauro Carvalho Chehab
126e5134114SMauro Carvalho Chehab if (max_line > 16) {
127e5134114SMauro Carvalho Chehab dprintk0(sd->dev, "too much data to dump\n");
128e5134114SMauro Carvalho Chehab return;
129cb7a01acSMauro Carvalho Chehab }
130cb7a01acSMauro Carvalho Chehab
131e5134114SMauro Carvalho Chehab for (i = init; i < end; i += max_line) {
132e5134114SMauro Carvalho Chehab len = (end - i > max_line) ? max_line : end - i;
133e5134114SMauro Carvalho Chehab
134e5134114SMauro Carvalho Chehab for (j = 0; j < len; j++)
135e5134114SMauro Carvalho Chehab buf[j] = tvp5150_read(sd, i + j);
136e5134114SMauro Carvalho Chehab
137e5134114SMauro Carvalho Chehab dprintk0(sd->dev, "%s reg %02x = %*ph\n", s, i, len, buf);
138cb7a01acSMauro Carvalho Chehab }
139cb7a01acSMauro Carvalho Chehab }
140cb7a01acSMauro Carvalho Chehab
tvp5150_log_status(struct v4l2_subdev * sd)141cb7a01acSMauro Carvalho Chehab static int tvp5150_log_status(struct v4l2_subdev *sd)
142cb7a01acSMauro Carvalho Chehab {
143ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Video input source selection #1 = 0x%02x\n",
144cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
145ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Analog channel controls = 0x%02x\n",
146cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
147ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Operation mode controls = 0x%02x\n",
148cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_OP_MODE_CTL));
149ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Miscellaneous controls = 0x%02x\n",
150cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_MISC_CTL));
151ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Autoswitch mask= 0x%02x\n",
152cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_AUTOSW_MSK));
153ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Color killer threshold control = 0x%02x\n",
154cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
155ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
156cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
157cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
158cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
159ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Brightness control = 0x%02x\n",
160cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_BRIGHT_CTL));
161ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Color saturation control = 0x%02x\n",
162cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_SATURATION_CTL));
163ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Hue control = 0x%02x\n",
164cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_HUE_CTL));
165ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Contrast control = 0x%02x\n",
166cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_CONTRAST_CTL));
167ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Outputs and data rates select = 0x%02x\n",
168cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
169ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Configuration shared pins = 0x%02x\n",
170cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
171ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Active video cropping start = 0x%02x%02x\n",
172cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
173cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
174ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Active video cropping stop = 0x%02x%02x\n",
175cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
176cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
177ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Genlock/RTC = 0x%02x\n",
178cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_GENLOCK));
179ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Horizontal sync start = 0x%02x\n",
180cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
181ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Vertical blanking start = 0x%02x\n",
182cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
183ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Vertical blanking stop = 0x%02x\n",
184cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
185ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
186cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
187cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
188ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt reset register B = 0x%02x\n",
189cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
190ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt enable register B = 0x%02x\n",
191cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
192ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt configuration register B = 0x%02x\n",
193cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
194ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Video standard = 0x%02x\n",
195cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VIDEO_STD));
196ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
197cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
198cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
199ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Macrovision on counter = 0x%02x\n",
200cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
201ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Macrovision off counter = 0x%02x\n",
202cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
203ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
204cb7a01acSMauro Carvalho Chehab (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
205ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Device ID = %02x%02x\n",
206cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_MSB_DEV_ID),
207cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_LSB_DEV_ID));
208ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: ROM version = (hex) %02x.%02x\n",
209cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
210cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
211ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Vertical line count = 0x%02x%02x\n",
212cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
213cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
214ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt status register B = 0x%02x\n",
215cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
216ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt active register B = 0x%02x\n",
217cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
218ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
219cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_STATUS_REG_1),
220cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_STATUS_REG_2),
221cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_STATUS_REG_3),
222cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_STATUS_REG_4),
223cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_STATUS_REG_5));
224cb7a01acSMauro Carvalho Chehab
225cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
226cb7a01acSMauro Carvalho Chehab TVP5150_TELETEXT_FIL1_END, 8);
227cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
228cb7a01acSMauro Carvalho Chehab TVP5150_TELETEXT_FIL2_END, 8);
229cb7a01acSMauro Carvalho Chehab
230ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Teletext filter enable = 0x%02x\n",
231cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
232ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt status register A = 0x%02x\n",
233cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
234ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt enable register A = 0x%02x\n",
235cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
236ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Interrupt configuration = 0x%02x\n",
237cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_INT_CONF));
238ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: VDP status register = 0x%02x\n",
239cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
240ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: FIFO word count = 0x%02x\n",
241cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
242ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: FIFO interrupt threshold = 0x%02x\n",
243cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
244ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: FIFO reset = 0x%02x\n",
245cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_FIFO_RESET));
246ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Line number interrupt = 0x%02x\n",
247cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
248ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Pixel alignment register = 0x%02x%02x\n",
249cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
250cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
251ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: FIFO output control = 0x%02x\n",
252cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
253ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Full field enable = 0x%02x\n",
254cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
255ad0e3744SMauro Carvalho Chehab dprintk0(sd->dev, "tvp5150: Full field mode register = 0x%02x\n",
256cb7a01acSMauro Carvalho Chehab tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
257cb7a01acSMauro Carvalho Chehab
258cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
259cb7a01acSMauro Carvalho Chehab TVP5150_CC_DATA_END, 8);
260cb7a01acSMauro Carvalho Chehab
261cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
262cb7a01acSMauro Carvalho Chehab TVP5150_WSS_DATA_END, 8);
263cb7a01acSMauro Carvalho Chehab
264cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
265cb7a01acSMauro Carvalho Chehab TVP5150_VPS_DATA_END, 8);
266cb7a01acSMauro Carvalho Chehab
267cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
268cb7a01acSMauro Carvalho Chehab TVP5150_VITC_DATA_END, 10);
269cb7a01acSMauro Carvalho Chehab
270cb7a01acSMauro Carvalho Chehab dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
271cb7a01acSMauro Carvalho Chehab TVP5150_LINE_MODE_END, 8);
272cb7a01acSMauro Carvalho Chehab return 0;
273cb7a01acSMauro Carvalho Chehab }
274cb7a01acSMauro Carvalho Chehab
275cb7a01acSMauro Carvalho Chehab /****************************************************************************
276cb7a01acSMauro Carvalho Chehab Basic functions
277cb7a01acSMauro Carvalho Chehab ****************************************************************************/
278cb7a01acSMauro Carvalho Chehab
tvp5150_selmux(struct v4l2_subdev * sd)2796e98bee2SLaurent Pinchart static void tvp5150_selmux(struct v4l2_subdev *sd)
280cb7a01acSMauro Carvalho Chehab {
281cb7a01acSMauro Carvalho Chehab int opmode = 0;
282cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
2838a7441baSMarco Felsch unsigned int mask, val;
284cb7a01acSMauro Carvalho Chehab int input = 0;
285cb7a01acSMauro Carvalho Chehab
286c43875f6SMauro Carvalho Chehab /* Only tvp5150am1 and tvp5151 have signal generator support */
287c43875f6SMauro Carvalho Chehab if ((decoder->dev_id == 0x5150 && decoder->rom_ver == 0x0400) ||
288c43875f6SMauro Carvalho Chehab (decoder->dev_id == 0x5151 && decoder->rom_ver == 0x0100)) {
289c43875f6SMauro Carvalho Chehab if (!decoder->enable)
290cb7a01acSMauro Carvalho Chehab input = 8;
291c43875f6SMauro Carvalho Chehab }
292cb7a01acSMauro Carvalho Chehab
293cb7a01acSMauro Carvalho Chehab switch (decoder->input) {
294cb7a01acSMauro Carvalho Chehab case TVP5150_COMPOSITE1:
295cb7a01acSMauro Carvalho Chehab input |= 2;
2961771e9fbSGustavo A. R. Silva fallthrough;
297cb7a01acSMauro Carvalho Chehab case TVP5150_COMPOSITE0:
298cb7a01acSMauro Carvalho Chehab break;
299cb7a01acSMauro Carvalho Chehab case TVP5150_SVIDEO:
300cb7a01acSMauro Carvalho Chehab default:
301cb7a01acSMauro Carvalho Chehab input |= 1;
302cb7a01acSMauro Carvalho Chehab break;
303cb7a01acSMauro Carvalho Chehab }
304cb7a01acSMauro Carvalho Chehab
305430f35b7SMarco Felsch dev_dbg_lvl(sd->dev, 1, debug,
306430f35b7SMarco Felsch "Selecting video route: route input=%s, output=%s => tvp5150 input=0x%02x, opmode=0x%02x\n",
307430f35b7SMarco Felsch decoder->input == 0 ? "aip1a" :
308430f35b7SMarco Felsch decoder->input == 2 ? "aip1b" : "svideo",
309430f35b7SMarco Felsch decoder->output == 0 ? "normal" : "black-frame-gen",
310cb7a01acSMauro Carvalho Chehab input, opmode);
311cb7a01acSMauro Carvalho Chehab
31228b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_OP_MODE_CTL, opmode);
31328b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_VD_IN_SRC_SEL_1, input);
314cb7a01acSMauro Carvalho Chehab
315b4b2de38SLaurent Pinchart /*
316b4b2de38SLaurent Pinchart * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For
317b4b2de38SLaurent Pinchart * S-Video we output the vertical lock (VLK) signal on FID/GLCO/VLK/HVLK
318b4b2de38SLaurent Pinchart * and set INTREQ/GPCL/VBLK to logic 0. For composite we output the
319b4b2de38SLaurent Pinchart * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set
320b4b2de38SLaurent Pinchart * INTREQ/GPCL/VBLK to logic 1.
321cb7a01acSMauro Carvalho Chehab */
3228a7441baSMarco Felsch mask = TVP5150_MISC_CTL_GPCL | TVP5150_MISC_CTL_HVLK;
323cb7a01acSMauro Carvalho Chehab if (decoder->input == TVP5150_SVIDEO)
3248a7441baSMarco Felsch val = TVP5150_MISC_CTL_HVLK;
325cb7a01acSMauro Carvalho Chehab else
3268a7441baSMarco Felsch val = TVP5150_MISC_CTL_GPCL;
3278a7441baSMarco Felsch regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val);
328cb7a01acSMauro Carvalho Chehab };
329cb7a01acSMauro Carvalho Chehab
330cb7a01acSMauro Carvalho Chehab struct i2c_reg_value {
331cb7a01acSMauro Carvalho Chehab unsigned char reg;
332cb7a01acSMauro Carvalho Chehab unsigned char value;
333cb7a01acSMauro Carvalho Chehab };
334cb7a01acSMauro Carvalho Chehab
335cb7a01acSMauro Carvalho Chehab /* Default values as sugested at TVP5150AM1 datasheet */
336cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp5150_init_default[] = {
337cb7a01acSMauro Carvalho Chehab { /* 0x00 */
338cb7a01acSMauro Carvalho Chehab TVP5150_VD_IN_SRC_SEL_1, 0x00
339cb7a01acSMauro Carvalho Chehab },
340cb7a01acSMauro Carvalho Chehab { /* 0x01 */
341cb7a01acSMauro Carvalho Chehab TVP5150_ANAL_CHL_CTL, 0x15
342cb7a01acSMauro Carvalho Chehab },
343cb7a01acSMauro Carvalho Chehab { /* 0x02 */
344cb7a01acSMauro Carvalho Chehab TVP5150_OP_MODE_CTL, 0x00
345cb7a01acSMauro Carvalho Chehab },
346cb7a01acSMauro Carvalho Chehab { /* 0x03 */
347cb7a01acSMauro Carvalho Chehab TVP5150_MISC_CTL, 0x01
348cb7a01acSMauro Carvalho Chehab },
349cb7a01acSMauro Carvalho Chehab { /* 0x06 */
350cb7a01acSMauro Carvalho Chehab TVP5150_COLOR_KIL_THSH_CTL, 0x10
351cb7a01acSMauro Carvalho Chehab },
352cb7a01acSMauro Carvalho Chehab { /* 0x07 */
353cb7a01acSMauro Carvalho Chehab TVP5150_LUMA_PROC_CTL_1, 0x60
354cb7a01acSMauro Carvalho Chehab },
355cb7a01acSMauro Carvalho Chehab { /* 0x08 */
356cb7a01acSMauro Carvalho Chehab TVP5150_LUMA_PROC_CTL_2, 0x00
357cb7a01acSMauro Carvalho Chehab },
358cb7a01acSMauro Carvalho Chehab { /* 0x09 */
359cb7a01acSMauro Carvalho Chehab TVP5150_BRIGHT_CTL, 0x80
360cb7a01acSMauro Carvalho Chehab },
361cb7a01acSMauro Carvalho Chehab { /* 0x0a */
362cb7a01acSMauro Carvalho Chehab TVP5150_SATURATION_CTL, 0x80
363cb7a01acSMauro Carvalho Chehab },
364cb7a01acSMauro Carvalho Chehab { /* 0x0b */
365cb7a01acSMauro Carvalho Chehab TVP5150_HUE_CTL, 0x00
366cb7a01acSMauro Carvalho Chehab },
367cb7a01acSMauro Carvalho Chehab { /* 0x0c */
368cb7a01acSMauro Carvalho Chehab TVP5150_CONTRAST_CTL, 0x80
369cb7a01acSMauro Carvalho Chehab },
370cb7a01acSMauro Carvalho Chehab { /* 0x0d */
371cb7a01acSMauro Carvalho Chehab TVP5150_DATA_RATE_SEL, 0x47
372cb7a01acSMauro Carvalho Chehab },
373cb7a01acSMauro Carvalho Chehab { /* 0x0e */
374cb7a01acSMauro Carvalho Chehab TVP5150_LUMA_PROC_CTL_3, 0x00
375cb7a01acSMauro Carvalho Chehab },
376cb7a01acSMauro Carvalho Chehab { /* 0x0f */
377cb7a01acSMauro Carvalho Chehab TVP5150_CONF_SHARED_PIN, 0x08
378cb7a01acSMauro Carvalho Chehab },
379cb7a01acSMauro Carvalho Chehab { /* 0x11 */
380cb7a01acSMauro Carvalho Chehab TVP5150_ACT_VD_CROP_ST_MSB, 0x00
381cb7a01acSMauro Carvalho Chehab },
382cb7a01acSMauro Carvalho Chehab { /* 0x12 */
383cb7a01acSMauro Carvalho Chehab TVP5150_ACT_VD_CROP_ST_LSB, 0x00
384cb7a01acSMauro Carvalho Chehab },
385cb7a01acSMauro Carvalho Chehab { /* 0x13 */
386cb7a01acSMauro Carvalho Chehab TVP5150_ACT_VD_CROP_STP_MSB, 0x00
387cb7a01acSMauro Carvalho Chehab },
388cb7a01acSMauro Carvalho Chehab { /* 0x14 */
389cb7a01acSMauro Carvalho Chehab TVP5150_ACT_VD_CROP_STP_LSB, 0x00
390cb7a01acSMauro Carvalho Chehab },
391cb7a01acSMauro Carvalho Chehab { /* 0x15 */
392cb7a01acSMauro Carvalho Chehab TVP5150_GENLOCK, 0x01
393cb7a01acSMauro Carvalho Chehab },
394cb7a01acSMauro Carvalho Chehab { /* 0x16 */
395cb7a01acSMauro Carvalho Chehab TVP5150_HORIZ_SYNC_START, 0x80
396cb7a01acSMauro Carvalho Chehab },
397cb7a01acSMauro Carvalho Chehab { /* 0x18 */
398cb7a01acSMauro Carvalho Chehab TVP5150_VERT_BLANKING_START, 0x00
399cb7a01acSMauro Carvalho Chehab },
400cb7a01acSMauro Carvalho Chehab { /* 0x19 */
401cb7a01acSMauro Carvalho Chehab TVP5150_VERT_BLANKING_STOP, 0x00
402cb7a01acSMauro Carvalho Chehab },
403cb7a01acSMauro Carvalho Chehab { /* 0x1a */
404cb7a01acSMauro Carvalho Chehab TVP5150_CHROMA_PROC_CTL_1, 0x0c
405cb7a01acSMauro Carvalho Chehab },
406cb7a01acSMauro Carvalho Chehab { /* 0x1b */
407cb7a01acSMauro Carvalho Chehab TVP5150_CHROMA_PROC_CTL_2, 0x14
408cb7a01acSMauro Carvalho Chehab },
409cb7a01acSMauro Carvalho Chehab { /* 0x1c */
410cb7a01acSMauro Carvalho Chehab TVP5150_INT_RESET_REG_B, 0x00
411cb7a01acSMauro Carvalho Chehab },
412cb7a01acSMauro Carvalho Chehab { /* 0x1d */
413cb7a01acSMauro Carvalho Chehab TVP5150_INT_ENABLE_REG_B, 0x00
414cb7a01acSMauro Carvalho Chehab },
415cb7a01acSMauro Carvalho Chehab { /* 0x1e */
416cb7a01acSMauro Carvalho Chehab TVP5150_INTT_CONFIG_REG_B, 0x00
417cb7a01acSMauro Carvalho Chehab },
418cb7a01acSMauro Carvalho Chehab { /* 0x28 */
419cb7a01acSMauro Carvalho Chehab TVP5150_VIDEO_STD, 0x00
420cb7a01acSMauro Carvalho Chehab },
421cb7a01acSMauro Carvalho Chehab { /* 0x2e */
422cb7a01acSMauro Carvalho Chehab TVP5150_MACROVISION_ON_CTR, 0x0f
423cb7a01acSMauro Carvalho Chehab },
424cb7a01acSMauro Carvalho Chehab { /* 0x2f */
425cb7a01acSMauro Carvalho Chehab TVP5150_MACROVISION_OFF_CTR, 0x01
426cb7a01acSMauro Carvalho Chehab },
427cb7a01acSMauro Carvalho Chehab { /* 0xbb */
428cb7a01acSMauro Carvalho Chehab TVP5150_TELETEXT_FIL_ENA, 0x00
429cb7a01acSMauro Carvalho Chehab },
430cb7a01acSMauro Carvalho Chehab { /* 0xc0 */
431cb7a01acSMauro Carvalho Chehab TVP5150_INT_STATUS_REG_A, 0x00
432cb7a01acSMauro Carvalho Chehab },
433cb7a01acSMauro Carvalho Chehab { /* 0xc1 */
434cb7a01acSMauro Carvalho Chehab TVP5150_INT_ENABLE_REG_A, 0x00
435cb7a01acSMauro Carvalho Chehab },
436cb7a01acSMauro Carvalho Chehab { /* 0xc2 */
437cb7a01acSMauro Carvalho Chehab TVP5150_INT_CONF, 0x04
438cb7a01acSMauro Carvalho Chehab },
439cb7a01acSMauro Carvalho Chehab { /* 0xc8 */
440cb7a01acSMauro Carvalho Chehab TVP5150_FIFO_INT_THRESHOLD, 0x80
441cb7a01acSMauro Carvalho Chehab },
442cb7a01acSMauro Carvalho Chehab { /* 0xc9 */
443cb7a01acSMauro Carvalho Chehab TVP5150_FIFO_RESET, 0x00
444cb7a01acSMauro Carvalho Chehab },
445cb7a01acSMauro Carvalho Chehab { /* 0xca */
446cb7a01acSMauro Carvalho Chehab TVP5150_LINE_NUMBER_INT, 0x00
447cb7a01acSMauro Carvalho Chehab },
448cb7a01acSMauro Carvalho Chehab { /* 0xcb */
449cb7a01acSMauro Carvalho Chehab TVP5150_PIX_ALIGN_REG_LOW, 0x4e
450cb7a01acSMauro Carvalho Chehab },
451cb7a01acSMauro Carvalho Chehab { /* 0xcc */
452cb7a01acSMauro Carvalho Chehab TVP5150_PIX_ALIGN_REG_HIGH, 0x00
453cb7a01acSMauro Carvalho Chehab },
454cb7a01acSMauro Carvalho Chehab { /* 0xcd */
455cb7a01acSMauro Carvalho Chehab TVP5150_FIFO_OUT_CTRL, 0x01
456cb7a01acSMauro Carvalho Chehab },
457cb7a01acSMauro Carvalho Chehab { /* 0xcf */
458cb7a01acSMauro Carvalho Chehab TVP5150_FULL_FIELD_ENA, 0x00
459cb7a01acSMauro Carvalho Chehab },
460cb7a01acSMauro Carvalho Chehab { /* 0xd0 */
461cb7a01acSMauro Carvalho Chehab TVP5150_LINE_MODE_INI, 0x00
462cb7a01acSMauro Carvalho Chehab },
463cb7a01acSMauro Carvalho Chehab { /* 0xfc */
464cb7a01acSMauro Carvalho Chehab TVP5150_FULL_FIELD_MODE_REG, 0x7f
465cb7a01acSMauro Carvalho Chehab },
466cb7a01acSMauro Carvalho Chehab { /* end of data */
467cb7a01acSMauro Carvalho Chehab 0xff, 0xff
468cb7a01acSMauro Carvalho Chehab }
469cb7a01acSMauro Carvalho Chehab };
470cb7a01acSMauro Carvalho Chehab
471cb7a01acSMauro Carvalho Chehab /* Default values as sugested at TVP5150AM1 datasheet */
472cb7a01acSMauro Carvalho Chehab static const struct i2c_reg_value tvp5150_init_enable[] = {
4738105e1bcSPhilipp Zabel { /* Automatic offset and AGC enabled */
474cb7a01acSMauro Carvalho Chehab TVP5150_ANAL_CHL_CTL, 0x15
475cb7a01acSMauro Carvalho Chehab }, { /* Activate YCrCb output 0x9 or 0xd ? */
476b4b2de38SLaurent Pinchart TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL |
477b4b2de38SLaurent Pinchart TVP5150_MISC_CTL_INTREQ_OE |
478b4b2de38SLaurent Pinchart TVP5150_MISC_CTL_YCBCR_OE |
479b4b2de38SLaurent Pinchart TVP5150_MISC_CTL_SYNC_OE |
480b4b2de38SLaurent Pinchart TVP5150_MISC_CTL_VBLANK |
481b4b2de38SLaurent Pinchart TVP5150_MISC_CTL_CLOCK_OE,
482cb7a01acSMauro Carvalho Chehab }, { /* Activates video std autodetection for all standards */
483cb7a01acSMauro Carvalho Chehab TVP5150_AUTOSW_MSK, 0x0
484cb7a01acSMauro Carvalho Chehab }, { /* Default format: 0x47. For 4:2:2: 0x40 */
485cb7a01acSMauro Carvalho Chehab TVP5150_DATA_RATE_SEL, 0x47
486cb7a01acSMauro Carvalho Chehab }, {
487cb7a01acSMauro Carvalho Chehab TVP5150_CHROMA_PROC_CTL_1, 0x0c
488cb7a01acSMauro Carvalho Chehab }, {
489cb7a01acSMauro Carvalho Chehab TVP5150_CHROMA_PROC_CTL_2, 0x54
490cb7a01acSMauro Carvalho Chehab }, { /* Non documented, but initialized on WinTV USB2 */
491cb7a01acSMauro Carvalho Chehab 0x27, 0x20
492cb7a01acSMauro Carvalho Chehab }, {
493cb7a01acSMauro Carvalho Chehab 0xff, 0xff
494cb7a01acSMauro Carvalho Chehab }
495cb7a01acSMauro Carvalho Chehab };
496cb7a01acSMauro Carvalho Chehab
497cb7a01acSMauro Carvalho Chehab struct tvp5150_vbi_type {
498cb7a01acSMauro Carvalho Chehab unsigned int vbi_type;
499cb7a01acSMauro Carvalho Chehab unsigned int ini_line;
500cb7a01acSMauro Carvalho Chehab unsigned int end_line;
501cb7a01acSMauro Carvalho Chehab unsigned int by_field :1;
502cb7a01acSMauro Carvalho Chehab };
503cb7a01acSMauro Carvalho Chehab
504cb7a01acSMauro Carvalho Chehab struct i2c_vbi_ram_value {
505cb7a01acSMauro Carvalho Chehab u16 reg;
506cb7a01acSMauro Carvalho Chehab struct tvp5150_vbi_type type;
507cb7a01acSMauro Carvalho Chehab unsigned char values[16];
508cb7a01acSMauro Carvalho Chehab };
509cb7a01acSMauro Carvalho Chehab
510cb7a01acSMauro Carvalho Chehab /* This struct have the values for each supported VBI Standard
511cb7a01acSMauro Carvalho Chehab * by
512cb7a01acSMauro Carvalho Chehab tvp5150_vbi_types should follow the same order as vbi_ram_default
513cb7a01acSMauro Carvalho Chehab * value 0 means rom position 0x10, value 1 means rom position 0x30
514cb7a01acSMauro Carvalho Chehab * and so on. There are 16 possible locations from 0 to 15.
515cb7a01acSMauro Carvalho Chehab */
516cb7a01acSMauro Carvalho Chehab
51765dc760bSNasser Afshin static struct i2c_vbi_ram_value vbi_ram_default[] = {
51865dc760bSNasser Afshin
5199bfd8f88SNasser Afshin /*
5209bfd8f88SNasser Afshin * FIXME: Current api doesn't handle all VBI types, those not
5219bfd8f88SNasser Afshin * yet supported are placed under #if 0
5229bfd8f88SNasser Afshin */
523cb7a01acSMauro Carvalho Chehab #if 0
5243dd6b560SMauro Carvalho Chehab [0] = {0x010, /* Teletext, SECAM, WST System A */
525cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1},
526cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
527cb7a01acSMauro Carvalho Chehab 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
528cb7a01acSMauro Carvalho Chehab },
529cb7a01acSMauro Carvalho Chehab #endif
5303dd6b560SMauro Carvalho Chehab [1] = {0x030, /* Teletext, PAL, WST System B */
531cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_TELETEXT_B, 6, 22, 1},
532cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
533cb7a01acSMauro Carvalho Chehab 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
534cb7a01acSMauro Carvalho Chehab },
535cb7a01acSMauro Carvalho Chehab #if 0
5363dd6b560SMauro Carvalho Chehab [2] = {0x050, /* Teletext, PAL, WST System C */
537cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_TELETEXT_PAL_C, 6, 22, 1},
538cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
539cb7a01acSMauro Carvalho Chehab 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
540cb7a01acSMauro Carvalho Chehab },
5413dd6b560SMauro Carvalho Chehab [3] = {0x070, /* Teletext, NTSC, WST System B */
542cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_TELETEXT_NTSC_B, 10, 21, 1},
543cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
544cb7a01acSMauro Carvalho Chehab 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
545cb7a01acSMauro Carvalho Chehab },
5463dd6b560SMauro Carvalho Chehab [4] = {0x090, /* Tetetext, NTSC NABTS System C */
547cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_TELETEXT_NTSC_C, 10, 21, 1},
548cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
549cb7a01acSMauro Carvalho Chehab 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
550cb7a01acSMauro Carvalho Chehab },
5513dd6b560SMauro Carvalho Chehab [5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */
552cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_TELETEXT_NTSC_D, 10, 21, 1},
553cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
554cb7a01acSMauro Carvalho Chehab 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
555cb7a01acSMauro Carvalho Chehab },
5563dd6b560SMauro Carvalho Chehab [6] = {0x0d0, /* Closed Caption, PAL/SECAM */
557cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_CAPTION_625, 22, 22, 1},
558cb7a01acSMauro Carvalho Chehab { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
559cb7a01acSMauro Carvalho Chehab 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
560cb7a01acSMauro Carvalho Chehab },
561cb7a01acSMauro Carvalho Chehab #endif
5623dd6b560SMauro Carvalho Chehab [7] = {0x0f0, /* Closed Caption, NTSC */
563cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_CAPTION_525, 21, 21, 1},
564cb7a01acSMauro Carvalho Chehab { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
565cb7a01acSMauro Carvalho Chehab 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
566cb7a01acSMauro Carvalho Chehab },
5673dd6b560SMauro Carvalho Chehab [8] = {0x110, /* Wide Screen Signal, PAL/SECAM */
568cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_WSS_625, 23, 23, 1},
569cb7a01acSMauro Carvalho Chehab { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
570cb7a01acSMauro Carvalho Chehab 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
571cb7a01acSMauro Carvalho Chehab },
572cb7a01acSMauro Carvalho Chehab #if 0
5733dd6b560SMauro Carvalho Chehab [9] = {0x130, /* Wide Screen Signal, NTSC C */
574cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_WSS_525, 20, 20, 1},
575cb7a01acSMauro Carvalho Chehab { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
576cb7a01acSMauro Carvalho Chehab 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
577cb7a01acSMauro Carvalho Chehab },
5783dd6b560SMauro Carvalho Chehab [10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
579cb7a01acSMauro Carvalho Chehab {V4l2_SLICED_VITC_625, 6, 22, 0},
580cb7a01acSMauro Carvalho Chehab { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
581cb7a01acSMauro Carvalho Chehab 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
582cb7a01acSMauro Carvalho Chehab },
5833dd6b560SMauro Carvalho Chehab [11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */
584cb7a01acSMauro Carvalho Chehab {V4l2_SLICED_VITC_525, 10, 20, 0},
585cb7a01acSMauro Carvalho Chehab { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
586cb7a01acSMauro Carvalho Chehab 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
587cb7a01acSMauro Carvalho Chehab },
588cb7a01acSMauro Carvalho Chehab #endif
5893dd6b560SMauro Carvalho Chehab [12] = {0x190, /* Video Program System (VPS), PAL */
590cb7a01acSMauro Carvalho Chehab {V4L2_SLICED_VPS, 16, 16, 0},
591cb7a01acSMauro Carvalho Chehab { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
592cb7a01acSMauro Carvalho Chehab 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
593cb7a01acSMauro Carvalho Chehab },
594cb7a01acSMauro Carvalho Chehab /* 0x1d0 User programmable */
595cb7a01acSMauro Carvalho Chehab };
596cb7a01acSMauro Carvalho Chehab
tvp5150_write_inittab(struct v4l2_subdev * sd,const struct i2c_reg_value * regs)597cb7a01acSMauro Carvalho Chehab static int tvp5150_write_inittab(struct v4l2_subdev *sd,
598cb7a01acSMauro Carvalho Chehab const struct i2c_reg_value *regs)
599cb7a01acSMauro Carvalho Chehab {
60028b9e227SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
60128b9e227SPhilipp Zabel
602cb7a01acSMauro Carvalho Chehab while (regs->reg != 0xff) {
60328b9e227SPhilipp Zabel regmap_write(decoder->regmap, regs->reg, regs->value);
604cb7a01acSMauro Carvalho Chehab regs++;
605cb7a01acSMauro Carvalho Chehab }
606cb7a01acSMauro Carvalho Chehab return 0;
607cb7a01acSMauro Carvalho Chehab }
608cb7a01acSMauro Carvalho Chehab
tvp5150_vdp_init(struct v4l2_subdev * sd)6093dd6b560SMauro Carvalho Chehab static int tvp5150_vdp_init(struct v4l2_subdev *sd)
610cb7a01acSMauro Carvalho Chehab {
61128b9e227SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
61228b9e227SPhilipp Zabel struct regmap *map = decoder->regmap;
613cb7a01acSMauro Carvalho Chehab unsigned int i;
6143dd6b560SMauro Carvalho Chehab int j;
615cb7a01acSMauro Carvalho Chehab
616cb7a01acSMauro Carvalho Chehab /* Disable Full Field */
61728b9e227SPhilipp Zabel regmap_write(map, TVP5150_FULL_FIELD_ENA, 0);
618cb7a01acSMauro Carvalho Chehab
619cb7a01acSMauro Carvalho Chehab /* Before programming, Line mode should be at 0xff */
620cb7a01acSMauro Carvalho Chehab for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
62128b9e227SPhilipp Zabel regmap_write(map, i, 0xff);
622cb7a01acSMauro Carvalho Chehab
623cb7a01acSMauro Carvalho Chehab /* Load Ram Table */
6243dd6b560SMauro Carvalho Chehab for (j = 0; j < ARRAY_SIZE(vbi_ram_default); j++) {
6253dd6b560SMauro Carvalho Chehab const struct i2c_vbi_ram_value *regs = &vbi_ram_default[j];
6263dd6b560SMauro Carvalho Chehab
6273dd6b560SMauro Carvalho Chehab if (!regs->type.vbi_type)
6283dd6b560SMauro Carvalho Chehab continue;
6293dd6b560SMauro Carvalho Chehab
63028b9e227SPhilipp Zabel regmap_write(map, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
63128b9e227SPhilipp Zabel regmap_write(map, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
632cb7a01acSMauro Carvalho Chehab
633cb7a01acSMauro Carvalho Chehab for (i = 0; i < 16; i++)
63428b9e227SPhilipp Zabel regmap_write(map, TVP5150_VDP_CONF_RAM_DATA,
63528b9e227SPhilipp Zabel regs->values[i]);
636cb7a01acSMauro Carvalho Chehab }
637cb7a01acSMauro Carvalho Chehab return 0;
638cb7a01acSMauro Carvalho Chehab }
639cb7a01acSMauro Carvalho Chehab
640cb7a01acSMauro Carvalho Chehab /* Fills VBI capabilities based on i2c_vbi_ram_value struct */
tvp5150_g_sliced_vbi_cap(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_cap * cap)641cb7a01acSMauro Carvalho Chehab static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
642cb7a01acSMauro Carvalho Chehab struct v4l2_sliced_vbi_cap *cap)
643cb7a01acSMauro Carvalho Chehab {
6443dd6b560SMauro Carvalho Chehab int line, i;
645cb7a01acSMauro Carvalho Chehab
646257e29f8SMauro Carvalho Chehab dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n");
647bb7a3681SNasser Afshin memset(cap, 0, sizeof(*cap));
648cb7a01acSMauro Carvalho Chehab
6493dd6b560SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
6503dd6b560SMauro Carvalho Chehab const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i];
6513dd6b560SMauro Carvalho Chehab
6523dd6b560SMauro Carvalho Chehab if (!regs->type.vbi_type)
6533dd6b560SMauro Carvalho Chehab continue;
6543dd6b560SMauro Carvalho Chehab
6553dd6b560SMauro Carvalho Chehab for (line = regs->type.ini_line;
6563dd6b560SMauro Carvalho Chehab line <= regs->type.end_line;
6573dd6b560SMauro Carvalho Chehab line++) {
658cb7a01acSMauro Carvalho Chehab cap->service_lines[0][line] |= regs->type.vbi_type;
659cb7a01acSMauro Carvalho Chehab }
660cb7a01acSMauro Carvalho Chehab cap->service_set |= regs->type.vbi_type;
661cb7a01acSMauro Carvalho Chehab }
662cb7a01acSMauro Carvalho Chehab return 0;
663cb7a01acSMauro Carvalho Chehab }
664cb7a01acSMauro Carvalho Chehab
665cb7a01acSMauro Carvalho Chehab /* Set vbi processing
666cb7a01acSMauro Carvalho Chehab * type - one of tvp5150_vbi_types
667cb7a01acSMauro Carvalho Chehab * line - line to gather data
668cb7a01acSMauro Carvalho Chehab * fields: bit 0 field1, bit 1, field2
669cb7a01acSMauro Carvalho Chehab * flags (default=0xf0) is a bitmask, were set means:
670cb7a01acSMauro Carvalho Chehab * bit 7: enable filtering null bytes on CC
671cb7a01acSMauro Carvalho Chehab * bit 6: send data also to FIFO
672cb7a01acSMauro Carvalho Chehab * bit 5: don't allow data with errors on FIFO
673cb7a01acSMauro Carvalho Chehab * bit 4: enable ECC when possible
674cb7a01acSMauro Carvalho Chehab * pix_align = pix alignment:
675cb7a01acSMauro Carvalho Chehab * LSB = field1
676cb7a01acSMauro Carvalho Chehab * MSB = field2
677cb7a01acSMauro Carvalho Chehab */
tvp5150_set_vbi(struct v4l2_subdev * sd,unsigned int type,u8 flags,int line,const int fields)678cb7a01acSMauro Carvalho Chehab static int tvp5150_set_vbi(struct v4l2_subdev *sd,
679cb7a01acSMauro Carvalho Chehab unsigned int type, u8 flags, int line,
680cb7a01acSMauro Carvalho Chehab const int fields)
681cb7a01acSMauro Carvalho Chehab {
682cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
683cb7a01acSMauro Carvalho Chehab v4l2_std_id std = decoder->norm;
684cb7a01acSMauro Carvalho Chehab u8 reg;
6853dd6b560SMauro Carvalho Chehab int i, pos = 0;
686cb7a01acSMauro Carvalho Chehab
687cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_ALL) {
688257e29f8SMauro Carvalho Chehab dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
689cb7a01acSMauro Carvalho Chehab return 0;
690cb7a01acSMauro Carvalho Chehab } else if (std & V4L2_STD_625_50) {
691cb7a01acSMauro Carvalho Chehab /* Don't follow NTSC Line number convension */
692cb7a01acSMauro Carvalho Chehab line += 3;
693cb7a01acSMauro Carvalho Chehab }
694cb7a01acSMauro Carvalho Chehab
695cb7a01acSMauro Carvalho Chehab if (line < 6 || line > 27)
696cb7a01acSMauro Carvalho Chehab return 0;
697cb7a01acSMauro Carvalho Chehab
6983dd6b560SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
6993dd6b560SMauro Carvalho Chehab const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i];
7003dd6b560SMauro Carvalho Chehab
7013dd6b560SMauro Carvalho Chehab if (!regs->type.vbi_type)
7023dd6b560SMauro Carvalho Chehab continue;
7033dd6b560SMauro Carvalho Chehab
704cb7a01acSMauro Carvalho Chehab if ((type & regs->type.vbi_type) &&
705cb7a01acSMauro Carvalho Chehab (line >= regs->type.ini_line) &&
706b3d930aaSGustavo A. R. Silva (line <= regs->type.end_line))
707cb7a01acSMauro Carvalho Chehab break;
708cb7a01acSMauro Carvalho Chehab pos++;
709cb7a01acSMauro Carvalho Chehab }
710b3d930aaSGustavo A. R. Silva
711cb7a01acSMauro Carvalho Chehab type = pos | (flags & 0xf0);
712cb7a01acSMauro Carvalho Chehab reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
713cb7a01acSMauro Carvalho Chehab
714b3d930aaSGustavo A. R. Silva if (fields & 1)
71528b9e227SPhilipp Zabel regmap_write(decoder->regmap, reg, type);
716cb7a01acSMauro Carvalho Chehab
717b3d930aaSGustavo A. R. Silva if (fields & 2)
71828b9e227SPhilipp Zabel regmap_write(decoder->regmap, reg + 1, type);
719cb7a01acSMauro Carvalho Chehab
720cb7a01acSMauro Carvalho Chehab return type;
721cb7a01acSMauro Carvalho Chehab }
722cb7a01acSMauro Carvalho Chehab
tvp5150_get_vbi(struct v4l2_subdev * sd,int line)7233dd6b560SMauro Carvalho Chehab static int tvp5150_get_vbi(struct v4l2_subdev *sd, int line)
724cb7a01acSMauro Carvalho Chehab {
725cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
726cb7a01acSMauro Carvalho Chehab v4l2_std_id std = decoder->norm;
727cb7a01acSMauro Carvalho Chehab u8 reg;
728cb7a01acSMauro Carvalho Chehab int pos, type = 0;
729cb7a01acSMauro Carvalho Chehab int i, ret = 0;
730cb7a01acSMauro Carvalho Chehab
731cb7a01acSMauro Carvalho Chehab if (std == V4L2_STD_ALL) {
732257e29f8SMauro Carvalho Chehab dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
733cb7a01acSMauro Carvalho Chehab return 0;
734cb7a01acSMauro Carvalho Chehab } else if (std & V4L2_STD_625_50) {
735cb7a01acSMauro Carvalho Chehab /* Don't follow NTSC Line number convension */
736cb7a01acSMauro Carvalho Chehab line += 3;
737cb7a01acSMauro Carvalho Chehab }
738cb7a01acSMauro Carvalho Chehab
739cb7a01acSMauro Carvalho Chehab if (line < 6 || line > 27)
740cb7a01acSMauro Carvalho Chehab return 0;
741cb7a01acSMauro Carvalho Chehab
742cb7a01acSMauro Carvalho Chehab reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
743cb7a01acSMauro Carvalho Chehab
744cb7a01acSMauro Carvalho Chehab for (i = 0; i <= 1; i++) {
745cb7a01acSMauro Carvalho Chehab ret = tvp5150_read(sd, reg + i);
746cb7a01acSMauro Carvalho Chehab if (ret < 0) {
747257e29f8SMauro Carvalho Chehab dev_err(sd->dev, "%s: failed with error = %d\n",
748cb7a01acSMauro Carvalho Chehab __func__, ret);
749cb7a01acSMauro Carvalho Chehab return 0;
750cb7a01acSMauro Carvalho Chehab }
751cb7a01acSMauro Carvalho Chehab pos = ret & 0x0f;
7523dd6b560SMauro Carvalho Chehab if (pos < ARRAY_SIZE(vbi_ram_default))
7533dd6b560SMauro Carvalho Chehab type |= vbi_ram_default[pos].type.vbi_type;
754cb7a01acSMauro Carvalho Chehab }
755cb7a01acSMauro Carvalho Chehab
756cb7a01acSMauro Carvalho Chehab return type;
757cb7a01acSMauro Carvalho Chehab }
758cb7a01acSMauro Carvalho Chehab
tvp5150_set_std(struct v4l2_subdev * sd,v4l2_std_id std)759cb7a01acSMauro Carvalho Chehab static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
760cb7a01acSMauro Carvalho Chehab {
761cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
762cb7a01acSMauro Carvalho Chehab int fmt = 0;
763cb7a01acSMauro Carvalho Chehab
764cb7a01acSMauro Carvalho Chehab /* First tests should be against specific std */
765cb7a01acSMauro Carvalho Chehab
76626811ae0SHans Verkuil if (std == V4L2_STD_NTSC_443) {
767cb7a01acSMauro Carvalho Chehab fmt = VIDEO_STD_NTSC_4_43_BIT;
76826811ae0SHans Verkuil } else if (std == V4L2_STD_PAL_M) {
769cb7a01acSMauro Carvalho Chehab fmt = VIDEO_STD_PAL_M_BIT;
77026811ae0SHans Verkuil } else if (std == V4L2_STD_PAL_N || std == V4L2_STD_PAL_Nc) {
771cb7a01acSMauro Carvalho Chehab fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
772cb7a01acSMauro Carvalho Chehab } else {
773cb7a01acSMauro Carvalho Chehab /* Then, test against generic ones */
774cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_NTSC)
775cb7a01acSMauro Carvalho Chehab fmt = VIDEO_STD_NTSC_MJ_BIT;
776cb7a01acSMauro Carvalho Chehab else if (std & V4L2_STD_PAL)
777cb7a01acSMauro Carvalho Chehab fmt = VIDEO_STD_PAL_BDGHIN_BIT;
778cb7a01acSMauro Carvalho Chehab else if (std & V4L2_STD_SECAM)
779cb7a01acSMauro Carvalho Chehab fmt = VIDEO_STD_SECAM_BIT;
780cb7a01acSMauro Carvalho Chehab }
781cb7a01acSMauro Carvalho Chehab
782257e29f8SMauro Carvalho Chehab dev_dbg_lvl(sd->dev, 1, debug, "Set video std register to %d.\n", fmt);
78328b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_VIDEO_STD, fmt);
784cb7a01acSMauro Carvalho Chehab return 0;
785cb7a01acSMauro Carvalho Chehab }
786cb7a01acSMauro Carvalho Chehab
tvp5150_g_std(struct v4l2_subdev * sd,v4l2_std_id * std)7870db87cc7SMarco Felsch static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
7880db87cc7SMarco Felsch {
7890db87cc7SMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
7900db87cc7SMarco Felsch
7910db87cc7SMarco Felsch *std = decoder->norm;
7920db87cc7SMarco Felsch
7930db87cc7SMarco Felsch return 0;
7940db87cc7SMarco Felsch }
7950db87cc7SMarco Felsch
tvp5150_s_std(struct v4l2_subdev * sd,v4l2_std_id std)796cb7a01acSMauro Carvalho Chehab static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
797cb7a01acSMauro Carvalho Chehab {
798cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
799baf17821SMarco Felsch struct tvp5150_connector *cur_con = decoder->cur_connector;
800baf17821SMarco Felsch v4l2_std_id supported_stds;
801cb7a01acSMauro Carvalho Chehab
802cb7a01acSMauro Carvalho Chehab if (decoder->norm == std)
803cb7a01acSMauro Carvalho Chehab return 0;
804cb7a01acSMauro Carvalho Chehab
805baf17821SMarco Felsch /* In case of no of-connectors are available no limitations are made */
806baf17821SMarco Felsch if (!decoder->connectors_num)
807baf17821SMarco Felsch supported_stds = V4L2_STD_ALL;
808baf17821SMarco Felsch else
809baf17821SMarco Felsch supported_stds = cur_con->base.connector.analog.sdtv_stds;
810baf17821SMarco Felsch
811baf17821SMarco Felsch /*
812baf17821SMarco Felsch * Check if requested std or group of std's is/are supported by the
813baf17821SMarco Felsch * connector.
814baf17821SMarco Felsch */
815baf17821SMarco Felsch if ((supported_stds & std) == 0)
816baf17821SMarco Felsch return -EINVAL;
817baf17821SMarco Felsch
818cb7a01acSMauro Carvalho Chehab /* Change cropping height limits */
819cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_525_60)
820cb7a01acSMauro Carvalho Chehab decoder->rect.height = TVP5150_V_MAX_525_60;
821cb7a01acSMauro Carvalho Chehab else
822cb7a01acSMauro Carvalho Chehab decoder->rect.height = TVP5150_V_MAX_OTHERS;
823cb7a01acSMauro Carvalho Chehab
824baf17821SMarco Felsch /* Set only the specific supported std in case of group of std's. */
825baf17821SMarco Felsch decoder->norm = supported_stds & std;
826cb7a01acSMauro Carvalho Chehab
827cb7a01acSMauro Carvalho Chehab return tvp5150_set_std(sd, std);
828cb7a01acSMauro Carvalho Chehab }
829cb7a01acSMauro Carvalho Chehab
tvp5150_read_std(struct v4l2_subdev * sd)83015695866SPhilipp Zabel static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
83115695866SPhilipp Zabel {
83215695866SPhilipp Zabel int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
83315695866SPhilipp Zabel
83415695866SPhilipp Zabel switch (val & 0x0F) {
83515695866SPhilipp Zabel case 0x01:
83615695866SPhilipp Zabel return V4L2_STD_NTSC;
83715695866SPhilipp Zabel case 0x03:
83815695866SPhilipp Zabel return V4L2_STD_PAL;
83915695866SPhilipp Zabel case 0x05:
84015695866SPhilipp Zabel return V4L2_STD_PAL_M;
84115695866SPhilipp Zabel case 0x07:
84215695866SPhilipp Zabel return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
84315695866SPhilipp Zabel case 0x09:
84415695866SPhilipp Zabel return V4L2_STD_NTSC_443;
84515695866SPhilipp Zabel case 0xb:
84615695866SPhilipp Zabel return V4L2_STD_SECAM;
84715695866SPhilipp Zabel default:
84815695866SPhilipp Zabel return V4L2_STD_UNKNOWN;
84915695866SPhilipp Zabel }
85015695866SPhilipp Zabel }
85115695866SPhilipp Zabel
query_lock(struct v4l2_subdev * sd)8525bd1d91dSMauro Carvalho Chehab static int query_lock(struct v4l2_subdev *sd)
8535bd1d91dSMauro Carvalho Chehab {
8545bd1d91dSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
8555bd1d91dSMauro Carvalho Chehab int status;
8565bd1d91dSMauro Carvalho Chehab
8575bd1d91dSMauro Carvalho Chehab if (decoder->irq)
8585bd1d91dSMauro Carvalho Chehab return decoder->lock;
8595bd1d91dSMauro Carvalho Chehab
8605bd1d91dSMauro Carvalho Chehab regmap_read(decoder->regmap, TVP5150_STATUS_REG_1, &status);
8615bd1d91dSMauro Carvalho Chehab
8625bd1d91dSMauro Carvalho Chehab /* For standard detection, we need the 3 locks */
8635bd1d91dSMauro Carvalho Chehab return (status & 0x0e) == 0x0e;
8645bd1d91dSMauro Carvalho Chehab }
8655bd1d91dSMauro Carvalho Chehab
tvp5150_querystd(struct v4l2_subdev * sd,v4l2_std_id * std_id)866ee9a6ff6SPhilipp Zabel static int tvp5150_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
867ee9a6ff6SPhilipp Zabel {
8685bd1d91dSMauro Carvalho Chehab *std_id = query_lock(sd) ? tvp5150_read_std(sd) : V4L2_STD_UNKNOWN;
869ee9a6ff6SPhilipp Zabel
870ee9a6ff6SPhilipp Zabel return 0;
871ee9a6ff6SPhilipp Zabel }
872ee9a6ff6SPhilipp Zabel
8732f0a5c65SPhilipp Zabel static const struct v4l2_event tvp5150_ev_fmt = {
8742f0a5c65SPhilipp Zabel .type = V4L2_EVENT_SOURCE_CHANGE,
8752f0a5c65SPhilipp Zabel .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
8762f0a5c65SPhilipp Zabel };
8772f0a5c65SPhilipp Zabel
tvp5150_isr(int irq,void * dev_id)8788e4c97e0SPhilipp Zabel static irqreturn_t tvp5150_isr(int irq, void *dev_id)
8798e4c97e0SPhilipp Zabel {
8808e4c97e0SPhilipp Zabel struct tvp5150 *decoder = dev_id;
8818e4c97e0SPhilipp Zabel struct regmap *map = decoder->regmap;
88262a764e1SPhilipp Zabel unsigned int mask, active = 0, status = 0;
88362a764e1SPhilipp Zabel
88462a764e1SPhilipp Zabel mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE |
88562a764e1SPhilipp Zabel TVP5150_MISC_CTL_CLOCK_OE;
8868e4c97e0SPhilipp Zabel
8878e4c97e0SPhilipp Zabel regmap_read(map, TVP5150_INT_STATUS_REG_A, &status);
8888e4c97e0SPhilipp Zabel if (status) {
8898e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_INT_STATUS_REG_A, status);
8908e4c97e0SPhilipp Zabel
89162a764e1SPhilipp Zabel if (status & TVP5150_INT_A_LOCK) {
8928e4c97e0SPhilipp Zabel decoder->lock = !!(status & TVP5150_INT_A_LOCK_STATUS);
8937bb3c338SPhilipp Zabel dev_dbg_lvl(decoder->sd.dev, 1, debug,
8947bb3c338SPhilipp Zabel "sync lo%s signal\n",
8957bb3c338SPhilipp Zabel decoder->lock ? "ck" : "ss");
8962f0a5c65SPhilipp Zabel v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt);
89762a764e1SPhilipp Zabel regmap_update_bits(map, TVP5150_MISC_CTL, mask,
89862a764e1SPhilipp Zabel decoder->lock ? decoder->oe : 0);
89962a764e1SPhilipp Zabel }
9008e4c97e0SPhilipp Zabel
9018e4c97e0SPhilipp Zabel return IRQ_HANDLED;
9028e4c97e0SPhilipp Zabel }
9038e4c97e0SPhilipp Zabel
9048e4c97e0SPhilipp Zabel regmap_read(map, TVP5150_INT_ACTIVE_REG_B, &active);
9058e4c97e0SPhilipp Zabel if (active) {
9068e4c97e0SPhilipp Zabel status = 0;
9078e4c97e0SPhilipp Zabel regmap_read(map, TVP5150_INT_STATUS_REG_B, &status);
9088e4c97e0SPhilipp Zabel if (status)
9098e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_INT_RESET_REG_B, status);
9108e4c97e0SPhilipp Zabel }
9118e4c97e0SPhilipp Zabel
9128e4c97e0SPhilipp Zabel return IRQ_HANDLED;
9138e4c97e0SPhilipp Zabel }
9148e4c97e0SPhilipp Zabel
tvp5150_reset(struct v4l2_subdev * sd,u32 val)915cb7a01acSMauro Carvalho Chehab static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
916cb7a01acSMauro Carvalho Chehab {
917cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
9188105e1bcSPhilipp Zabel struct regmap *map = decoder->regmap;
919cb7a01acSMauro Carvalho Chehab
920cb7a01acSMauro Carvalho Chehab /* Initializes TVP5150 to its default values */
921cb7a01acSMauro Carvalho Chehab tvp5150_write_inittab(sd, tvp5150_init_default);
922cb7a01acSMauro Carvalho Chehab
9238e4c97e0SPhilipp Zabel if (decoder->irq) {
9248e4c97e0SPhilipp Zabel /* Configure pins: FID, VSYNC, INTREQ, SCLK */
9258e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x0);
9268e4c97e0SPhilipp Zabel /* Set interrupt polarity to active high */
9278e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE | 0x1);
9288e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x1);
9298e4c97e0SPhilipp Zabel } else {
9308105e1bcSPhilipp Zabel /* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */
9318105e1bcSPhilipp Zabel regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2);
9328e4c97e0SPhilipp Zabel /* Keep interrupt polarity active low */
9338e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE);
9348e4c97e0SPhilipp Zabel regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0);
9358e4c97e0SPhilipp Zabel }
9368105e1bcSPhilipp Zabel
937cb7a01acSMauro Carvalho Chehab /* Initializes VDP registers */
9383dd6b560SMauro Carvalho Chehab tvp5150_vdp_init(sd);
939cb7a01acSMauro Carvalho Chehab
940cb7a01acSMauro Carvalho Chehab /* Selects decoder input */
941cb7a01acSMauro Carvalho Chehab tvp5150_selmux(sd);
942cb7a01acSMauro Carvalho Chehab
943cb7a01acSMauro Carvalho Chehab /* Initialize image preferences */
944cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_setup(&decoder->hdl);
945cb7a01acSMauro Carvalho Chehab
9461bb086bcSPhilipp Zabel return 0;
9471bb086bcSPhilipp Zabel }
9481bb086bcSPhilipp Zabel
tvp5150_enable(struct v4l2_subdev * sd)9491bb086bcSPhilipp Zabel static int tvp5150_enable(struct v4l2_subdev *sd)
9501bb086bcSPhilipp Zabel {
9511bb086bcSPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
9521bb086bcSPhilipp Zabel v4l2_std_id std;
9531bb086bcSPhilipp Zabel
9541bb086bcSPhilipp Zabel /* Initializes TVP5150 to stream enabled values */
9551bb086bcSPhilipp Zabel tvp5150_write_inittab(sd, tvp5150_init_enable);
9561bb086bcSPhilipp Zabel
95715695866SPhilipp Zabel if (decoder->norm == V4L2_STD_ALL)
95815695866SPhilipp Zabel std = tvp5150_read_std(sd);
95915695866SPhilipp Zabel else
96015695866SPhilipp Zabel std = decoder->norm;
96115695866SPhilipp Zabel
96215695866SPhilipp Zabel /* Disable autoswitch mode */
96315695866SPhilipp Zabel tvp5150_set_std(sd, std);
964a2e5f1b3SJavier Martinez Canillas
96562a764e1SPhilipp Zabel /*
96662a764e1SPhilipp Zabel * Enable the YCbCr and clock outputs. In discrete sync mode
967331ca86aSlijian * (non-BT.656) additionally enable the sync outputs.
96862a764e1SPhilipp Zabel */
96962a764e1SPhilipp Zabel switch (decoder->mbus_type) {
97062a764e1SPhilipp Zabel case V4L2_MBUS_PARALLEL:
9718a7441baSMarco Felsch /* 8-bit 4:2:2 YUV with discrete sync output */
9728a7441baSMarco Felsch regmap_update_bits(decoder->regmap, TVP5150_DATA_RATE_SEL,
9738a7441baSMarco Felsch 0x7, 0x0);
97462a764e1SPhilipp Zabel decoder->oe = TVP5150_MISC_CTL_YCBCR_OE |
97562a764e1SPhilipp Zabel TVP5150_MISC_CTL_CLOCK_OE |
97662a764e1SPhilipp Zabel TVP5150_MISC_CTL_SYNC_OE;
97762a764e1SPhilipp Zabel break;
97862a764e1SPhilipp Zabel case V4L2_MBUS_BT656:
97962a764e1SPhilipp Zabel decoder->oe = TVP5150_MISC_CTL_YCBCR_OE |
98062a764e1SPhilipp Zabel TVP5150_MISC_CTL_CLOCK_OE;
98162a764e1SPhilipp Zabel break;
98262a764e1SPhilipp Zabel default:
98362a764e1SPhilipp Zabel return -EINVAL;
98462a764e1SPhilipp Zabel }
985a2e5f1b3SJavier Martinez Canillas
986cb7a01acSMauro Carvalho Chehab return 0;
987cb7a01acSMauro Carvalho Chehab };
988cb7a01acSMauro Carvalho Chehab
tvp5150_s_ctrl(struct v4l2_ctrl * ctrl)989cb7a01acSMauro Carvalho Chehab static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
990cb7a01acSMauro Carvalho Chehab {
991cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = to_sd(ctrl);
992c43875f6SMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
993cb7a01acSMauro Carvalho Chehab
994cb7a01acSMauro Carvalho Chehab switch (ctrl->id) {
995cb7a01acSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS:
99628b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_BRIGHT_CTL, ctrl->val);
997cb7a01acSMauro Carvalho Chehab return 0;
998cb7a01acSMauro Carvalho Chehab case V4L2_CID_CONTRAST:
99928b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_CONTRAST_CTL, ctrl->val);
1000cb7a01acSMauro Carvalho Chehab return 0;
1001cb7a01acSMauro Carvalho Chehab case V4L2_CID_SATURATION:
100228b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_SATURATION_CTL,
100328b9e227SPhilipp Zabel ctrl->val);
1004cb7a01acSMauro Carvalho Chehab return 0;
1005cb7a01acSMauro Carvalho Chehab case V4L2_CID_HUE:
100628b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_HUE_CTL, ctrl->val);
10072d29bcc8SMarco Felsch return 0;
1008c43875f6SMauro Carvalho Chehab case V4L2_CID_TEST_PATTERN:
1009c43875f6SMauro Carvalho Chehab decoder->enable = ctrl->val ? false : true;
1010c43875f6SMauro Carvalho Chehab tvp5150_selmux(sd);
1011cb7a01acSMauro Carvalho Chehab return 0;
1012cb7a01acSMauro Carvalho Chehab }
1013cb7a01acSMauro Carvalho Chehab return -EINVAL;
1014cb7a01acSMauro Carvalho Chehab }
1015cb7a01acSMauro Carvalho Chehab
tvp5150_set_default(v4l2_std_id std,struct v4l2_rect * crop)10165cb82940SMarco Felsch static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop)
10175cb82940SMarco Felsch {
10185cb82940SMarco Felsch /* Default is no cropping */
10195cb82940SMarco Felsch crop->top = 0;
10205cb82940SMarco Felsch crop->left = 0;
10215cb82940SMarco Felsch crop->width = TVP5150_H_MAX;
10225cb82940SMarco Felsch if (std & V4L2_STD_525_60)
10235cb82940SMarco Felsch crop->height = TVP5150_V_MAX_525_60;
10245cb82940SMarco Felsch else
10255cb82940SMarco Felsch crop->height = TVP5150_V_MAX_OTHERS;
10265cb82940SMarco Felsch }
10275cb82940SMarco Felsch
102846fe6e7dSMarco Felsch static struct v4l2_rect *
tvp5150_get_pad_crop(struct tvp5150 * decoder,struct v4l2_subdev_state * sd_state,unsigned int pad,enum v4l2_subdev_format_whence which)102946fe6e7dSMarco Felsch tvp5150_get_pad_crop(struct tvp5150 *decoder,
10300d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, unsigned int pad,
103146fe6e7dSMarco Felsch enum v4l2_subdev_format_whence which)
103246fe6e7dSMarco Felsch {
103346fe6e7dSMarco Felsch switch (which) {
103446fe6e7dSMarco Felsch case V4L2_SUBDEV_FORMAT_ACTIVE:
103546fe6e7dSMarco Felsch return &decoder->rect;
103646fe6e7dSMarco Felsch case V4L2_SUBDEV_FORMAT_TRY:
103746fe6e7dSMarco Felsch #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
10380d346d2aSTomi Valkeinen return v4l2_subdev_get_try_crop(&decoder->sd, sd_state, pad);
103946fe6e7dSMarco Felsch #else
104046fe6e7dSMarco Felsch return ERR_PTR(-EINVAL);
104146fe6e7dSMarco Felsch #endif
104246fe6e7dSMarco Felsch default:
104346fe6e7dSMarco Felsch return ERR_PTR(-EINVAL);
104446fe6e7dSMarco Felsch }
104546fe6e7dSMarco Felsch }
104646fe6e7dSMarco Felsch
tvp5150_fill_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)1047da298c6dSHans Verkuil static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
10480d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
1049da298c6dSHans Verkuil struct v4l2_subdev_format *format)
1050cb7a01acSMauro Carvalho Chehab {
1051da298c6dSHans Verkuil struct v4l2_mbus_framefmt *f;
1052cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
1053cb7a01acSMauro Carvalho Chehab
1054bc322c0dSMauro Carvalho Chehab if (!format || (format->pad != TVP5150_PAD_VID_OUT))
1055cb7a01acSMauro Carvalho Chehab return -EINVAL;
1056cb7a01acSMauro Carvalho Chehab
1057da298c6dSHans Verkuil f = &format->format;
1058da298c6dSHans Verkuil
1059cb7a01acSMauro Carvalho Chehab f->width = decoder->rect.width;
10601831af09SJavier Martinez Canillas f->height = decoder->rect.height / 2;
1061cb7a01acSMauro Carvalho Chehab
10625cb82940SMarco Felsch f->code = TVP5150_MBUS_FMT;
10635cb82940SMarco Felsch f->field = TVP5150_FIELD;
10645cb82940SMarco Felsch f->colorspace = TVP5150_COLORSPACE;
1065cb7a01acSMauro Carvalho Chehab
1066257e29f8SMauro Carvalho Chehab dev_dbg_lvl(sd->dev, 1, debug, "width = %d, height = %d\n", f->width,
1067cb7a01acSMauro Carvalho Chehab f->height);
1068cb7a01acSMauro Carvalho Chehab return 0;
1069cb7a01acSMauro Carvalho Chehab }
1070cb7a01acSMauro Carvalho Chehab
tvp5150_get_hmax(struct v4l2_subdev * sd)107146fe6e7dSMarco Felsch static unsigned int tvp5150_get_hmax(struct v4l2_subdev *sd)
1072cb7a01acSMauro Carvalho Chehab {
1073cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
1074cb7a01acSMauro Carvalho Chehab v4l2_std_id std;
1075cb7a01acSMauro Carvalho Chehab
1076cb7a01acSMauro Carvalho Chehab /* Calculate height based on current standard */
1077cb7a01acSMauro Carvalho Chehab if (decoder->norm == V4L2_STD_ALL)
1078cb7a01acSMauro Carvalho Chehab std = tvp5150_read_std(sd);
1079cb7a01acSMauro Carvalho Chehab else
1080cb7a01acSMauro Carvalho Chehab std = decoder->norm;
1081cb7a01acSMauro Carvalho Chehab
108246fe6e7dSMarco Felsch return (std & V4L2_STD_525_60) ?
108346fe6e7dSMarco Felsch TVP5150_V_MAX_525_60 : TVP5150_V_MAX_OTHERS;
108446fe6e7dSMarco Felsch }
1085cb7a01acSMauro Carvalho Chehab
tvp5150_set_hw_selection(struct v4l2_subdev * sd,struct v4l2_rect * rect)108646fe6e7dSMarco Felsch static void tvp5150_set_hw_selection(struct v4l2_subdev *sd,
108746fe6e7dSMarco Felsch struct v4l2_rect *rect)
108846fe6e7dSMarco Felsch {
108946fe6e7dSMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
109046fe6e7dSMarco Felsch unsigned int hmax = tvp5150_get_hmax(sd);
1091cb7a01acSMauro Carvalho Chehab
1092b4125e5bSMarco Felsch regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
109328b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
1094b4125e5bSMarco Felsch rect->top + rect->height - hmax);
109528b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
1096b4125e5bSMarco Felsch rect->left >> TVP5150_CROP_SHIFT);
109728b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
1098b4125e5bSMarco Felsch rect->left | (1 << TVP5150_CROP_SHIFT));
109928b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
1100b4125e5bSMarco Felsch (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
1101cb7a01acSMauro Carvalho Chehab TVP5150_CROP_SHIFT);
110228b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
1103b4125e5bSMarco Felsch rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
110446fe6e7dSMarco Felsch }
1105cb7a01acSMauro Carvalho Chehab
tvp5150_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)110646fe6e7dSMarco Felsch static int tvp5150_set_selection(struct v4l2_subdev *sd,
11070d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
110846fe6e7dSMarco Felsch struct v4l2_subdev_selection *sel)
110946fe6e7dSMarco Felsch {
111046fe6e7dSMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
111146fe6e7dSMarco Felsch struct v4l2_rect *rect = &sel->r;
111246fe6e7dSMarco Felsch struct v4l2_rect *crop;
111346fe6e7dSMarco Felsch unsigned int hmax;
111446fe6e7dSMarco Felsch
111546fe6e7dSMarco Felsch if (sel->target != V4L2_SEL_TGT_CROP)
111646fe6e7dSMarco Felsch return -EINVAL;
111746fe6e7dSMarco Felsch
111846fe6e7dSMarco Felsch dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
111946fe6e7dSMarco Felsch __func__, rect->left, rect->top, rect->width, rect->height);
112046fe6e7dSMarco Felsch
112146fe6e7dSMarco Felsch /* tvp5150 has some special limits */
112246fe6e7dSMarco Felsch rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
112346fe6e7dSMarco Felsch rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
112446fe6e7dSMarco Felsch hmax = tvp5150_get_hmax(sd);
112546fe6e7dSMarco Felsch
112646fe6e7dSMarco Felsch /*
112746fe6e7dSMarco Felsch * alignments:
112846fe6e7dSMarco Felsch * - width = 2 due to UYVY colorspace
112946fe6e7dSMarco Felsch * - height, image = no special alignment
113046fe6e7dSMarco Felsch */
113146fe6e7dSMarco Felsch v4l_bound_align_image(&rect->width,
113246fe6e7dSMarco Felsch TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left,
113346fe6e7dSMarco Felsch TVP5150_H_MAX - rect->left, 1, &rect->height,
113446fe6e7dSMarco Felsch hmax - TVP5150_MAX_CROP_TOP - rect->top,
113546fe6e7dSMarco Felsch hmax - rect->top, 0, 0);
113646fe6e7dSMarco Felsch
113746fe6e7dSMarco Felsch if (!IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) &&
113846fe6e7dSMarco Felsch sel->which == V4L2_SUBDEV_FORMAT_TRY)
113946fe6e7dSMarco Felsch return 0;
114046fe6e7dSMarco Felsch
11410d346d2aSTomi Valkeinen crop = tvp5150_get_pad_crop(decoder, sd_state, sel->pad, sel->which);
114246fe6e7dSMarco Felsch if (IS_ERR(crop))
114346fe6e7dSMarco Felsch return PTR_ERR(crop);
114446fe6e7dSMarco Felsch
114546fe6e7dSMarco Felsch /*
114646fe6e7dSMarco Felsch * Update output image size if the selection (crop) rectangle size or
114746fe6e7dSMarco Felsch * position has been modified.
114846fe6e7dSMarco Felsch */
114946fe6e7dSMarco Felsch if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
115046fe6e7dSMarco Felsch !v4l2_rect_equal(rect, crop))
115146fe6e7dSMarco Felsch tvp5150_set_hw_selection(sd, rect);
115246fe6e7dSMarco Felsch
115346fe6e7dSMarco Felsch *crop = *rect;
1154cb7a01acSMauro Carvalho Chehab
1155cb7a01acSMauro Carvalho Chehab return 0;
1156cb7a01acSMauro Carvalho Chehab }
1157cb7a01acSMauro Carvalho Chehab
tvp5150_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)115810d5509cSHans Verkuil static int tvp5150_get_selection(struct v4l2_subdev *sd,
11590d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
116010d5509cSHans Verkuil struct v4l2_subdev_selection *sel)
1161cb7a01acSMauro Carvalho Chehab {
116210d5509cSHans Verkuil struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
116346fe6e7dSMarco Felsch struct v4l2_rect *crop;
1164cb7a01acSMauro Carvalho Chehab v4l2_std_id std;
1165cb7a01acSMauro Carvalho Chehab
116610d5509cSHans Verkuil switch (sel->target) {
116710d5509cSHans Verkuil case V4L2_SEL_TGT_CROP_BOUNDS:
116810d5509cSHans Verkuil sel->r.left = 0;
116910d5509cSHans Verkuil sel->r.top = 0;
117010d5509cSHans Verkuil sel->r.width = TVP5150_H_MAX;
1171cb7a01acSMauro Carvalho Chehab
1172cb7a01acSMauro Carvalho Chehab /* Calculate height based on current standard */
1173cb7a01acSMauro Carvalho Chehab if (decoder->norm == V4L2_STD_ALL)
1174cb7a01acSMauro Carvalho Chehab std = tvp5150_read_std(sd);
1175cb7a01acSMauro Carvalho Chehab else
1176cb7a01acSMauro Carvalho Chehab std = decoder->norm;
1177cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_525_60)
117810d5509cSHans Verkuil sel->r.height = TVP5150_V_MAX_525_60;
1179cb7a01acSMauro Carvalho Chehab else
118010d5509cSHans Verkuil sel->r.height = TVP5150_V_MAX_OTHERS;
1181cb7a01acSMauro Carvalho Chehab return 0;
118210d5509cSHans Verkuil case V4L2_SEL_TGT_CROP:
11830d346d2aSTomi Valkeinen crop = tvp5150_get_pad_crop(decoder, sd_state, sel->pad,
118446fe6e7dSMarco Felsch sel->which);
118546fe6e7dSMarco Felsch if (IS_ERR(crop))
118646fe6e7dSMarco Felsch return PTR_ERR(crop);
118746fe6e7dSMarco Felsch sel->r = *crop;
118810d5509cSHans Verkuil return 0;
118910d5509cSHans Verkuil default:
119010d5509cSHans Verkuil return -EINVAL;
119110d5509cSHans Verkuil }
1192cb7a01acSMauro Carvalho Chehab }
1193cb7a01acSMauro Carvalho Chehab
tvp5150_get_mbus_config(struct v4l2_subdev * sd,unsigned int pad,struct v4l2_mbus_config * cfg)11940c3da525SJacopo Mondi static int tvp5150_get_mbus_config(struct v4l2_subdev *sd,
11950c3da525SJacopo Mondi unsigned int pad,
1196dd3a46bbSLaurent Pinchart struct v4l2_mbus_config *cfg)
1197dd3a46bbSLaurent Pinchart {
1198a2e5f1b3SJavier Martinez Canillas struct tvp5150 *decoder = to_tvp5150(sd);
1199a2e5f1b3SJavier Martinez Canillas
1200a2e5f1b3SJavier Martinez Canillas cfg->type = decoder->mbus_type;
12016a7bdd89SLaurent Pinchart cfg->bus.parallel.flags = V4L2_MBUS_MASTER
12026a7bdd89SLaurent Pinchart | V4L2_MBUS_PCLK_SAMPLE_RISING
12036a7bdd89SLaurent Pinchart | V4L2_MBUS_FIELD_EVEN_LOW
12046a7bdd89SLaurent Pinchart | V4L2_MBUS_DATA_ACTIVE_HIGH;
1205dd3a46bbSLaurent Pinchart
1206dd3a46bbSLaurent Pinchart return 0;
1207dd3a46bbSLaurent Pinchart }
1208dd3a46bbSLaurent Pinchart
1209cb7a01acSMauro Carvalho Chehab /****************************************************************************
1210e545ac87SLaurent Pinchart V4L2 subdev pad ops
1211e545ac87SLaurent Pinchart ****************************************************************************/
tvp5150_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)1212b440b733SPhilipp Zabel static int tvp5150_init_cfg(struct v4l2_subdev *sd,
12130d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state)
1214b440b733SPhilipp Zabel {
1215b440b733SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
1216b440b733SPhilipp Zabel v4l2_std_id std;
1217b440b733SPhilipp Zabel
1218b440b733SPhilipp Zabel /*
1219b440b733SPhilipp Zabel * Reset selection to maximum on subdev_open() if autodetection is on
1220b440b733SPhilipp Zabel * and a standard change is detected.
1221b440b733SPhilipp Zabel */
1222b440b733SPhilipp Zabel if (decoder->norm == V4L2_STD_ALL) {
1223b440b733SPhilipp Zabel std = tvp5150_read_std(sd);
1224b440b733SPhilipp Zabel if (std != decoder->detected_norm) {
1225b440b733SPhilipp Zabel decoder->detected_norm = std;
1226b440b733SPhilipp Zabel tvp5150_set_default(std, &decoder->rect);
1227b440b733SPhilipp Zabel }
1228b440b733SPhilipp Zabel }
1229b440b733SPhilipp Zabel
1230b440b733SPhilipp Zabel return 0;
1231b440b733SPhilipp Zabel }
1232b440b733SPhilipp Zabel
tvp5150_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)1233e545ac87SLaurent Pinchart static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd,
12340d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
1235e545ac87SLaurent Pinchart struct v4l2_subdev_mbus_code_enum *code)
1236e545ac87SLaurent Pinchart {
1237e545ac87SLaurent Pinchart if (code->pad || code->index)
1238e545ac87SLaurent Pinchart return -EINVAL;
1239e545ac87SLaurent Pinchart
12405cb82940SMarco Felsch code->code = TVP5150_MBUS_FMT;
1241e545ac87SLaurent Pinchart return 0;
1242e545ac87SLaurent Pinchart }
1243e545ac87SLaurent Pinchart
tvp5150_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)1244e545ac87SLaurent Pinchart static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
12450d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
1246e545ac87SLaurent Pinchart struct v4l2_subdev_frame_size_enum *fse)
1247e545ac87SLaurent Pinchart {
1248e545ac87SLaurent Pinchart struct tvp5150 *decoder = to_tvp5150(sd);
1249e545ac87SLaurent Pinchart
12505cb82940SMarco Felsch if (fse->index >= 8 || fse->code != TVP5150_MBUS_FMT)
1251e545ac87SLaurent Pinchart return -EINVAL;
1252e545ac87SLaurent Pinchart
12535cb82940SMarco Felsch fse->code = TVP5150_MBUS_FMT;
1254e545ac87SLaurent Pinchart fse->min_width = decoder->rect.width;
1255e545ac87SLaurent Pinchart fse->max_width = decoder->rect.width;
1256e545ac87SLaurent Pinchart fse->min_height = decoder->rect.height / 2;
1257e545ac87SLaurent Pinchart fse->max_height = decoder->rect.height / 2;
1258e545ac87SLaurent Pinchart
1259e545ac87SLaurent Pinchart return 0;
1260e545ac87SLaurent Pinchart }
1261e545ac87SLaurent Pinchart
1262e545ac87SLaurent Pinchart /****************************************************************************
12630556f1d5SMarco Felsch * Media entity ops
12640556f1d5SMarco Felsch ****************************************************************************/
12650556f1d5SMarco Felsch #if defined(CONFIG_MEDIA_CONTROLLER)
tvp5150_set_link(struct media_pad * connector_pad,struct media_pad * tvp5150_pad,u32 flags)12660556f1d5SMarco Felsch static int tvp5150_set_link(struct media_pad *connector_pad,
12670556f1d5SMarco Felsch struct media_pad *tvp5150_pad, u32 flags)
12680556f1d5SMarco Felsch {
12690556f1d5SMarco Felsch struct media_link *link;
12700556f1d5SMarco Felsch
12710556f1d5SMarco Felsch link = media_entity_find_link(connector_pad, tvp5150_pad);
12720556f1d5SMarco Felsch if (!link)
12730556f1d5SMarco Felsch return -EINVAL;
12740556f1d5SMarco Felsch
12750556f1d5SMarco Felsch link->flags = flags;
12760556f1d5SMarco Felsch link->reverse->flags = link->flags;
12770556f1d5SMarco Felsch
12780556f1d5SMarco Felsch return 0;
12790556f1d5SMarco Felsch }
12800556f1d5SMarco Felsch
tvp5150_disable_all_input_links(struct tvp5150 * decoder)12810556f1d5SMarco Felsch static int tvp5150_disable_all_input_links(struct tvp5150 *decoder)
12820556f1d5SMarco Felsch {
12830556f1d5SMarco Felsch struct media_pad *connector_pad;
12840556f1d5SMarco Felsch unsigned int i;
12850556f1d5SMarco Felsch int err;
12860556f1d5SMarco Felsch
12870556f1d5SMarco Felsch for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
1288b2e44430SLaurent Pinchart connector_pad = media_pad_remote_pad_first(&decoder->pads[i]);
12890556f1d5SMarco Felsch if (!connector_pad)
12900556f1d5SMarco Felsch continue;
12910556f1d5SMarco Felsch
12920556f1d5SMarco Felsch err = tvp5150_set_link(connector_pad, &decoder->pads[i], 0);
12930556f1d5SMarco Felsch if (err)
12940556f1d5SMarco Felsch return err;
12950556f1d5SMarco Felsch }
12960556f1d5SMarco Felsch
12970556f1d5SMarco Felsch return 0;
12980556f1d5SMarco Felsch }
12990556f1d5SMarco Felsch
13000556f1d5SMarco Felsch static int tvp5150_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
13010556f1d5SMarco Felsch u32 config);
13020556f1d5SMarco Felsch
tvp5150_link_setup(struct media_entity * entity,const struct media_pad * tvp5150_pad,const struct media_pad * remote,u32 flags)13030556f1d5SMarco Felsch static int tvp5150_link_setup(struct media_entity *entity,
13040556f1d5SMarco Felsch const struct media_pad *tvp5150_pad,
13050556f1d5SMarco Felsch const struct media_pad *remote, u32 flags)
13060556f1d5SMarco Felsch {
13070556f1d5SMarco Felsch struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
13080556f1d5SMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
13090556f1d5SMarco Felsch struct media_pad *other_tvp5150_pad =
13100556f1d5SMarco Felsch &decoder->pads[tvp5150_pad->index ^ 1];
13110556f1d5SMarco Felsch struct v4l2_fwnode_connector *v4l2c;
13120556f1d5SMarco Felsch bool is_svideo = false;
13130556f1d5SMarco Felsch unsigned int i;
13140556f1d5SMarco Felsch int err;
13150556f1d5SMarco Felsch
13160556f1d5SMarco Felsch /*
13170556f1d5SMarco Felsch * The TVP5150 state is determined by the enabled sink pad link(s).
13180556f1d5SMarco Felsch * Enabling or disabling the source pad link has no effect.
13190556f1d5SMarco Felsch */
13200556f1d5SMarco Felsch if (tvp5150_pad->flags & MEDIA_PAD_FL_SOURCE)
13210556f1d5SMarco Felsch return 0;
13220556f1d5SMarco Felsch
13230556f1d5SMarco Felsch /* Check if the svideo connector should be enabled */
13240556f1d5SMarco Felsch for (i = 0; i < decoder->connectors_num; i++) {
13250556f1d5SMarco Felsch if (remote->entity == &decoder->connectors[i].ent) {
13260556f1d5SMarco Felsch v4l2c = &decoder->connectors[i].base;
13270556f1d5SMarco Felsch is_svideo = v4l2c->type == V4L2_CONN_SVIDEO;
13280556f1d5SMarco Felsch break;
13290556f1d5SMarco Felsch }
13300556f1d5SMarco Felsch }
13310556f1d5SMarco Felsch
13320556f1d5SMarco Felsch dev_dbg_lvl(sd->dev, 1, debug, "link setup '%s':%d->'%s':%d[%d]",
13330556f1d5SMarco Felsch remote->entity->name, remote->index,
13340556f1d5SMarco Felsch tvp5150_pad->entity->name, tvp5150_pad->index,
13350556f1d5SMarco Felsch flags & MEDIA_LNK_FL_ENABLED);
13360556f1d5SMarco Felsch if (is_svideo)
13370556f1d5SMarco Felsch dev_dbg_lvl(sd->dev, 1, debug,
13380556f1d5SMarco Felsch "link setup '%s':%d->'%s':%d[%d]",
13390556f1d5SMarco Felsch remote->entity->name, remote->index,
13400556f1d5SMarco Felsch other_tvp5150_pad->entity->name,
13410556f1d5SMarco Felsch other_tvp5150_pad->index,
13420556f1d5SMarco Felsch flags & MEDIA_LNK_FL_ENABLED);
13430556f1d5SMarco Felsch
13440556f1d5SMarco Felsch /*
13450556f1d5SMarco Felsch * The TVP5150 has an internal mux which allows the following setup:
13460556f1d5SMarco Felsch *
13470556f1d5SMarco Felsch * comp-connector1 --\
13480556f1d5SMarco Felsch * |---> AIP1A
13490556f1d5SMarco Felsch * /
13500556f1d5SMarco Felsch * svideo-connector -|
13510556f1d5SMarco Felsch * \
13520556f1d5SMarco Felsch * |---> AIP1B
13530556f1d5SMarco Felsch * comp-connector2 --/
13540556f1d5SMarco Felsch *
13550556f1d5SMarco Felsch * We can't rely on user space that the current connector gets disabled
13560556f1d5SMarco Felsch * first before enabling the new connector. Disable all active
13570556f1d5SMarco Felsch * connector links to be on the safe side.
13580556f1d5SMarco Felsch */
13590556f1d5SMarco Felsch err = tvp5150_disable_all_input_links(decoder);
13600556f1d5SMarco Felsch if (err)
13610556f1d5SMarco Felsch return err;
13620556f1d5SMarco Felsch
13630556f1d5SMarco Felsch tvp5150_s_routing(sd, is_svideo ? TVP5150_SVIDEO : tvp5150_pad->index,
13640556f1d5SMarco Felsch flags & MEDIA_LNK_FL_ENABLED ? TVP5150_NORMAL :
13650556f1d5SMarco Felsch TVP5150_BLACK_SCREEN, 0);
13660556f1d5SMarco Felsch
13670556f1d5SMarco Felsch if (flags & MEDIA_LNK_FL_ENABLED) {
1368baf17821SMarco Felsch struct v4l2_fwnode_connector_analog *v4l2ca;
1369baf17821SMarco Felsch u32 new_norm;
1370baf17821SMarco Felsch
13710556f1d5SMarco Felsch /*
13720556f1d5SMarco Felsch * S-Video connector is conneted to both ports AIP1A and AIP1B.
13730556f1d5SMarco Felsch * Both links must be enabled in one-shot regardless which link
13740556f1d5SMarco Felsch * the user requests.
13750556f1d5SMarco Felsch */
13760556f1d5SMarco Felsch if (is_svideo) {
13770556f1d5SMarco Felsch err = tvp5150_set_link((struct media_pad *)remote,
13780556f1d5SMarco Felsch other_tvp5150_pad, flags);
13790556f1d5SMarco Felsch if (err)
13800556f1d5SMarco Felsch return err;
13810556f1d5SMarco Felsch }
1382baf17821SMarco Felsch
1383baf17821SMarco Felsch if (!decoder->connectors_num)
1384baf17821SMarco Felsch return 0;
1385baf17821SMarco Felsch
1386baf17821SMarco Felsch /* Update the current connector */
1387baf17821SMarco Felsch decoder->cur_connector =
1388baf17821SMarco Felsch container_of(remote, struct tvp5150_connector, pad);
1389baf17821SMarco Felsch
1390baf17821SMarco Felsch /*
1391baf17821SMarco Felsch * Do nothing if the new connector supports the same tv-norms as
1392baf17821SMarco Felsch * the old one.
1393baf17821SMarco Felsch */
1394baf17821SMarco Felsch v4l2ca = &decoder->cur_connector->base.connector.analog;
1395baf17821SMarco Felsch new_norm = decoder->norm & v4l2ca->sdtv_stds;
1396baf17821SMarco Felsch if (decoder->norm == new_norm)
1397baf17821SMarco Felsch return 0;
1398baf17821SMarco Felsch
1399baf17821SMarco Felsch /*
1400baf17821SMarco Felsch * Fallback to the new connector tv-norms if we can't find any
1401baf17821SMarco Felsch * common between the current tv-norm and the new one.
1402baf17821SMarco Felsch */
1403baf17821SMarco Felsch tvp5150_s_std(sd, new_norm ? new_norm : v4l2ca->sdtv_stds);
14040556f1d5SMarco Felsch }
14050556f1d5SMarco Felsch
14060556f1d5SMarco Felsch return 0;
14070556f1d5SMarco Felsch }
14080556f1d5SMarco Felsch
14090556f1d5SMarco Felsch static const struct media_entity_operations tvp5150_sd_media_ops = {
14100556f1d5SMarco Felsch .link_setup = tvp5150_link_setup,
14110556f1d5SMarco Felsch };
14120556f1d5SMarco Felsch #endif
14130556f1d5SMarco Felsch /****************************************************************************
1414cb7a01acSMauro Carvalho Chehab I2C Command
1415cb7a01acSMauro Carvalho Chehab ****************************************************************************/
tvp5150_runtime_suspend(struct device * dev)141673549a69SMarco Felsch static int __maybe_unused tvp5150_runtime_suspend(struct device *dev)
141773549a69SMarco Felsch {
14181c891423SKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev);
141973549a69SMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
142073549a69SMarco Felsch
142173549a69SMarco Felsch if (decoder->irq)
142273549a69SMarco Felsch /* Disable lock interrupt */
142373549a69SMarco Felsch return regmap_update_bits(decoder->regmap,
142473549a69SMarco Felsch TVP5150_INT_ENABLE_REG_A,
142573549a69SMarco Felsch TVP5150_INT_A_LOCK, 0);
142673549a69SMarco Felsch return 0;
142773549a69SMarco Felsch }
142873549a69SMarco Felsch
tvp5150_runtime_resume(struct device * dev)142973549a69SMarco Felsch static int __maybe_unused tvp5150_runtime_resume(struct device *dev)
143073549a69SMarco Felsch {
14311c891423SKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev);
143273549a69SMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
143373549a69SMarco Felsch
143473549a69SMarco Felsch if (decoder->irq)
143573549a69SMarco Felsch /* Enable lock interrupt */
143673549a69SMarco Felsch return regmap_update_bits(decoder->regmap,
143773549a69SMarco Felsch TVP5150_INT_ENABLE_REG_A,
143873549a69SMarco Felsch TVP5150_INT_A_LOCK,
143973549a69SMarco Felsch TVP5150_INT_A_LOCK);
144073549a69SMarco Felsch return 0;
144173549a69SMarco Felsch }
1442cb7a01acSMauro Carvalho Chehab
tvp5150_s_stream(struct v4l2_subdev * sd,int enable)1443460b6c08SLaurent Pinchart static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
1444460b6c08SLaurent Pinchart {
1445a2e5f1b3SJavier Martinez Canillas struct tvp5150 *decoder = to_tvp5150(sd);
144673549a69SMarco Felsch unsigned int mask, val = 0;
144773549a69SMarco Felsch int ret;
1448a2e5f1b3SJavier Martinez Canillas
14498a7441baSMarco Felsch mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE |
14508a7441baSMarco Felsch TVP5150_MISC_CTL_CLOCK_OE;
145179d6205aSLaurent Pinchart
145279d6205aSLaurent Pinchart if (enable) {
145381f2fe55SMauro Carvalho Chehab ret = pm_runtime_resume_and_get(sd->dev);
145481f2fe55SMauro Carvalho Chehab if (ret < 0)
145573549a69SMarco Felsch return ret;
145673549a69SMarco Felsch
14571bb086bcSPhilipp Zabel tvp5150_enable(sd);
14581bb086bcSPhilipp Zabel
145962a764e1SPhilipp Zabel /* Enable outputs if decoder is locked */
14605bd1d91dSMauro Carvalho Chehab if (decoder->irq)
146162a764e1SPhilipp Zabel val = decoder->lock ? decoder->oe : 0;
14625bd1d91dSMauro Carvalho Chehab else
14635bd1d91dSMauro Carvalho Chehab val = decoder->oe;
146473549a69SMarco Felsch
14652f0a5c65SPhilipp Zabel v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt);
146673549a69SMarco Felsch } else {
146773549a69SMarco Felsch pm_runtime_put(sd->dev);
146879d6205aSLaurent Pinchart }
1469a2e5f1b3SJavier Martinez Canillas
14708a7441baSMarco Felsch regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val);
1471460b6c08SLaurent Pinchart
1472460b6c08SLaurent Pinchart return 0;
1473460b6c08SLaurent Pinchart }
1474460b6c08SLaurent Pinchart
tvp5150_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)1475cb7a01acSMauro Carvalho Chehab static int tvp5150_s_routing(struct v4l2_subdev *sd,
1476cb7a01acSMauro Carvalho Chehab u32 input, u32 output, u32 config)
1477cb7a01acSMauro Carvalho Chehab {
1478cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
1479cb7a01acSMauro Carvalho Chehab
1480cb7a01acSMauro Carvalho Chehab decoder->input = input;
1481cb7a01acSMauro Carvalho Chehab decoder->output = output;
1482c43875f6SMauro Carvalho Chehab
1483c43875f6SMauro Carvalho Chehab if (output == TVP5150_BLACK_SCREEN)
1484c43875f6SMauro Carvalho Chehab decoder->enable = false;
1485c43875f6SMauro Carvalho Chehab else
1486c43875f6SMauro Carvalho Chehab decoder->enable = true;
1487c43875f6SMauro Carvalho Chehab
1488cb7a01acSMauro Carvalho Chehab tvp5150_selmux(sd);
1489cb7a01acSMauro Carvalho Chehab return 0;
1490cb7a01acSMauro Carvalho Chehab }
1491cb7a01acSMauro Carvalho Chehab
tvp5150_s_raw_fmt(struct v4l2_subdev * sd,struct v4l2_vbi_format * fmt)1492cb7a01acSMauro Carvalho Chehab static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
1493cb7a01acSMauro Carvalho Chehab {
149428b9e227SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
149528b9e227SPhilipp Zabel
14969bfd8f88SNasser Afshin /*
14979bfd8f88SNasser Afshin * this is for capturing 36 raw vbi lines
14989bfd8f88SNasser Afshin * if there's a way to cut off the beginning 2 vbi lines
14999bfd8f88SNasser Afshin * with the tvp5150 then the vbi line count could be lowered
15009bfd8f88SNasser Afshin * to 17 lines/field again, although I couldn't find a register
15019bfd8f88SNasser Afshin * which could do that cropping
15029bfd8f88SNasser Afshin */
15039bfd8f88SNasser Afshin
1504cb7a01acSMauro Carvalho Chehab if (fmt->sample_format == V4L2_PIX_FMT_GREY)
150528b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_LUMA_PROC_CTL_1, 0x70);
1506cb7a01acSMauro Carvalho Chehab if (fmt->count[0] == 18 && fmt->count[1] == 18) {
150728b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START,
150828b9e227SPhilipp Zabel 0x00);
150928b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, 0x01);
1510cb7a01acSMauro Carvalho Chehab }
1511cb7a01acSMauro Carvalho Chehab return 0;
1512cb7a01acSMauro Carvalho Chehab }
1513cb7a01acSMauro Carvalho Chehab
tvp5150_s_sliced_fmt(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_format * svbi)1514cb7a01acSMauro Carvalho Chehab static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1515cb7a01acSMauro Carvalho Chehab {
151628b9e227SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
1517cb7a01acSMauro Carvalho Chehab int i;
1518cb7a01acSMauro Carvalho Chehab
1519cb7a01acSMauro Carvalho Chehab if (svbi->service_set != 0) {
1520cb7a01acSMauro Carvalho Chehab for (i = 0; i <= 23; i++) {
1521cb7a01acSMauro Carvalho Chehab svbi->service_lines[1][i] = 0;
1522cb7a01acSMauro Carvalho Chehab svbi->service_lines[0][i] =
15233dd6b560SMauro Carvalho Chehab tvp5150_set_vbi(sd, svbi->service_lines[0][i],
15243dd6b560SMauro Carvalho Chehab 0xf0, i, 3);
1525cb7a01acSMauro Carvalho Chehab }
1526cb7a01acSMauro Carvalho Chehab /* Enables FIFO */
152728b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 1);
1528cb7a01acSMauro Carvalho Chehab } else {
1529cb7a01acSMauro Carvalho Chehab /* Disables FIFO*/
153028b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 0);
1531cb7a01acSMauro Carvalho Chehab
1532cb7a01acSMauro Carvalho Chehab /* Disable Full Field */
153328b9e227SPhilipp Zabel regmap_write(decoder->regmap, TVP5150_FULL_FIELD_ENA, 0);
1534cb7a01acSMauro Carvalho Chehab
1535cb7a01acSMauro Carvalho Chehab /* Disable Line modes */
1536cb7a01acSMauro Carvalho Chehab for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
153728b9e227SPhilipp Zabel regmap_write(decoder->regmap, i, 0xff);
1538cb7a01acSMauro Carvalho Chehab }
1539cb7a01acSMauro Carvalho Chehab return 0;
1540cb7a01acSMauro Carvalho Chehab }
1541cb7a01acSMauro Carvalho Chehab
tvp5150_g_sliced_fmt(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_format * svbi)1542cb7a01acSMauro Carvalho Chehab static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1543cb7a01acSMauro Carvalho Chehab {
1544cb7a01acSMauro Carvalho Chehab int i, mask = 0;
1545cb7a01acSMauro Carvalho Chehab
154630634e8eSHans Verkuil memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
1547cb7a01acSMauro Carvalho Chehab
1548cb7a01acSMauro Carvalho Chehab for (i = 0; i <= 23; i++) {
1549cb7a01acSMauro Carvalho Chehab svbi->service_lines[0][i] =
15503dd6b560SMauro Carvalho Chehab tvp5150_get_vbi(sd, i);
1551cb7a01acSMauro Carvalho Chehab mask |= svbi->service_lines[0][i];
1552cb7a01acSMauro Carvalho Chehab }
1553cb7a01acSMauro Carvalho Chehab svbi->service_set = mask;
1554cb7a01acSMauro Carvalho Chehab return 0;
1555cb7a01acSMauro Carvalho Chehab }
1556cb7a01acSMauro Carvalho Chehab
1557cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
tvp5150_g_register(struct v4l2_subdev * sd,struct v4l2_dbg_register * reg)1558cb7a01acSMauro Carvalho Chehab static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1559cb7a01acSMauro Carvalho Chehab {
1560cb7a01acSMauro Carvalho Chehab int res;
1561cb7a01acSMauro Carvalho Chehab
1562cb7a01acSMauro Carvalho Chehab res = tvp5150_read(sd, reg->reg & 0xff);
1563cb7a01acSMauro Carvalho Chehab if (res < 0) {
1564257e29f8SMauro Carvalho Chehab dev_err(sd->dev, "%s: failed with error = %d\n", __func__, res);
1565cb7a01acSMauro Carvalho Chehab return res;
1566cb7a01acSMauro Carvalho Chehab }
1567cb7a01acSMauro Carvalho Chehab
1568cb7a01acSMauro Carvalho Chehab reg->val = res;
1569cb7a01acSMauro Carvalho Chehab reg->size = 1;
1570cb7a01acSMauro Carvalho Chehab return 0;
1571cb7a01acSMauro Carvalho Chehab }
1572cb7a01acSMauro Carvalho Chehab
tvp5150_s_register(struct v4l2_subdev * sd,const struct v4l2_dbg_register * reg)1573977ba3b1SHans Verkuil static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
1574cb7a01acSMauro Carvalho Chehab {
157528b9e227SPhilipp Zabel struct tvp5150 *decoder = to_tvp5150(sd);
157628b9e227SPhilipp Zabel
157728b9e227SPhilipp Zabel return regmap_write(decoder->regmap, reg->reg & 0xff, reg->val & 0xff);
1578cb7a01acSMauro Carvalho Chehab }
1579cb7a01acSMauro Carvalho Chehab #endif
1580cb7a01acSMauro Carvalho Chehab
tvp5150_subscribe_event(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)1581e953c103SMarco Felsch static int tvp5150_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
1582e953c103SMarco Felsch struct v4l2_event_subscription *sub)
1583e953c103SMarco Felsch {
1584e953c103SMarco Felsch switch (sub->type) {
1585e953c103SMarco Felsch case V4L2_EVENT_SOURCE_CHANGE:
1586e953c103SMarco Felsch return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
1587e953c103SMarco Felsch case V4L2_EVENT_CTRL:
1588e953c103SMarco Felsch return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
1589e953c103SMarco Felsch default:
1590e953c103SMarco Felsch return -EINVAL;
1591e953c103SMarco Felsch }
1592e953c103SMarco Felsch }
1593e953c103SMarco Felsch
tvp5150_g_tuner(struct v4l2_subdev * sd,struct v4l2_tuner * vt)1594cb7a01acSMauro Carvalho Chehab static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
1595cb7a01acSMauro Carvalho Chehab {
1596cb7a01acSMauro Carvalho Chehab int status = tvp5150_read(sd, 0x88);
1597cb7a01acSMauro Carvalho Chehab
1598cb7a01acSMauro Carvalho Chehab vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
1599cb7a01acSMauro Carvalho Chehab return 0;
1600cb7a01acSMauro Carvalho Chehab }
1601cb7a01acSMauro Carvalho Chehab
tvp5150_registered(struct v4l2_subdev * sd)16020556f1d5SMarco Felsch static int tvp5150_registered(struct v4l2_subdev *sd)
16030556f1d5SMarco Felsch {
16040556f1d5SMarco Felsch #if defined(CONFIG_MEDIA_CONTROLLER)
16050556f1d5SMarco Felsch struct tvp5150 *decoder = to_tvp5150(sd);
16060556f1d5SMarco Felsch unsigned int i;
16070556f1d5SMarco Felsch int ret;
16080556f1d5SMarco Felsch
16090556f1d5SMarco Felsch /*
16100556f1d5SMarco Felsch * Setup connector pads and links. Enable the link to the first
16110556f1d5SMarco Felsch * available connector per default.
16120556f1d5SMarco Felsch */
16130556f1d5SMarco Felsch for (i = 0; i < decoder->connectors_num; i++) {
16140556f1d5SMarco Felsch struct media_entity *con = &decoder->connectors[i].ent;
16150556f1d5SMarco Felsch struct media_pad *pad = &decoder->connectors[i].pad;
16160556f1d5SMarco Felsch struct v4l2_fwnode_connector *v4l2c =
16170556f1d5SMarco Felsch &decoder->connectors[i].base;
16180556f1d5SMarco Felsch struct v4l2_connector_link *link =
16190556f1d5SMarco Felsch v4l2_connector_first_link(v4l2c);
16200556f1d5SMarco Felsch unsigned int port = link->fwnode_link.remote_port;
16210556f1d5SMarco Felsch unsigned int flags = i ? 0 : MEDIA_LNK_FL_ENABLED;
16220556f1d5SMarco Felsch bool is_svideo = v4l2c->type == V4L2_CONN_SVIDEO;
16230556f1d5SMarco Felsch
16240556f1d5SMarco Felsch pad->flags = MEDIA_PAD_FL_SOURCE;
16250556f1d5SMarco Felsch ret = media_entity_pads_init(con, 1, pad);
16260556f1d5SMarco Felsch if (ret < 0)
16270556f1d5SMarco Felsch goto err;
16280556f1d5SMarco Felsch
16290556f1d5SMarco Felsch ret = media_device_register_entity(sd->v4l2_dev->mdev, con);
16300556f1d5SMarco Felsch if (ret < 0)
16310556f1d5SMarco Felsch goto err;
16320556f1d5SMarco Felsch
16330556f1d5SMarco Felsch ret = media_create_pad_link(con, 0, &sd->entity, port, flags);
16340556f1d5SMarco Felsch if (ret < 0)
16350556f1d5SMarco Felsch goto err;
16360556f1d5SMarco Felsch
16370556f1d5SMarco Felsch if (is_svideo) {
16380556f1d5SMarco Felsch /*
16390556f1d5SMarco Felsch * Check tvp5150_link_setup() comments for more
16400556f1d5SMarco Felsch * information.
16410556f1d5SMarco Felsch */
16420556f1d5SMarco Felsch link = v4l2_connector_last_link(v4l2c);
16430556f1d5SMarco Felsch port = link->fwnode_link.remote_port;
16440556f1d5SMarco Felsch ret = media_create_pad_link(con, 0, &sd->entity, port,
16450556f1d5SMarco Felsch flags);
16460556f1d5SMarco Felsch if (ret < 0)
16470556f1d5SMarco Felsch goto err;
16480556f1d5SMarco Felsch }
16490556f1d5SMarco Felsch
16500556f1d5SMarco Felsch /* Enable default input. */
16510556f1d5SMarco Felsch if (flags == MEDIA_LNK_FL_ENABLED) {
16520556f1d5SMarco Felsch decoder->input =
16530556f1d5SMarco Felsch is_svideo ? TVP5150_SVIDEO :
16540556f1d5SMarco Felsch port == 0 ? TVP5150_COMPOSITE0 :
16550556f1d5SMarco Felsch TVP5150_COMPOSITE1;
16560556f1d5SMarco Felsch
16570556f1d5SMarco Felsch tvp5150_selmux(sd);
1658baf17821SMarco Felsch decoder->cur_connector = &decoder->connectors[i];
1659baf17821SMarco Felsch tvp5150_s_std(sd, v4l2c->connector.analog.sdtv_stds);
16600556f1d5SMarco Felsch }
16610556f1d5SMarco Felsch }
16620556f1d5SMarco Felsch
16630556f1d5SMarco Felsch return 0;
16640556f1d5SMarco Felsch
16650556f1d5SMarco Felsch err:
1666d000e9b5SChuhong Yuan for (i = 0; i < decoder->connectors_num; i++) {
16670556f1d5SMarco Felsch media_device_unregister_entity(&decoder->connectors[i].ent);
1668d000e9b5SChuhong Yuan media_entity_cleanup(&decoder->connectors[i].ent);
1669d000e9b5SChuhong Yuan }
16700556f1d5SMarco Felsch return ret;
16710556f1d5SMarco Felsch #endif
16720556f1d5SMarco Felsch
16730556f1d5SMarco Felsch return 0;
16740556f1d5SMarco Felsch }
16750556f1d5SMarco Felsch
tvp5150_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)16769c8e5098SMarco Felsch static int tvp5150_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
16779c8e5098SMarco Felsch {
167881f2fe55SMauro Carvalho Chehab return pm_runtime_resume_and_get(sd->dev);
16799c8e5098SMarco Felsch }
16809c8e5098SMarco Felsch
tvp5150_close(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)16819c8e5098SMarco Felsch static int tvp5150_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
16829c8e5098SMarco Felsch {
16839c8e5098SMarco Felsch pm_runtime_put(sd->dev);
16849c8e5098SMarco Felsch
16859c8e5098SMarco Felsch return 0;
16869c8e5098SMarco Felsch }
16879c8e5098SMarco Felsch
1688cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1689cb7a01acSMauro Carvalho Chehab
1690cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
1691cb7a01acSMauro Carvalho Chehab .s_ctrl = tvp5150_s_ctrl,
1692cb7a01acSMauro Carvalho Chehab };
1693cb7a01acSMauro Carvalho Chehab
1694cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
1695cb7a01acSMauro Carvalho Chehab .log_status = tvp5150_log_status,
1696cb7a01acSMauro Carvalho Chehab .reset = tvp5150_reset,
1697cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1698cb7a01acSMauro Carvalho Chehab .g_register = tvp5150_g_register,
1699cb7a01acSMauro Carvalho Chehab .s_register = tvp5150_s_register,
1700cb7a01acSMauro Carvalho Chehab #endif
1701e953c103SMarco Felsch .subscribe_event = tvp5150_subscribe_event,
1702e953c103SMarco Felsch .unsubscribe_event = v4l2_event_subdev_unsubscribe,
1703cb7a01acSMauro Carvalho Chehab };
1704cb7a01acSMauro Carvalho Chehab
1705cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
1706cb7a01acSMauro Carvalho Chehab .g_tuner = tvp5150_g_tuner,
1707cb7a01acSMauro Carvalho Chehab };
1708cb7a01acSMauro Carvalho Chehab
1709cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
17108774bed9SLaurent Pinchart .s_std = tvp5150_s_std,
17110db87cc7SMarco Felsch .g_std = tvp5150_g_std,
1712ee9a6ff6SPhilipp Zabel .querystd = tvp5150_querystd,
1713460b6c08SLaurent Pinchart .s_stream = tvp5150_s_stream,
1714cb7a01acSMauro Carvalho Chehab .s_routing = tvp5150_s_routing,
1715cb7a01acSMauro Carvalho Chehab };
1716cb7a01acSMauro Carvalho Chehab
1717cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
1718cb7a01acSMauro Carvalho Chehab .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
1719cb7a01acSMauro Carvalho Chehab .g_sliced_fmt = tvp5150_g_sliced_fmt,
1720cb7a01acSMauro Carvalho Chehab .s_sliced_fmt = tvp5150_s_sliced_fmt,
1721cb7a01acSMauro Carvalho Chehab .s_raw_fmt = tvp5150_s_raw_fmt,
1722cb7a01acSMauro Carvalho Chehab };
1723cb7a01acSMauro Carvalho Chehab
1724ebcff5fcSHans Verkuil static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
1725b440b733SPhilipp Zabel .init_cfg = tvp5150_init_cfg,
1726ebcff5fcSHans Verkuil .enum_mbus_code = tvp5150_enum_mbus_code,
1727e545ac87SLaurent Pinchart .enum_frame_size = tvp5150_enum_frame_size,
1728da298c6dSHans Verkuil .set_fmt = tvp5150_fill_fmt,
1729da298c6dSHans Verkuil .get_fmt = tvp5150_fill_fmt,
173010d5509cSHans Verkuil .get_selection = tvp5150_get_selection,
173110d5509cSHans Verkuil .set_selection = tvp5150_set_selection,
17320c3da525SJacopo Mondi .get_mbus_config = tvp5150_get_mbus_config,
1733ebcff5fcSHans Verkuil };
1734ebcff5fcSHans Verkuil
1735cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops tvp5150_ops = {
1736cb7a01acSMauro Carvalho Chehab .core = &tvp5150_core_ops,
1737cb7a01acSMauro Carvalho Chehab .tuner = &tvp5150_tuner_ops,
1738cb7a01acSMauro Carvalho Chehab .video = &tvp5150_video_ops,
1739cb7a01acSMauro Carvalho Chehab .vbi = &tvp5150_vbi_ops,
1740ebcff5fcSHans Verkuil .pad = &tvp5150_pad_ops,
1741cb7a01acSMauro Carvalho Chehab };
1742cb7a01acSMauro Carvalho Chehab
17430556f1d5SMarco Felsch static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
17440556f1d5SMarco Felsch .registered = tvp5150_registered,
17459c8e5098SMarco Felsch .open = tvp5150_open,
17469c8e5098SMarco Felsch .close = tvp5150_close,
17470556f1d5SMarco Felsch };
17480556f1d5SMarco Felsch
1749cb7a01acSMauro Carvalho Chehab /****************************************************************************
1750cb7a01acSMauro Carvalho Chehab I2C Client & Driver
1751cb7a01acSMauro Carvalho Chehab ****************************************************************************/
1752cb7a01acSMauro Carvalho Chehab
175328b9e227SPhilipp Zabel static const struct regmap_range tvp5150_readable_ranges[] = {
175428b9e227SPhilipp Zabel {
175528b9e227SPhilipp Zabel .range_min = TVP5150_VD_IN_SRC_SEL_1,
175628b9e227SPhilipp Zabel .range_max = TVP5150_AUTOSW_MSK,
175728b9e227SPhilipp Zabel }, {
175828b9e227SPhilipp Zabel .range_min = TVP5150_COLOR_KIL_THSH_CTL,
175928b9e227SPhilipp Zabel .range_max = TVP5150_CONF_SHARED_PIN,
176028b9e227SPhilipp Zabel }, {
176128b9e227SPhilipp Zabel .range_min = TVP5150_ACT_VD_CROP_ST_MSB,
176228b9e227SPhilipp Zabel .range_max = TVP5150_HORIZ_SYNC_START,
176328b9e227SPhilipp Zabel }, {
176428b9e227SPhilipp Zabel .range_min = TVP5150_VERT_BLANKING_START,
176528b9e227SPhilipp Zabel .range_max = TVP5150_INTT_CONFIG_REG_B,
176628b9e227SPhilipp Zabel }, {
176728b9e227SPhilipp Zabel .range_min = TVP5150_VIDEO_STD,
176828b9e227SPhilipp Zabel .range_max = TVP5150_VIDEO_STD,
176928b9e227SPhilipp Zabel }, {
177028b9e227SPhilipp Zabel .range_min = TVP5150_CB_GAIN_FACT,
177128b9e227SPhilipp Zabel .range_max = TVP5150_REV_SELECT,
177228b9e227SPhilipp Zabel }, {
177328b9e227SPhilipp Zabel .range_min = TVP5150_MSB_DEV_ID,
177428b9e227SPhilipp Zabel .range_max = TVP5150_STATUS_REG_5,
177528b9e227SPhilipp Zabel }, {
177628b9e227SPhilipp Zabel .range_min = TVP5150_CC_DATA_INI,
177728b9e227SPhilipp Zabel .range_max = TVP5150_TELETEXT_FIL_ENA,
177828b9e227SPhilipp Zabel }, {
177928b9e227SPhilipp Zabel .range_min = TVP5150_INT_STATUS_REG_A,
178028b9e227SPhilipp Zabel .range_max = TVP5150_FIFO_OUT_CTRL,
178128b9e227SPhilipp Zabel }, {
178228b9e227SPhilipp Zabel .range_min = TVP5150_FULL_FIELD_ENA,
178328b9e227SPhilipp Zabel .range_max = TVP5150_FULL_FIELD_MODE_REG,
178428b9e227SPhilipp Zabel },
178528b9e227SPhilipp Zabel };
178628b9e227SPhilipp Zabel
tvp5150_volatile_reg(struct device * dev,unsigned int reg)1787c135e997SMauro Carvalho Chehab static bool tvp5150_volatile_reg(struct device *dev, unsigned int reg)
178828b9e227SPhilipp Zabel {
178928b9e227SPhilipp Zabel switch (reg) {
179028b9e227SPhilipp Zabel case TVP5150_VERT_LN_COUNT_MSB:
179128b9e227SPhilipp Zabel case TVP5150_VERT_LN_COUNT_LSB:
179228b9e227SPhilipp Zabel case TVP5150_INT_STATUS_REG_A:
179328b9e227SPhilipp Zabel case TVP5150_INT_STATUS_REG_B:
179428b9e227SPhilipp Zabel case TVP5150_INT_ACTIVE_REG_B:
179528b9e227SPhilipp Zabel case TVP5150_STATUS_REG_1:
179628b9e227SPhilipp Zabel case TVP5150_STATUS_REG_2:
179728b9e227SPhilipp Zabel case TVP5150_STATUS_REG_3:
179828b9e227SPhilipp Zabel case TVP5150_STATUS_REG_4:
179928b9e227SPhilipp Zabel case TVP5150_STATUS_REG_5:
180028b9e227SPhilipp Zabel /* CC, WSS, VPS, VITC data? */
180128b9e227SPhilipp Zabel case TVP5150_VBI_FIFO_READ_DATA:
180228b9e227SPhilipp Zabel case TVP5150_VDP_STATUS_REG:
180328b9e227SPhilipp Zabel case TVP5150_FIFO_WORD_COUNT:
180428b9e227SPhilipp Zabel return true;
180528b9e227SPhilipp Zabel default:
180628b9e227SPhilipp Zabel return false;
180728b9e227SPhilipp Zabel }
180828b9e227SPhilipp Zabel }
180928b9e227SPhilipp Zabel
181028b9e227SPhilipp Zabel static const struct regmap_access_table tvp5150_readable_table = {
181128b9e227SPhilipp Zabel .yes_ranges = tvp5150_readable_ranges,
181228b9e227SPhilipp Zabel .n_yes_ranges = ARRAY_SIZE(tvp5150_readable_ranges),
181328b9e227SPhilipp Zabel };
181428b9e227SPhilipp Zabel
181528b9e227SPhilipp Zabel static struct regmap_config tvp5150_config = {
181628b9e227SPhilipp Zabel .reg_bits = 8,
181728b9e227SPhilipp Zabel .val_bits = 8,
181828b9e227SPhilipp Zabel .max_register = 0xff,
181928b9e227SPhilipp Zabel
182028b9e227SPhilipp Zabel .cache_type = REGCACHE_RBTREE,
182128b9e227SPhilipp Zabel
182228b9e227SPhilipp Zabel .rd_table = &tvp5150_readable_table,
182328b9e227SPhilipp Zabel .volatile_reg = tvp5150_volatile_reg,
182428b9e227SPhilipp Zabel };
182528b9e227SPhilipp Zabel
tvp5150_detect_version(struct tvp5150 * core)18267871597aSLaurent Pinchart static int tvp5150_detect_version(struct tvp5150 *core)
18277871597aSLaurent Pinchart {
18287871597aSLaurent Pinchart struct v4l2_subdev *sd = &core->sd;
18297871597aSLaurent Pinchart struct i2c_client *c = v4l2_get_subdevdata(sd);
18307871597aSLaurent Pinchart u8 regs[4];
18317871597aSLaurent Pinchart int res;
18327871597aSLaurent Pinchart
18337871597aSLaurent Pinchart /*
18347871597aSLaurent Pinchart * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
18357871597aSLaurent Pinchart * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
18367871597aSLaurent Pinchart */
183728b9e227SPhilipp Zabel res = regmap_bulk_read(core->regmap, TVP5150_MSB_DEV_ID, regs, 4);
183828b9e227SPhilipp Zabel if (res < 0) {
183928b9e227SPhilipp Zabel dev_err(&c->dev, "reading ID registers failed: %d\n", res);
18407871597aSLaurent Pinchart return res;
18417871597aSLaurent Pinchart }
18427871597aSLaurent Pinchart
184382275133SJavier Martinez Canillas core->dev_id = (regs[0] << 8) | regs[1];
184482275133SJavier Martinez Canillas core->rom_ver = (regs[2] << 8) | regs[3];
18457871597aSLaurent Pinchart
1846257e29f8SMauro Carvalho Chehab dev_info(sd->dev, "tvp%04x (%u.%u) chip found @ 0x%02x (%s)\n",
184782275133SJavier Martinez Canillas core->dev_id, regs[2], regs[3], c->addr << 1,
184882275133SJavier Martinez Canillas c->adapter->name);
18497871597aSLaurent Pinchart
185082275133SJavier Martinez Canillas if (core->dev_id == 0x5150 && core->rom_ver == 0x0321) {
1851257e29f8SMauro Carvalho Chehab dev_info(sd->dev, "tvp5150a detected.\n");
185282275133SJavier Martinez Canillas } else if (core->dev_id == 0x5150 && core->rom_ver == 0x0400) {
1853257e29f8SMauro Carvalho Chehab dev_info(sd->dev, "tvp5150am1 detected.\n");
18547871597aSLaurent Pinchart
18557871597aSLaurent Pinchart /* ITU-T BT.656.4 timing */
185628b9e227SPhilipp Zabel regmap_write(core->regmap, TVP5150_REV_SELECT, 0);
185782275133SJavier Martinez Canillas } else if (core->dev_id == 0x5151 && core->rom_ver == 0x0100) {
1858257e29f8SMauro Carvalho Chehab dev_info(sd->dev, "tvp5151 detected.\n");
18597871597aSLaurent Pinchart } else {
1860257e29f8SMauro Carvalho Chehab dev_info(sd->dev, "*** unknown tvp%04x chip detected.\n",
186182275133SJavier Martinez Canillas core->dev_id);
18627871597aSLaurent Pinchart }
18637871597aSLaurent Pinchart
18647871597aSLaurent Pinchart return 0;
18657871597aSLaurent Pinchart }
18667871597aSLaurent Pinchart
tvp5150_init(struct i2c_client * c)186709aa2609SJavier Martinez Canillas static int tvp5150_init(struct i2c_client *c)
186809aa2609SJavier Martinez Canillas {
186909aa2609SJavier Martinez Canillas struct gpio_desc *pdn_gpio;
187009aa2609SJavier Martinez Canillas struct gpio_desc *reset_gpio;
187109aa2609SJavier Martinez Canillas
187209aa2609SJavier Martinez Canillas pdn_gpio = devm_gpiod_get_optional(&c->dev, "pdn", GPIOD_OUT_HIGH);
187309aa2609SJavier Martinez Canillas if (IS_ERR(pdn_gpio))
187409aa2609SJavier Martinez Canillas return PTR_ERR(pdn_gpio);
187509aa2609SJavier Martinez Canillas
187609aa2609SJavier Martinez Canillas if (pdn_gpio) {
187709aa2609SJavier Martinez Canillas gpiod_set_value_cansleep(pdn_gpio, 0);
187809aa2609SJavier Martinez Canillas /* Delay time between power supplies active and reset */
187909aa2609SJavier Martinez Canillas msleep(20);
188009aa2609SJavier Martinez Canillas }
188109aa2609SJavier Martinez Canillas
188209aa2609SJavier Martinez Canillas reset_gpio = devm_gpiod_get_optional(&c->dev, "reset", GPIOD_OUT_HIGH);
188309aa2609SJavier Martinez Canillas if (IS_ERR(reset_gpio))
188409aa2609SJavier Martinez Canillas return PTR_ERR(reset_gpio);
188509aa2609SJavier Martinez Canillas
188609aa2609SJavier Martinez Canillas if (reset_gpio) {
188709aa2609SJavier Martinez Canillas /* RESETB pulse duration */
188809aa2609SJavier Martinez Canillas ndelay(500);
188909aa2609SJavier Martinez Canillas gpiod_set_value_cansleep(reset_gpio, 0);
189009aa2609SJavier Martinez Canillas /* Delay time between end of reset to I2C active */
189109aa2609SJavier Martinez Canillas usleep_range(200, 250);
189209aa2609SJavier Martinez Canillas }
189309aa2609SJavier Martinez Canillas
189409aa2609SJavier Martinez Canillas return 0;
189509aa2609SJavier Martinez Canillas }
189609aa2609SJavier Martinez Canillas
18970556f1d5SMarco Felsch #if defined(CONFIG_MEDIA_CONTROLLER)
tvp5150_mc_init(struct tvp5150 * decoder)18980556f1d5SMarco Felsch static int tvp5150_mc_init(struct tvp5150 *decoder)
18990556f1d5SMarco Felsch {
19000556f1d5SMarco Felsch struct v4l2_subdev *sd = &decoder->sd;
19010556f1d5SMarco Felsch unsigned int i;
19020556f1d5SMarco Felsch
19030556f1d5SMarco Felsch sd->entity.ops = &tvp5150_sd_media_ops;
19040556f1d5SMarco Felsch sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
19050556f1d5SMarco Felsch
19060556f1d5SMarco Felsch for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
19070556f1d5SMarco Felsch decoder->pads[i].flags = MEDIA_PAD_FL_SINK;
19080556f1d5SMarco Felsch decoder->pads[i].sig_type = PAD_SIGNAL_ANALOG;
19090556f1d5SMarco Felsch }
19100556f1d5SMarco Felsch
19110556f1d5SMarco Felsch decoder->pads[i].flags = MEDIA_PAD_FL_SOURCE;
19120556f1d5SMarco Felsch decoder->pads[i].sig_type = PAD_SIGNAL_DV;
19130556f1d5SMarco Felsch
19140556f1d5SMarco Felsch return media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS,
19150556f1d5SMarco Felsch decoder->pads);
19160556f1d5SMarco Felsch }
19170556f1d5SMarco Felsch
19180556f1d5SMarco Felsch #else /* !defined(CONFIG_MEDIA_CONTROLLER) */
19190556f1d5SMarco Felsch
tvp5150_mc_init(struct tvp5150 * decoder)19200556f1d5SMarco Felsch static inline int tvp5150_mc_init(struct tvp5150 *decoder)
19210556f1d5SMarco Felsch {
19220556f1d5SMarco Felsch return 0;
19230556f1d5SMarco Felsch }
19240556f1d5SMarco Felsch #endif /* defined(CONFIG_MEDIA_CONTROLLER) */
19250556f1d5SMarco Felsch
tvp5150_validate_connectors(struct tvp5150 * decoder)19260556f1d5SMarco Felsch static int tvp5150_validate_connectors(struct tvp5150 *decoder)
19270556f1d5SMarco Felsch {
19280556f1d5SMarco Felsch struct device *dev = decoder->sd.dev;
19290556f1d5SMarco Felsch struct tvp5150_connector *tvpc;
19300556f1d5SMarco Felsch struct v4l2_fwnode_connector *v4l2c;
19310556f1d5SMarco Felsch unsigned int i;
19320556f1d5SMarco Felsch
19330556f1d5SMarco Felsch if (!decoder->connectors_num) {
19340556f1d5SMarco Felsch dev_err(dev, "No valid connector found\n");
19350556f1d5SMarco Felsch return -ENODEV;
19360556f1d5SMarco Felsch }
19370556f1d5SMarco Felsch
19380556f1d5SMarco Felsch for (i = 0; i < decoder->connectors_num; i++) {
19390556f1d5SMarco Felsch struct v4l2_connector_link *link0 = NULL;
19400556f1d5SMarco Felsch struct v4l2_connector_link *link1;
19410556f1d5SMarco Felsch
19420556f1d5SMarco Felsch tvpc = &decoder->connectors[i];
19430556f1d5SMarco Felsch v4l2c = &tvpc->base;
19440556f1d5SMarco Felsch
19450556f1d5SMarco Felsch if (v4l2c->type == V4L2_CONN_COMPOSITE) {
19460556f1d5SMarco Felsch if (v4l2c->nr_of_links != 1) {
19470556f1d5SMarco Felsch dev_err(dev, "Composite: connector needs 1 link\n");
19480556f1d5SMarco Felsch return -EINVAL;
19490556f1d5SMarco Felsch }
19500556f1d5SMarco Felsch link0 = v4l2_connector_first_link(v4l2c);
19510556f1d5SMarco Felsch if (!link0) {
19520556f1d5SMarco Felsch dev_err(dev, "Composite: invalid first link\n");
19530556f1d5SMarco Felsch return -EINVAL;
19540556f1d5SMarco Felsch }
19550556f1d5SMarco Felsch if (link0->fwnode_link.remote_id == 1) {
19560556f1d5SMarco Felsch dev_err(dev, "Composite: invalid endpoint id\n");
19570556f1d5SMarco Felsch return -EINVAL;
19580556f1d5SMarco Felsch }
19590556f1d5SMarco Felsch }
19600556f1d5SMarco Felsch
19610556f1d5SMarco Felsch if (v4l2c->type == V4L2_CONN_SVIDEO) {
19620556f1d5SMarco Felsch if (v4l2c->nr_of_links != 2) {
19630556f1d5SMarco Felsch dev_err(dev, "SVideo: connector needs 2 links\n");
19640556f1d5SMarco Felsch return -EINVAL;
19650556f1d5SMarco Felsch }
19660556f1d5SMarco Felsch link0 = v4l2_connector_first_link(v4l2c);
19670556f1d5SMarco Felsch if (!link0) {
19680556f1d5SMarco Felsch dev_err(dev, "SVideo: invalid first link\n");
19690556f1d5SMarco Felsch return -EINVAL;
19700556f1d5SMarco Felsch }
19710556f1d5SMarco Felsch link1 = v4l2_connector_last_link(v4l2c);
19720556f1d5SMarco Felsch if (link0->fwnode_link.remote_port ==
19730556f1d5SMarco Felsch link1->fwnode_link.remote_port) {
19740556f1d5SMarco Felsch dev_err(dev, "SVideo: invalid link setup\n");
19750556f1d5SMarco Felsch return -EINVAL;
19760556f1d5SMarco Felsch }
19770556f1d5SMarco Felsch }
1978baf17821SMarco Felsch
1979baf17821SMarco Felsch if (!(v4l2c->connector.analog.sdtv_stds & TVP5150_STD_MASK)) {
1980baf17821SMarco Felsch dev_err(dev, "Unsupported tv-norm on connector %s\n",
1981baf17821SMarco Felsch v4l2c->name);
1982baf17821SMarco Felsch return -EINVAL;
1983baf17821SMarco Felsch }
19840556f1d5SMarco Felsch }
19850556f1d5SMarco Felsch
19860556f1d5SMarco Felsch return 0;
19870556f1d5SMarco Felsch }
19880556f1d5SMarco Felsch
tvp5150_parse_dt(struct tvp5150 * decoder,struct device_node * np)1989a2e5f1b3SJavier Martinez Canillas static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
1990a2e5f1b3SJavier Martinez Canillas {
19910556f1d5SMarco Felsch struct device *dev = decoder->sd.dev;
19920556f1d5SMarco Felsch struct v4l2_fwnode_endpoint bus_cfg = {
19930556f1d5SMarco Felsch .bus_type = V4L2_MBUS_UNKNOWN
19940556f1d5SMarco Felsch };
19950556f1d5SMarco Felsch struct device_node *ep_np;
19960556f1d5SMarco Felsch struct tvp5150_connector *tvpc;
19970556f1d5SMarco Felsch struct v4l2_fwnode_connector *v4l2c;
19980556f1d5SMarco Felsch unsigned int flags, ep_num;
19990556f1d5SMarco Felsch unsigned int i;
20000556f1d5SMarco Felsch int ret;
2001a2e5f1b3SJavier Martinez Canillas
20020556f1d5SMarco Felsch /* At least 1 output and 1 input */
20030556f1d5SMarco Felsch ep_num = of_graph_get_endpoint_count(np);
20040556f1d5SMarco Felsch if (ep_num < 2 || ep_num > 5) {
20050556f1d5SMarco Felsch dev_err(dev, "At least 1 input and 1 output must be connected to the device.\n");
2006a2e5f1b3SJavier Martinez Canillas return -EINVAL;
20070556f1d5SMarco Felsch }
2008a2e5f1b3SJavier Martinez Canillas
20090556f1d5SMarco Felsch /* Layout if all connectors are used:
20100556f1d5SMarco Felsch *
20110556f1d5SMarco Felsch * tvp-5150 port@0 (AIP1A)
20120556f1d5SMarco Felsch * endpoint@0 -----------> Comp0-Con port
20130556f1d5SMarco Felsch * endpoint@1 --------+--> Svideo-Con port
20140556f1d5SMarco Felsch * tvp-5150 port@1 (AIP1B) |
20150556f1d5SMarco Felsch * endpoint@1 --------+
20160556f1d5SMarco Felsch * endpoint@0 -----------> Comp1-Con port
20170556f1d5SMarco Felsch * tvp-5150 port@2
20180556f1d5SMarco Felsch * endpoint (video bitstream output at YOUT[0-7] parallel bus)
20190556f1d5SMarco Felsch */
20200556f1d5SMarco Felsch for_each_endpoint_of_node(np, ep_np) {
20210556f1d5SMarco Felsch struct fwnode_handle *ep_fwnode = of_fwnode_handle(ep_np);
20220556f1d5SMarco Felsch unsigned int next_connector = decoder->connectors_num;
20230556f1d5SMarco Felsch struct of_endpoint ep;
20240556f1d5SMarco Felsch
20250556f1d5SMarco Felsch of_graph_parse_endpoint(ep_np, &ep);
20260556f1d5SMarco Felsch if (ep.port > 1 || ep.id > 1) {
20270556f1d5SMarco Felsch dev_dbg(dev, "Ignore connector on port@%u/ep@%u\n",
20280556f1d5SMarco Felsch ep.port, ep.id);
20290556f1d5SMarco Felsch continue;
20300556f1d5SMarco Felsch }
20310556f1d5SMarco Felsch
20320556f1d5SMarco Felsch tvpc = &decoder->connectors[next_connector];
20330556f1d5SMarco Felsch v4l2c = &tvpc->base;
20340556f1d5SMarco Felsch
20350556f1d5SMarco Felsch if (ep.port == 0 || (ep.port == 1 && ep.id == 0)) {
20360556f1d5SMarco Felsch ret = v4l2_fwnode_connector_parse(ep_fwnode, v4l2c);
2037a2e5f1b3SJavier Martinez Canillas if (ret)
20380556f1d5SMarco Felsch goto err_put;
20390556f1d5SMarco Felsch ret = v4l2_fwnode_connector_add_link(ep_fwnode, v4l2c);
20400556f1d5SMarco Felsch if (ret)
20410556f1d5SMarco Felsch goto err_put;
20420556f1d5SMarco Felsch decoder->connectors_num++;
20430556f1d5SMarco Felsch } else {
20440556f1d5SMarco Felsch /* Adding the 2nd svideo link */
20450556f1d5SMarco Felsch for (i = 0; i < TVP5150_MAX_CONNECTORS; i++) {
20460556f1d5SMarco Felsch tvpc = &decoder->connectors[i];
20470556f1d5SMarco Felsch v4l2c = &tvpc->base;
20480556f1d5SMarco Felsch if (v4l2c->type == V4L2_CONN_SVIDEO)
20490556f1d5SMarco Felsch break;
20500556f1d5SMarco Felsch }
20510556f1d5SMarco Felsch
20520556f1d5SMarco Felsch ret = v4l2_fwnode_connector_add_link(ep_fwnode, v4l2c);
20530556f1d5SMarco Felsch if (ret)
20540556f1d5SMarco Felsch goto err_put;
20550556f1d5SMarco Felsch }
20560556f1d5SMarco Felsch }
20570556f1d5SMarco Felsch
20580556f1d5SMarco Felsch ret = tvp5150_validate_connectors(decoder);
20590556f1d5SMarco Felsch if (ret)
20600556f1d5SMarco Felsch goto err_free;
20610556f1d5SMarco Felsch
20620556f1d5SMarco Felsch for (i = 0; i < decoder->connectors_num; i++) {
20630556f1d5SMarco Felsch tvpc = &decoder->connectors[i];
20640556f1d5SMarco Felsch v4l2c = &tvpc->base;
20650556f1d5SMarco Felsch tvpc->ent.flags = MEDIA_ENT_FL_CONNECTOR;
20660556f1d5SMarco Felsch tvpc->ent.function = v4l2c->type == V4L2_CONN_SVIDEO ?
20670556f1d5SMarco Felsch MEDIA_ENT_F_CONN_SVIDEO : MEDIA_ENT_F_CONN_COMPOSITE;
20680556f1d5SMarco Felsch tvpc->ent.name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
20690556f1d5SMarco Felsch v4l2c->name, v4l2c->label ?
20700556f1d5SMarco Felsch v4l2c->label : "");
2071*26ce7054SClaudiu Beznea if (!tvpc->ent.name) {
2072*26ce7054SClaudiu Beznea ret = -ENOMEM;
2073*26ce7054SClaudiu Beznea goto err_free;
2074*26ce7054SClaudiu Beznea }
20750556f1d5SMarco Felsch }
20760556f1d5SMarco Felsch
20770556f1d5SMarco Felsch ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0);
20780556f1d5SMarco Felsch if (!ep_np) {
2079eb08c481SZhang Xiaoxu ret = -EINVAL;
20800556f1d5SMarco Felsch dev_err(dev, "Error no output endpoint available\n");
20810556f1d5SMarco Felsch goto err_free;
20820556f1d5SMarco Felsch }
20830556f1d5SMarco Felsch ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_np), &bus_cfg);
20840556f1d5SMarco Felsch of_node_put(ep_np);
20850556f1d5SMarco Felsch if (ret)
20860556f1d5SMarco Felsch goto err_free;
2087a2e5f1b3SJavier Martinez Canillas
2088a2e5f1b3SJavier Martinez Canillas flags = bus_cfg.bus.parallel.flags;
2089a2e5f1b3SJavier Martinez Canillas if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL &&
2090a2e5f1b3SJavier Martinez Canillas !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH &&
2091a2e5f1b3SJavier Martinez Canillas flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH &&
20922bd5e437SJavier Martinez Canillas flags & V4L2_MBUS_FIELD_EVEN_LOW)) {
20932bd5e437SJavier Martinez Canillas ret = -EINVAL;
20940556f1d5SMarco Felsch goto err_free;
20952bd5e437SJavier Martinez Canillas }
2096a2e5f1b3SJavier Martinez Canillas
2097a2e5f1b3SJavier Martinez Canillas decoder->mbus_type = bus_cfg.bus_type;
2098a2e5f1b3SJavier Martinez Canillas
20990556f1d5SMarco Felsch return 0;
21000556f1d5SMarco Felsch
21010556f1d5SMarco Felsch err_put:
21020556f1d5SMarco Felsch of_node_put(ep_np);
21030556f1d5SMarco Felsch err_free:
21040556f1d5SMarco Felsch for (i = 0; i < TVP5150_MAX_CONNECTORS; i++)
21050556f1d5SMarco Felsch v4l2_fwnode_connector_free(&decoder->connectors[i].base);
21060556f1d5SMarco Felsch
2107a2e5f1b3SJavier Martinez Canillas return ret;
2108a2e5f1b3SJavier Martinez Canillas }
2109a2e5f1b3SJavier Martinez Canillas
2110c43875f6SMauro Carvalho Chehab static const char * const tvp5150_test_patterns[2] = {
2111c43875f6SMauro Carvalho Chehab "Disabled",
2112c43875f6SMauro Carvalho Chehab "Black screen"
2113c43875f6SMauro Carvalho Chehab };
2114c43875f6SMauro Carvalho Chehab
tvp5150_probe(struct i2c_client * c)2115e6714993SKieran Bingham static int tvp5150_probe(struct i2c_client *c)
2116cb7a01acSMauro Carvalho Chehab {
2117cb7a01acSMauro Carvalho Chehab struct tvp5150 *core;
2118cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd;
2119a2e5f1b3SJavier Martinez Canillas struct device_node *np = c->dev.of_node;
212028b9e227SPhilipp Zabel struct regmap *map;
2121baf17821SMarco Felsch unsigned int i;
21227871597aSLaurent Pinchart int res;
2123cb7a01acSMauro Carvalho Chehab
2124cb7a01acSMauro Carvalho Chehab /* Check if the adapter supports the needed features */
2125cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(c->adapter,
2126cb7a01acSMauro Carvalho Chehab I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
2127cb7a01acSMauro Carvalho Chehab return -EIO;
2128cb7a01acSMauro Carvalho Chehab
212909aa2609SJavier Martinez Canillas res = tvp5150_init(c);
213009aa2609SJavier Martinez Canillas if (res)
213109aa2609SJavier Martinez Canillas return res;
213209aa2609SJavier Martinez Canillas
2133c02b211dSLaurent Pinchart core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL);
2134c02b211dSLaurent Pinchart if (!core)
2135cb7a01acSMauro Carvalho Chehab return -ENOMEM;
2136a2e5f1b3SJavier Martinez Canillas
213728b9e227SPhilipp Zabel map = devm_regmap_init_i2c(c, &tvp5150_config);
213828b9e227SPhilipp Zabel if (IS_ERR(map))
213928b9e227SPhilipp Zabel return PTR_ERR(map);
214028b9e227SPhilipp Zabel
214128b9e227SPhilipp Zabel core->regmap = map;
2142cb7a01acSMauro Carvalho Chehab sd = &core->sd;
214396ca7c41SMichael Tretter v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
214496ca7c41SMichael Tretter sd->internal_ops = &tvp5150_internal_ops;
2145e953c103SMarco Felsch sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
2146a2e5f1b3SJavier Martinez Canillas
2147a2e5f1b3SJavier Martinez Canillas if (IS_ENABLED(CONFIG_OF) && np) {
2148a2e5f1b3SJavier Martinez Canillas res = tvp5150_parse_dt(core, np);
2149a2e5f1b3SJavier Martinez Canillas if (res) {
2150257e29f8SMauro Carvalho Chehab dev_err(sd->dev, "DT parsing error: %d\n", res);
2151a2e5f1b3SJavier Martinez Canillas return res;
2152a2e5f1b3SJavier Martinez Canillas }
2153a2e5f1b3SJavier Martinez Canillas } else {
2154a2e5f1b3SJavier Martinez Canillas /* Default to BT.656 embedded sync */
2155a2e5f1b3SJavier Martinez Canillas core->mbus_type = V4L2_MBUS_BT656;
2156a2e5f1b3SJavier Martinez Canillas }
2157a2e5f1b3SJavier Martinez Canillas
21580556f1d5SMarco Felsch res = tvp5150_mc_init(core);
21590556f1d5SMarco Felsch if (res)
2160e545ac87SLaurent Pinchart return res;
2161f7b4b54eSJavier Martinez Canillas
21627871597aSLaurent Pinchart res = tvp5150_detect_version(core);
2163cb7a01acSMauro Carvalho Chehab if (res < 0)
2164c02b211dSLaurent Pinchart return res;
2165cb7a01acSMauro Carvalho Chehab
2166baf17821SMarco Felsch /*
2167baf17821SMarco Felsch * Iterate over all available connectors in case they are supported and
2168baf17821SMarco Felsch * successfully parsed. Fallback to default autodetect in case they
2169baf17821SMarco Felsch * aren't supported.
2170baf17821SMarco Felsch */
2171baf17821SMarco Felsch for (i = 0; i < core->connectors_num; i++) {
2172baf17821SMarco Felsch struct v4l2_fwnode_connector *v4l2c;
2173baf17821SMarco Felsch
2174baf17821SMarco Felsch v4l2c = &core->connectors[i].base;
2175baf17821SMarco Felsch core->norm |= v4l2c->connector.analog.sdtv_stds;
2176baf17821SMarco Felsch }
2177baf17821SMarco Felsch
2178baf17821SMarco Felsch if (!core->connectors_num)
2179baf17821SMarco Felsch core->norm = V4L2_STD_ALL;
2180baf17821SMarco Felsch
2181b440b733SPhilipp Zabel core->detected_norm = V4L2_STD_UNKNOWN;
2182cb7a01acSMauro Carvalho Chehab core->input = TVP5150_COMPOSITE1;
2183c43875f6SMauro Carvalho Chehab core->enable = true;
2184cb7a01acSMauro Carvalho Chehab
2185b1950b8dSLaurent Pinchart v4l2_ctrl_handler_init(&core->hdl, 5);
2186cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
2187cb7a01acSMauro Carvalho Chehab V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
2188cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
2189cb7a01acSMauro Carvalho Chehab V4L2_CID_CONTRAST, 0, 255, 1, 128);
2190cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
2191cb7a01acSMauro Carvalho Chehab V4L2_CID_SATURATION, 0, 255, 1, 128);
2192cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
2193cb7a01acSMauro Carvalho Chehab V4L2_CID_HUE, -128, 127, 1, 0);
2194b1950b8dSLaurent Pinchart v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
2195b1950b8dSLaurent Pinchart V4L2_CID_PIXEL_RATE, 27000000,
2196b1950b8dSLaurent Pinchart 27000000, 1, 27000000);
2197c43875f6SMauro Carvalho Chehab v4l2_ctrl_new_std_menu_items(&core->hdl, &tvp5150_ctrl_ops,
2198c43875f6SMauro Carvalho Chehab V4L2_CID_TEST_PATTERN,
21995c4c4505SMauro Carvalho Chehab ARRAY_SIZE(tvp5150_test_patterns) - 1,
2200c43875f6SMauro Carvalho Chehab 0, 0, tvp5150_test_patterns);
2201cb7a01acSMauro Carvalho Chehab sd->ctrl_handler = &core->hdl;
2202cb7a01acSMauro Carvalho Chehab if (core->hdl.error) {
2203cb7a01acSMauro Carvalho Chehab res = core->hdl.error;
2204c7d97499SJavier Martinez Canillas goto err;
2205cb7a01acSMauro Carvalho Chehab }
2206cb7a01acSMauro Carvalho Chehab
22075cb82940SMarco Felsch tvp5150_set_default(tvp5150_read_std(sd), &core->rect);
2208cb7a01acSMauro Carvalho Chehab
22098e4c97e0SPhilipp Zabel core->irq = c->irq;
2210aff808e8SLaurent Pinchart tvp5150_reset(sd, 0); /* Calls v4l2_ctrl_handler_setup() */
22118e4c97e0SPhilipp Zabel if (c->irq) {
22128e4c97e0SPhilipp Zabel res = devm_request_threaded_irq(&c->dev, c->irq, NULL,
22138e4c97e0SPhilipp Zabel tvp5150_isr, IRQF_TRIGGER_HIGH |
22148e4c97e0SPhilipp Zabel IRQF_ONESHOT, "tvp5150", core);
22158e4c97e0SPhilipp Zabel if (res)
221681fd5fd4SMarco Felsch goto err;
22178e4c97e0SPhilipp Zabel }
2218aff808e8SLaurent Pinchart
2219c7d97499SJavier Martinez Canillas res = v4l2_async_register_subdev(sd);
2220c7d97499SJavier Martinez Canillas if (res < 0)
2221c7d97499SJavier Martinez Canillas goto err;
2222c7d97499SJavier Martinez Canillas
2223cb7a01acSMauro Carvalho Chehab if (debug > 1)
2224cb7a01acSMauro Carvalho Chehab tvp5150_log_status(sd);
222573549a69SMarco Felsch
222673549a69SMarco Felsch pm_runtime_set_active(&c->dev);
222773549a69SMarco Felsch pm_runtime_enable(&c->dev);
222873549a69SMarco Felsch pm_runtime_idle(&c->dev);
222973549a69SMarco Felsch
2230cb7a01acSMauro Carvalho Chehab return 0;
2231c7d97499SJavier Martinez Canillas
2232c7d97499SJavier Martinez Canillas err:
2233c7d97499SJavier Martinez Canillas v4l2_ctrl_handler_free(&core->hdl);
2234c7d97499SJavier Martinez Canillas return res;
2235cb7a01acSMauro Carvalho Chehab }
2236cb7a01acSMauro Carvalho Chehab
tvp5150_remove(struct i2c_client * c)2237ed5c2f5fSUwe Kleine-König static void tvp5150_remove(struct i2c_client *c)
2238cb7a01acSMauro Carvalho Chehab {
2239cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(c);
2240cb7a01acSMauro Carvalho Chehab struct tvp5150 *decoder = to_tvp5150(sd);
22410556f1d5SMarco Felsch unsigned int i;
2242cb7a01acSMauro Carvalho Chehab
2243257e29f8SMauro Carvalho Chehab dev_dbg_lvl(sd->dev, 1, debug,
2244cb7a01acSMauro Carvalho Chehab "tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
2245cb7a01acSMauro Carvalho Chehab c->addr << 1);
2246cb7a01acSMauro Carvalho Chehab
22470556f1d5SMarco Felsch for (i = 0; i < decoder->connectors_num; i++)
22480556f1d5SMarco Felsch v4l2_fwnode_connector_free(&decoder->connectors[i].base);
2249d000e9b5SChuhong Yuan for (i = 0; i < decoder->connectors_num; i++) {
22500556f1d5SMarco Felsch media_device_unregister_entity(&decoder->connectors[i].ent);
2251d000e9b5SChuhong Yuan media_entity_cleanup(&decoder->connectors[i].ent);
2252d000e9b5SChuhong Yuan }
2253c7d97499SJavier Martinez Canillas v4l2_async_unregister_subdev(sd);
2254cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&decoder->hdl);
225573549a69SMarco Felsch pm_runtime_disable(&c->dev);
225673549a69SMarco Felsch pm_runtime_set_suspended(&c->dev);
2257cb7a01acSMauro Carvalho Chehab }
2258cb7a01acSMauro Carvalho Chehab
2259cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
2260cb7a01acSMauro Carvalho Chehab
226173549a69SMarco Felsch static const struct dev_pm_ops tvp5150_pm_ops = {
226273549a69SMarco Felsch SET_RUNTIME_PM_OPS(tvp5150_runtime_suspend,
226373549a69SMarco Felsch tvp5150_runtime_resume,
226473549a69SMarco Felsch NULL)
226573549a69SMarco Felsch };
226673549a69SMarco Felsch
2267cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id tvp5150_id[] = {
2268cb7a01acSMauro Carvalho Chehab { "tvp5150", 0 },
2269cb7a01acSMauro Carvalho Chehab { }
2270cb7a01acSMauro Carvalho Chehab };
2271cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, tvp5150_id);
2272cb7a01acSMauro Carvalho Chehab
22737ef930a7SEduard Gavin #if IS_ENABLED(CONFIG_OF)
22747ef930a7SEduard Gavin static const struct of_device_id tvp5150_of_match[] = {
22757ef930a7SEduard Gavin { .compatible = "ti,tvp5150", },
22767ef930a7SEduard Gavin { /* sentinel */ },
22777ef930a7SEduard Gavin };
22787ef930a7SEduard Gavin MODULE_DEVICE_TABLE(of, tvp5150_of_match);
22797ef930a7SEduard Gavin #endif
22807ef930a7SEduard Gavin
2281cb7a01acSMauro Carvalho Chehab static struct i2c_driver tvp5150_driver = {
2282cb7a01acSMauro Carvalho Chehab .driver = {
22837ef930a7SEduard Gavin .of_match_table = of_match_ptr(tvp5150_of_match),
2284cb7a01acSMauro Carvalho Chehab .name = "tvp5150",
228573549a69SMarco Felsch .pm = &tvp5150_pm_ops,
2286cb7a01acSMauro Carvalho Chehab },
2287aaeb31c0SUwe Kleine-König .probe = tvp5150_probe,
2288cb7a01acSMauro Carvalho Chehab .remove = tvp5150_remove,
2289cb7a01acSMauro Carvalho Chehab .id_table = tvp5150_id,
2290cb7a01acSMauro Carvalho Chehab };
2291cb7a01acSMauro Carvalho Chehab
2292cb7a01acSMauro Carvalho Chehab module_i2c_driver(tvp5150_driver);
2293