xref: /openbmc/linux/drivers/media/i2c/adv7604.c (revision 7cc7a833)
154450f59SHans Verkuil /*
254450f59SHans Verkuil  * adv7604 - Analog Devices ADV7604 video decoder driver
354450f59SHans Verkuil  *
454450f59SHans Verkuil  * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
554450f59SHans Verkuil  *
654450f59SHans Verkuil  * This program is free software; you may redistribute it and/or modify
754450f59SHans Verkuil  * it under the terms of the GNU General Public License as published by
854450f59SHans Verkuil  * the Free Software Foundation; version 2 of the License.
954450f59SHans Verkuil  *
1054450f59SHans Verkuil  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1154450f59SHans Verkuil  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1254450f59SHans Verkuil  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1354450f59SHans Verkuil  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
1454450f59SHans Verkuil  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1554450f59SHans Verkuil  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1654450f59SHans Verkuil  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1754450f59SHans Verkuil  * SOFTWARE.
1854450f59SHans Verkuil  *
1954450f59SHans Verkuil  */
2054450f59SHans Verkuil 
2154450f59SHans Verkuil /*
2254450f59SHans Verkuil  * References (c = chapter, p = page):
2354450f59SHans Verkuil  * REF_01 - Analog devices, ADV7604, Register Settings Recommendations,
2454450f59SHans Verkuil  *		Revision 2.5, June 2010
2554450f59SHans Verkuil  * REF_02 - Analog devices, Register map documentation, Documentation of
2654450f59SHans Verkuil  *		the register maps, Software manual, Rev. F, June 2010
2754450f59SHans Verkuil  * REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010
2854450f59SHans Verkuil  */
2954450f59SHans Verkuil 
30c72a53ceSLaurent Pinchart #include <linux/delay.h>
31e9d50e9eSLaurent Pinchart #include <linux/gpio/consumer.h>
32516613c1SHans Verkuil #include <linux/hdmi.h>
33c72a53ceSLaurent Pinchart #include <linux/i2c.h>
3454450f59SHans Verkuil #include <linux/kernel.h>
3554450f59SHans Verkuil #include <linux/module.h>
36859969b3SSakari Ailus #include <linux/of_graph.h>
3754450f59SHans Verkuil #include <linux/slab.h>
38c72a53ceSLaurent Pinchart #include <linux/v4l2-dv-timings.h>
3954450f59SHans Verkuil #include <linux/videodev2.h>
4054450f59SHans Verkuil #include <linux/workqueue.h>
41f862f57dSPablo Anton #include <linux/regmap.h>
42c72a53ceSLaurent Pinchart 
43b5dcee22SMauro Carvalho Chehab #include <media/i2c/adv7604.h>
4441a52373SHans Verkuil #include <media/cec.h>
45c72a53ceSLaurent Pinchart #include <media/v4l2-ctrls.h>
46c72a53ceSLaurent Pinchart #include <media/v4l2-device.h>
470975626dSLars-Peter Clausen #include <media/v4l2-event.h>
48c72a53ceSLaurent Pinchart #include <media/v4l2-dv-timings.h>
49859969b3SSakari Ailus #include <media/v4l2-fwnode.h>
5054450f59SHans Verkuil 
5154450f59SHans Verkuil static int debug;
5254450f59SHans Verkuil module_param(debug, int, 0644);
5354450f59SHans Verkuil MODULE_PARM_DESC(debug, "debug level (0-2)");
5454450f59SHans Verkuil 
5554450f59SHans Verkuil MODULE_DESCRIPTION("Analog Devices ADV7604 video decoder driver");
5654450f59SHans Verkuil MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
5754450f59SHans Verkuil MODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>");
5854450f59SHans Verkuil MODULE_LICENSE("GPL");
5954450f59SHans Verkuil 
6054450f59SHans Verkuil /* ADV7604 system clock frequency */
61b44b2e06SPablo Anton #define ADV76XX_FSC (28636360)
6254450f59SHans Verkuil 
63b44b2e06SPablo Anton #define ADV76XX_RGB_OUT					(1 << 1)
64539b33b0SLaurent Pinchart 
65b44b2e06SPablo Anton #define ADV76XX_OP_FORMAT_SEL_8BIT			(0 << 0)
66539b33b0SLaurent Pinchart #define ADV7604_OP_FORMAT_SEL_10BIT			(1 << 0)
67b44b2e06SPablo Anton #define ADV76XX_OP_FORMAT_SEL_12BIT			(2 << 0)
68539b33b0SLaurent Pinchart 
69b44b2e06SPablo Anton #define ADV76XX_OP_MODE_SEL_SDR_422			(0 << 5)
70539b33b0SLaurent Pinchart #define ADV7604_OP_MODE_SEL_DDR_422			(1 << 5)
71b44b2e06SPablo Anton #define ADV76XX_OP_MODE_SEL_SDR_444			(2 << 5)
72539b33b0SLaurent Pinchart #define ADV7604_OP_MODE_SEL_DDR_444			(3 << 5)
73b44b2e06SPablo Anton #define ADV76XX_OP_MODE_SEL_SDR_422_2X			(4 << 5)
74539b33b0SLaurent Pinchart #define ADV7604_OP_MODE_SEL_ADI_CM			(5 << 5)
75539b33b0SLaurent Pinchart 
76b44b2e06SPablo Anton #define ADV76XX_OP_CH_SEL_GBR				(0 << 5)
77b44b2e06SPablo Anton #define ADV76XX_OP_CH_SEL_GRB				(1 << 5)
78b44b2e06SPablo Anton #define ADV76XX_OP_CH_SEL_BGR				(2 << 5)
79b44b2e06SPablo Anton #define ADV76XX_OP_CH_SEL_RGB				(3 << 5)
80b44b2e06SPablo Anton #define ADV76XX_OP_CH_SEL_BRG				(4 << 5)
81b44b2e06SPablo Anton #define ADV76XX_OP_CH_SEL_RBG				(5 << 5)
82539b33b0SLaurent Pinchart 
83b44b2e06SPablo Anton #define ADV76XX_OP_SWAP_CB_CR				(1 << 0)
84539b33b0SLaurent Pinchart 
8541a52373SHans Verkuil #define ADV76XX_MAX_ADDRS (3)
8641a52373SHans Verkuil 
87b44b2e06SPablo Anton enum adv76xx_type {
88d42010a1SLars-Peter Clausen 	ADV7604,
89d42010a1SLars-Peter Clausen 	ADV7611,
908331d30bSWilliam Towle 	ADV7612,
91d42010a1SLars-Peter Clausen };
92d42010a1SLars-Peter Clausen 
93b44b2e06SPablo Anton struct adv76xx_reg_seq {
94d42010a1SLars-Peter Clausen 	unsigned int reg;
95d42010a1SLars-Peter Clausen 	u8 val;
96d42010a1SLars-Peter Clausen };
97d42010a1SLars-Peter Clausen 
98b44b2e06SPablo Anton struct adv76xx_format_info {
99f5fe58fdSBoris BREZILLON 	u32 code;
100539b33b0SLaurent Pinchart 	u8 op_ch_sel;
101539b33b0SLaurent Pinchart 	bool rgb_out;
102539b33b0SLaurent Pinchart 	bool swap_cb_cr;
103539b33b0SLaurent Pinchart 	u8 op_format_sel;
104539b33b0SLaurent Pinchart };
105539b33b0SLaurent Pinchart 
106516613c1SHans Verkuil struct adv76xx_cfg_read_infoframe {
107516613c1SHans Verkuil 	const char *desc;
108516613c1SHans Verkuil 	u8 present_mask;
109516613c1SHans Verkuil 	u8 head_addr;
110516613c1SHans Verkuil 	u8 payload_addr;
111516613c1SHans Verkuil };
112516613c1SHans Verkuil 
113b44b2e06SPablo Anton struct adv76xx_chip_info {
114b44b2e06SPablo Anton 	enum adv76xx_type type;
115d42010a1SLars-Peter Clausen 
116d42010a1SLars-Peter Clausen 	bool has_afe;
117d42010a1SLars-Peter Clausen 	unsigned int max_port;
118d42010a1SLars-Peter Clausen 	unsigned int num_dv_ports;
119d42010a1SLars-Peter Clausen 
120d42010a1SLars-Peter Clausen 	unsigned int edid_enable_reg;
121d42010a1SLars-Peter Clausen 	unsigned int edid_status_reg;
122d42010a1SLars-Peter Clausen 	unsigned int lcf_reg;
123d42010a1SLars-Peter Clausen 
124d42010a1SLars-Peter Clausen 	unsigned int cable_det_mask;
125d42010a1SLars-Peter Clausen 	unsigned int tdms_lock_mask;
126d42010a1SLars-Peter Clausen 	unsigned int fmt_change_digital_mask;
12780f4944eSjean-michel.hautbois@vodalys.com 	unsigned int cp_csc;
128d42010a1SLars-Peter Clausen 
129b44b2e06SPablo Anton 	const struct adv76xx_format_info *formats;
130539b33b0SLaurent Pinchart 	unsigned int nformats;
131539b33b0SLaurent Pinchart 
132d42010a1SLars-Peter Clausen 	void (*set_termination)(struct v4l2_subdev *sd, bool enable);
133d42010a1SLars-Peter Clausen 	void (*setup_irqs)(struct v4l2_subdev *sd);
134d42010a1SLars-Peter Clausen 	unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd);
135d42010a1SLars-Peter Clausen 	unsigned int (*read_cable_det)(struct v4l2_subdev *sd);
136d42010a1SLars-Peter Clausen 
137d42010a1SLars-Peter Clausen 	/* 0 = AFE, 1 = HDMI */
138b44b2e06SPablo Anton 	const struct adv76xx_reg_seq *recommended_settings[2];
139d42010a1SLars-Peter Clausen 	unsigned int num_recommended_settings[2];
140d42010a1SLars-Peter Clausen 
141d42010a1SLars-Peter Clausen 	unsigned long page_mask;
1425380baafSjean-michel.hautbois@vodalys.com 
1435380baafSjean-michel.hautbois@vodalys.com 	/* Masks for timings */
1445380baafSjean-michel.hautbois@vodalys.com 	unsigned int linewidth_mask;
1455380baafSjean-michel.hautbois@vodalys.com 	unsigned int field0_height_mask;
1465380baafSjean-michel.hautbois@vodalys.com 	unsigned int field1_height_mask;
1475380baafSjean-michel.hautbois@vodalys.com 	unsigned int hfrontporch_mask;
1485380baafSjean-michel.hautbois@vodalys.com 	unsigned int hsync_mask;
1495380baafSjean-michel.hautbois@vodalys.com 	unsigned int hbackporch_mask;
1505380baafSjean-michel.hautbois@vodalys.com 	unsigned int field0_vfrontporch_mask;
1515380baafSjean-michel.hautbois@vodalys.com 	unsigned int field1_vfrontporch_mask;
1525380baafSjean-michel.hautbois@vodalys.com 	unsigned int field0_vsync_mask;
1535380baafSjean-michel.hautbois@vodalys.com 	unsigned int field1_vsync_mask;
1545380baafSjean-michel.hautbois@vodalys.com 	unsigned int field0_vbackporch_mask;
1555380baafSjean-michel.hautbois@vodalys.com 	unsigned int field1_vbackporch_mask;
156d42010a1SLars-Peter Clausen };
157d42010a1SLars-Peter Clausen 
15854450f59SHans Verkuil /*
15954450f59SHans Verkuil  **********************************************************************
16054450f59SHans Verkuil  *
16154450f59SHans Verkuil  *  Arrays with configuration parameters for the ADV7604
16254450f59SHans Verkuil  *
16354450f59SHans Verkuil  **********************************************************************
16454450f59SHans Verkuil  */
165c784b1e2SLaurent Pinchart 
166b44b2e06SPablo Anton struct adv76xx_state {
167b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info;
168b44b2e06SPablo Anton 	struct adv76xx_platform_data pdata;
169539b33b0SLaurent Pinchart 
170e9d50e9eSLaurent Pinchart 	struct gpio_desc *hpd_gpio[4];
171f5591da9SDragos Bogdan 	struct gpio_desc *reset_gpio;
172e9d50e9eSLaurent Pinchart 
17354450f59SHans Verkuil 	struct v4l2_subdev sd;
174b44b2e06SPablo Anton 	struct media_pad pads[ADV76XX_PAD_MAX];
175c784b1e2SLaurent Pinchart 	unsigned int source_pad;
176539b33b0SLaurent Pinchart 
17754450f59SHans Verkuil 	struct v4l2_ctrl_handler hdl;
178539b33b0SLaurent Pinchart 
179b44b2e06SPablo Anton 	enum adv76xx_pad selected_input;
180539b33b0SLaurent Pinchart 
18154450f59SHans Verkuil 	struct v4l2_dv_timings timings;
182b44b2e06SPablo Anton 	const struct adv76xx_format_info *format;
183539b33b0SLaurent Pinchart 
1844a31a93aSMats Randgaard 	struct {
18554450f59SHans Verkuil 		u8 edid[256];
1864a31a93aSMats Randgaard 		u32 present;
1874a31a93aSMats Randgaard 		unsigned blocks;
1884a31a93aSMats Randgaard 	} edid;
189dd08beb9SMats Randgaard 	u16 spa_port_a[2];
19054450f59SHans Verkuil 	struct v4l2_fract aspect_ratio;
19154450f59SHans Verkuil 	u32 rgb_quantization_range;
19254450f59SHans Verkuil 	struct delayed_work delayed_work_enable_hotplug;
193cf9afb1dSHans Verkuil 	bool restart_stdi_once;
19454450f59SHans Verkuil 
195cbb5c835SMauro Carvalho Chehab 	/* CEC */
19641a52373SHans Verkuil 	struct cec_adapter *cec_adap;
19741a52373SHans Verkuil 	u8   cec_addr[ADV76XX_MAX_ADDRS];
19841a52373SHans Verkuil 	u8   cec_valid_addrs;
19941a52373SHans Verkuil 	bool cec_enabled_adap;
20041a52373SHans Verkuil 
20154450f59SHans Verkuil 	/* i2c clients */
202b44b2e06SPablo Anton 	struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX];
20354450f59SHans Verkuil 
204f862f57dSPablo Anton 	/* Regmaps */
205f862f57dSPablo Anton 	struct regmap *regmap[ADV76XX_PAGE_MAX];
206f862f57dSPablo Anton 
20754450f59SHans Verkuil 	/* controls */
20854450f59SHans Verkuil 	struct v4l2_ctrl *detect_tx_5v_ctrl;
20954450f59SHans Verkuil 	struct v4l2_ctrl *analog_sampling_phase_ctrl;
21054450f59SHans Verkuil 	struct v4l2_ctrl *free_run_color_manual_ctrl;
21154450f59SHans Verkuil 	struct v4l2_ctrl *free_run_color_ctrl;
21254450f59SHans Verkuil 	struct v4l2_ctrl *rgb_quantization_range_ctrl;
21354450f59SHans Verkuil };
21454450f59SHans Verkuil 
215b44b2e06SPablo Anton static bool adv76xx_has_afe(struct adv76xx_state *state)
216d42010a1SLars-Peter Clausen {
217d42010a1SLars-Peter Clausen 	return state->info->has_afe;
218d42010a1SLars-Peter Clausen }
219d42010a1SLars-Peter Clausen 
220bd3e275fSJean-Michel Hautbois /* Unsupported timings. This device cannot support 720p30. */
221bd3e275fSJean-Michel Hautbois static const struct v4l2_dv_timings adv76xx_timings_exceptions[] = {
222bd3e275fSJean-Michel Hautbois 	V4L2_DV_BT_CEA_1280X720P30,
223bd3e275fSJean-Michel Hautbois 	{ }
22454450f59SHans Verkuil };
22554450f59SHans Verkuil 
226bd3e275fSJean-Michel Hautbois static bool adv76xx_check_dv_timings(const struct v4l2_dv_timings *t, void *hdl)
227bd3e275fSJean-Michel Hautbois {
228bd3e275fSJean-Michel Hautbois 	int i;
229bd3e275fSJean-Michel Hautbois 
230bd3e275fSJean-Michel Hautbois 	for (i = 0; adv76xx_timings_exceptions[i].bt.width; i++)
231bd3e275fSJean-Michel Hautbois 		if (v4l2_match_dv_timings(t, adv76xx_timings_exceptions + i, 0, false))
232bd3e275fSJean-Michel Hautbois 			return false;
233bd3e275fSJean-Michel Hautbois 	return true;
234bd3e275fSJean-Michel Hautbois }
235bd3e275fSJean-Michel Hautbois 
236b44b2e06SPablo Anton struct adv76xx_video_standards {
237ccbd5bc4SHans Verkuil 	struct v4l2_dv_timings timings;
238ccbd5bc4SHans Verkuil 	u8 vid_std;
239ccbd5bc4SHans Verkuil 	u8 v_freq;
240ccbd5bc4SHans Verkuil };
241ccbd5bc4SHans Verkuil 
242ccbd5bc4SHans Verkuil /* sorted by number of lines */
243b44b2e06SPablo Anton static const struct adv76xx_video_standards adv7604_prim_mode_comp[] = {
244ccbd5bc4SHans Verkuil 	/* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */
245ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 },
246ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 },
247ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1280X720P60, 0x19, 0x00 },
248ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 },
249ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 },
250ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 },
251ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 },
252ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 },
253ccbd5bc4SHans Verkuil 	/* TODO add 1920x1080P60_RB (CVT timing) */
254ccbd5bc4SHans Verkuil 	{ },
255ccbd5bc4SHans Verkuil };
256ccbd5bc4SHans Verkuil 
257ccbd5bc4SHans Verkuil /* sorted by number of lines */
258b44b2e06SPablo Anton static const struct adv76xx_video_standards adv7604_prim_mode_gr[] = {
259ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 },
260ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 },
261ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 },
262ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 },
263ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 },
264ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 },
265ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 },
266ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 },
267ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 },
268ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 },
269ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 },
270ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 },
271ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 },
272ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 },
273ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 },
274ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1360X768P60, 0x12, 0x00 },
275ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1366X768P60, 0x13, 0x00 },
276ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1400X1050P60, 0x14, 0x00 },
277ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1400X1050P75, 0x15, 0x00 },
278ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1600X1200P60, 0x16, 0x00 }, /* TODO not tested */
279ccbd5bc4SHans Verkuil 	/* TODO add 1600X1200P60_RB (not a DMT timing) */
280ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1680X1050P60, 0x18, 0x00 },
281ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1920X1200P60_RB, 0x19, 0x00 }, /* TODO not tested */
282ccbd5bc4SHans Verkuil 	{ },
283ccbd5bc4SHans Verkuil };
284ccbd5bc4SHans Verkuil 
285ccbd5bc4SHans Verkuil /* sorted by number of lines */
286b44b2e06SPablo Anton static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_comp[] = {
287ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 },
288ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 },
289ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 },
290ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1280X720P60, 0x13, 0x00 },
291ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 },
292ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 },
293ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 },
294ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 },
295ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 },
296ccbd5bc4SHans Verkuil 	{ },
297ccbd5bc4SHans Verkuil };
298ccbd5bc4SHans Verkuil 
299ccbd5bc4SHans Verkuil /* sorted by number of lines */
300b44b2e06SPablo Anton static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_gr[] = {
301ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 },
302ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 },
303ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 },
304ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 },
305ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 },
306ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 },
307ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 },
308ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 },
309ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 },
310ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 },
311ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 },
312ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 },
313ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 },
314ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 },
315ccbd5bc4SHans Verkuil 	{ V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 },
316ccbd5bc4SHans Verkuil 	{ },
317ccbd5bc4SHans Verkuil };
318ccbd5bc4SHans Verkuil 
31948519838SHans Verkuil static const struct v4l2_event adv76xx_ev_fmt = {
32048519838SHans Verkuil 	.type = V4L2_EVENT_SOURCE_CHANGE,
32148519838SHans Verkuil 	.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
32248519838SHans Verkuil };
32348519838SHans Verkuil 
32454450f59SHans Verkuil /* ----------------------------------------------------------------------- */
32554450f59SHans Verkuil 
326b44b2e06SPablo Anton static inline struct adv76xx_state *to_state(struct v4l2_subdev *sd)
32754450f59SHans Verkuil {
328b44b2e06SPablo Anton 	return container_of(sd, struct adv76xx_state, sd);
32954450f59SHans Verkuil }
33054450f59SHans Verkuil 
33154450f59SHans Verkuil static inline unsigned htotal(const struct v4l2_bt_timings *t)
33254450f59SHans Verkuil {
333eacf8f9aSHans Verkuil 	return V4L2_DV_BT_FRAME_WIDTH(t);
33454450f59SHans Verkuil }
33554450f59SHans Verkuil 
33654450f59SHans Verkuil static inline unsigned vtotal(const struct v4l2_bt_timings *t)
33754450f59SHans Verkuil {
338eacf8f9aSHans Verkuil 	return V4L2_DV_BT_FRAME_HEIGHT(t);
33954450f59SHans Verkuil }
34054450f59SHans Verkuil 
34154450f59SHans Verkuil /* ----------------------------------------------------------------------- */
34254450f59SHans Verkuil 
343f862f57dSPablo Anton static int adv76xx_read_check(struct adv76xx_state *state,
344f862f57dSPablo Anton 			     int client_page, u8 reg)
34554450f59SHans Verkuil {
346f862f57dSPablo Anton 	struct i2c_client *client = state->i2c_clients[client_page];
34754450f59SHans Verkuil 	int err;
348f862f57dSPablo Anton 	unsigned int val;
34954450f59SHans Verkuil 
350f862f57dSPablo Anton 	err = regmap_read(state->regmap[client_page], reg, &val);
351f862f57dSPablo Anton 
352f862f57dSPablo Anton 	if (err) {
353f862f57dSPablo Anton 		v4l_err(client, "error reading %02x, %02x\n",
354f862f57dSPablo Anton 				client->addr, reg);
35554450f59SHans Verkuil 		return err;
35654450f59SHans Verkuil 	}
357f862f57dSPablo Anton 	return val;
358f862f57dSPablo Anton }
35954450f59SHans Verkuil 
360f862f57dSPablo Anton /* adv76xx_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX
361f862f57dSPablo Anton  * size to one or more registers.
362f862f57dSPablo Anton  *
363f862f57dSPablo Anton  * A value of zero will be returned on success, a negative errno will
364f862f57dSPablo Anton  * be returned in error cases.
365f862f57dSPablo Anton  */
366f862f57dSPablo Anton static int adv76xx_write_block(struct adv76xx_state *state, int client_page,
367f862f57dSPablo Anton 			      unsigned int init_reg, const void *val,
368f862f57dSPablo Anton 			      size_t val_len)
36954450f59SHans Verkuil {
370f862f57dSPablo Anton 	struct regmap *regmap = state->regmap[client_page];
37154450f59SHans Verkuil 
372f862f57dSPablo Anton 	if (val_len > I2C_SMBUS_BLOCK_MAX)
373f862f57dSPablo Anton 		val_len = I2C_SMBUS_BLOCK_MAX;
374f862f57dSPablo Anton 
375f862f57dSPablo Anton 	return regmap_raw_write(regmap, init_reg, val, val_len);
37654450f59SHans Verkuil }
37754450f59SHans Verkuil 
37854450f59SHans Verkuil /* ----------------------------------------------------------------------- */
37954450f59SHans Verkuil 
38054450f59SHans Verkuil static inline int io_read(struct v4l2_subdev *sd, u8 reg)
38154450f59SHans Verkuil {
382b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
38354450f59SHans Verkuil 
384f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_IO, reg);
38554450f59SHans Verkuil }
38654450f59SHans Verkuil 
38754450f59SHans Verkuil static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
38854450f59SHans Verkuil {
389b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
39054450f59SHans Verkuil 
391f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val);
39254450f59SHans Verkuil }
39354450f59SHans Verkuil 
39441a52373SHans Verkuil static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
39541a52373SHans Verkuil 				   u8 val)
39654450f59SHans Verkuil {
39722d97e56SLaurent Pinchart 	return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
39854450f59SHans Verkuil }
39954450f59SHans Verkuil 
40054450f59SHans Verkuil static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)
40154450f59SHans Verkuil {
402b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
40354450f59SHans Verkuil 
404f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV7604_PAGE_AVLINK, reg);
40554450f59SHans Verkuil }
40654450f59SHans Verkuil 
40754450f59SHans Verkuil static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val)
40854450f59SHans Verkuil {
409b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
41054450f59SHans Verkuil 
411f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV7604_PAGE_AVLINK], reg, val);
41254450f59SHans Verkuil }
41354450f59SHans Verkuil 
41454450f59SHans Verkuil static inline int cec_read(struct v4l2_subdev *sd, u8 reg)
41554450f59SHans Verkuil {
416b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
41754450f59SHans Verkuil 
418f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_CEC, reg);
41954450f59SHans Verkuil }
42054450f59SHans Verkuil 
42154450f59SHans Verkuil static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
42254450f59SHans Verkuil {
423b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
42454450f59SHans Verkuil 
425f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val);
42654450f59SHans Verkuil }
42754450f59SHans Verkuil 
42841a52373SHans Verkuil static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
42941a52373SHans Verkuil 				   u8 val)
43041a52373SHans Verkuil {
43141a52373SHans Verkuil 	return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
43241a52373SHans Verkuil }
43341a52373SHans Verkuil 
43454450f59SHans Verkuil static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
43554450f59SHans Verkuil {
436b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
43754450f59SHans Verkuil 
438f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_INFOFRAME, reg);
43954450f59SHans Verkuil }
44054450f59SHans Verkuil 
44154450f59SHans Verkuil static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
44254450f59SHans Verkuil {
443b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
44454450f59SHans Verkuil 
445f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_INFOFRAME], reg, val);
44654450f59SHans Verkuil }
44754450f59SHans Verkuil 
44854450f59SHans Verkuil static inline int afe_read(struct v4l2_subdev *sd, u8 reg)
44954450f59SHans Verkuil {
450b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
45154450f59SHans Verkuil 
452f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_AFE, reg);
45354450f59SHans Verkuil }
45454450f59SHans Verkuil 
45554450f59SHans Verkuil static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
45654450f59SHans Verkuil {
457b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
45854450f59SHans Verkuil 
459f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_AFE], reg, val);
46054450f59SHans Verkuil }
46154450f59SHans Verkuil 
46254450f59SHans Verkuil static inline int rep_read(struct v4l2_subdev *sd, u8 reg)
46354450f59SHans Verkuil {
464b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
46554450f59SHans Verkuil 
466f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_REP, reg);
46754450f59SHans Verkuil }
46854450f59SHans Verkuil 
46954450f59SHans Verkuil static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val)
47054450f59SHans Verkuil {
471b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
47254450f59SHans Verkuil 
473f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_REP], reg, val);
47454450f59SHans Verkuil }
47554450f59SHans Verkuil 
47622d97e56SLaurent Pinchart static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
47754450f59SHans Verkuil {
47822d97e56SLaurent Pinchart 	return rep_write(sd, reg, (rep_read(sd, reg) & ~mask) | val);
47954450f59SHans Verkuil }
48054450f59SHans Verkuil 
48154450f59SHans Verkuil static inline int edid_read(struct v4l2_subdev *sd, u8 reg)
48254450f59SHans Verkuil {
483b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
48454450f59SHans Verkuil 
485f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_EDID, reg);
48654450f59SHans Verkuil }
48754450f59SHans Verkuil 
48854450f59SHans Verkuil static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val)
48954450f59SHans Verkuil {
490b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
49154450f59SHans Verkuil 
492f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_EDID], reg, val);
49354450f59SHans Verkuil }
49454450f59SHans Verkuil 
495dd08beb9SMats Randgaard static inline int edid_write_block(struct v4l2_subdev *sd,
496f862f57dSPablo Anton 					unsigned int total_len, const u8 *val)
497dd08beb9SMats Randgaard {
498b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
499dd08beb9SMats Randgaard 	int err = 0;
500f862f57dSPablo Anton 	int i = 0;
501f862f57dSPablo Anton 	int len = 0;
502dd08beb9SMats Randgaard 
503f862f57dSPablo Anton 	v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n",
504f862f57dSPablo Anton 				__func__, total_len);
505dd08beb9SMats Randgaard 
506f862f57dSPablo Anton 	while (!err && i < total_len) {
507f862f57dSPablo Anton 		len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ?
508f862f57dSPablo Anton 				I2C_SMBUS_BLOCK_MAX :
509f862f57dSPablo Anton 				(total_len - i);
510f862f57dSPablo Anton 
511f862f57dSPablo Anton 		err = adv76xx_write_block(state, ADV76XX_PAGE_EDID,
512f862f57dSPablo Anton 				i, val + i, len);
513f862f57dSPablo Anton 		i += len;
514f862f57dSPablo Anton 	}
515f862f57dSPablo Anton 
516dd08beb9SMats Randgaard 	return err;
517dd08beb9SMats Randgaard }
518dd08beb9SMats Randgaard 
519b44b2e06SPablo Anton static void adv76xx_set_hpd(struct adv76xx_state *state, unsigned int hpd)
520e9d50e9eSLaurent Pinchart {
521e9d50e9eSLaurent Pinchart 	unsigned int i;
522e9d50e9eSLaurent Pinchart 
523269bd132SUwe Kleine-König 	for (i = 0; i < state->info->num_dv_ports; ++i)
524e9d50e9eSLaurent Pinchart 		gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i));
525e9d50e9eSLaurent Pinchart 
526b44b2e06SPablo Anton 	v4l2_subdev_notify(&state->sd, ADV76XX_HOTPLUG, &hpd);
527e9d50e9eSLaurent Pinchart }
528e9d50e9eSLaurent Pinchart 
529b44b2e06SPablo Anton static void adv76xx_delayed_work_enable_hotplug(struct work_struct *work)
53054450f59SHans Verkuil {
53154450f59SHans Verkuil 	struct delayed_work *dwork = to_delayed_work(work);
532b44b2e06SPablo Anton 	struct adv76xx_state *state = container_of(dwork, struct adv76xx_state,
53354450f59SHans Verkuil 						delayed_work_enable_hotplug);
53454450f59SHans Verkuil 	struct v4l2_subdev *sd = &state->sd;
53554450f59SHans Verkuil 
53654450f59SHans Verkuil 	v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__);
53754450f59SHans Verkuil 
538b44b2e06SPablo Anton 	adv76xx_set_hpd(state, state->edid.present);
53954450f59SHans Verkuil }
54054450f59SHans Verkuil 
54154450f59SHans Verkuil static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)
54254450f59SHans Verkuil {
543b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
54454450f59SHans Verkuil 
545f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_HDMI, reg);
54654450f59SHans Verkuil }
54754450f59SHans Verkuil 
54851182a94SLaurent Pinchart static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
54951182a94SLaurent Pinchart {
55051182a94SLaurent Pinchart 	return ((hdmi_read(sd, reg) << 8) | hdmi_read(sd, reg + 1)) & mask;
55151182a94SLaurent Pinchart }
55251182a94SLaurent Pinchart 
55354450f59SHans Verkuil static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
55454450f59SHans Verkuil {
555b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
55654450f59SHans Verkuil 
557f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_HDMI], reg, val);
55854450f59SHans Verkuil }
55954450f59SHans Verkuil 
56022d97e56SLaurent Pinchart static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
5614a31a93aSMats Randgaard {
56222d97e56SLaurent Pinchart 	return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val);
5634a31a93aSMats Randgaard }
5644a31a93aSMats Randgaard 
56554450f59SHans Verkuil static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val)
56654450f59SHans Verkuil {
567b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
56854450f59SHans Verkuil 
569f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_TEST], reg, val);
57054450f59SHans Verkuil }
57154450f59SHans Verkuil 
57254450f59SHans Verkuil static inline int cp_read(struct v4l2_subdev *sd, u8 reg)
57354450f59SHans Verkuil {
574b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
57554450f59SHans Verkuil 
576f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV76XX_PAGE_CP, reg);
57754450f59SHans Verkuil }
57854450f59SHans Verkuil 
57951182a94SLaurent Pinchart static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
58051182a94SLaurent Pinchart {
58151182a94SLaurent Pinchart 	return ((cp_read(sd, reg) << 8) | cp_read(sd, reg + 1)) & mask;
58251182a94SLaurent Pinchart }
58351182a94SLaurent Pinchart 
58454450f59SHans Verkuil static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
58554450f59SHans Verkuil {
586b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
58754450f59SHans Verkuil 
588f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV76XX_PAGE_CP], reg, val);
58954450f59SHans Verkuil }
59054450f59SHans Verkuil 
59122d97e56SLaurent Pinchart static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
59254450f59SHans Verkuil {
59322d97e56SLaurent Pinchart 	return cp_write(sd, reg, (cp_read(sd, reg) & ~mask) | val);
59454450f59SHans Verkuil }
59554450f59SHans Verkuil 
59654450f59SHans Verkuil static inline int vdp_read(struct v4l2_subdev *sd, u8 reg)
59754450f59SHans Verkuil {
598b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
59954450f59SHans Verkuil 
600f862f57dSPablo Anton 	return adv76xx_read_check(state, ADV7604_PAGE_VDP, reg);
60154450f59SHans Verkuil }
60254450f59SHans Verkuil 
60354450f59SHans Verkuil static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
60454450f59SHans Verkuil {
605b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
60654450f59SHans Verkuil 
607f862f57dSPablo Anton 	return regmap_write(state->regmap[ADV7604_PAGE_VDP], reg, val);
60854450f59SHans Verkuil }
60954450f59SHans Verkuil 
610b44b2e06SPablo Anton #define ADV76XX_REG(page, offset)	(((page) << 8) | (offset))
611b44b2e06SPablo Anton #define ADV76XX_REG_SEQ_TERM		0xffff
612d42010a1SLars-Peter Clausen 
613d42010a1SLars-Peter Clausen #ifdef CONFIG_VIDEO_ADV_DEBUG
614b44b2e06SPablo Anton static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg)
615d42010a1SLars-Peter Clausen {
616b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
617d42010a1SLars-Peter Clausen 	unsigned int page = reg >> 8;
618f862f57dSPablo Anton 	unsigned int val;
619f862f57dSPablo Anton 	int err;
620d42010a1SLars-Peter Clausen 
6217cc7a833SDan Carpenter 	if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask))
622d42010a1SLars-Peter Clausen 		return -EINVAL;
623d42010a1SLars-Peter Clausen 
624d42010a1SLars-Peter Clausen 	reg &= 0xff;
625f862f57dSPablo Anton 	err = regmap_read(state->regmap[page], reg, &val);
626d42010a1SLars-Peter Clausen 
627f862f57dSPablo Anton 	return err ? err : val;
628d42010a1SLars-Peter Clausen }
629d42010a1SLars-Peter Clausen #endif
630d42010a1SLars-Peter Clausen 
631b44b2e06SPablo Anton static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val)
632d42010a1SLars-Peter Clausen {
633b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
634d42010a1SLars-Peter Clausen 	unsigned int page = reg >> 8;
635d42010a1SLars-Peter Clausen 
6367cc7a833SDan Carpenter 	if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask))
637d42010a1SLars-Peter Clausen 		return -EINVAL;
638d42010a1SLars-Peter Clausen 
639d42010a1SLars-Peter Clausen 	reg &= 0xff;
640d42010a1SLars-Peter Clausen 
641f862f57dSPablo Anton 	return regmap_write(state->regmap[page], reg, val);
642d42010a1SLars-Peter Clausen }
643d42010a1SLars-Peter Clausen 
644b44b2e06SPablo Anton static void adv76xx_write_reg_seq(struct v4l2_subdev *sd,
645b44b2e06SPablo Anton 				  const struct adv76xx_reg_seq *reg_seq)
646d42010a1SLars-Peter Clausen {
647d42010a1SLars-Peter Clausen 	unsigned int i;
648d42010a1SLars-Peter Clausen 
649b44b2e06SPablo Anton 	for (i = 0; reg_seq[i].reg != ADV76XX_REG_SEQ_TERM; i++)
650b44b2e06SPablo Anton 		adv76xx_write_reg(sd, reg_seq[i].reg, reg_seq[i].val);
651d42010a1SLars-Peter Clausen }
652d42010a1SLars-Peter Clausen 
653539b33b0SLaurent Pinchart /* -----------------------------------------------------------------------------
654539b33b0SLaurent Pinchart  * Format helpers
655539b33b0SLaurent Pinchart  */
656539b33b0SLaurent Pinchart 
657b44b2e06SPablo Anton static const struct adv76xx_format_info adv7604_formats[] = {
658b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false,
659b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT },
660b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false,
661b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
662b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true,
663b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
664b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV10_2X10, ADV76XX_OP_CH_SEL_RGB, false, false,
665b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
666b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU10_2X10, ADV76XX_OP_CH_SEL_RGB, false, true,
667b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
668b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false,
669b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
670b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true,
671b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
672b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false,
673b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
674b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true,
675b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
676b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false,
677b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
678b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true,
679b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
680b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_UYVY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, false,
681b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
682b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_VYUY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, true,
683b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
684b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV10_1X20, ADV76XX_OP_CH_SEL_RGB, false, false,
685b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
686b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU10_1X20, ADV76XX_OP_CH_SEL_RGB, false, true,
687b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
688b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false,
689b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
690b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true,
691b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
692b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false,
693b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
694b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true,
695b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
696539b33b0SLaurent Pinchart };
697539b33b0SLaurent Pinchart 
698b44b2e06SPablo Anton static const struct adv76xx_format_info adv7611_formats[] = {
699b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false,
700b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT },
701b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false,
702b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
703b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true,
704b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
705b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false,
706b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
707b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true,
708b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
709b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false,
710b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
711b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true,
712b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
713b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false,
714b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
715b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true,
716b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
717b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false,
718b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
719b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true,
720b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
721b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false,
722b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
723b44b2e06SPablo Anton 	{ MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true,
724b44b2e06SPablo Anton 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
725539b33b0SLaurent Pinchart };
726539b33b0SLaurent Pinchart 
7278331d30bSWilliam Towle static const struct adv76xx_format_info adv7612_formats[] = {
7288331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false,
7298331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT },
7308331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false,
7318331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
7328331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true,
7338331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
7348331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false,
7358331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
7368331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true,
7378331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
7388331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false,
7398331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
7408331d30bSWilliam Towle 	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true,
7418331d30bSWilliam Towle 	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
7428331d30bSWilliam Towle };
7438331d30bSWilliam Towle 
744b44b2e06SPablo Anton static const struct adv76xx_format_info *
745b44b2e06SPablo Anton adv76xx_format_info(struct adv76xx_state *state, u32 code)
746539b33b0SLaurent Pinchart {
747539b33b0SLaurent Pinchart 	unsigned int i;
748539b33b0SLaurent Pinchart 
749539b33b0SLaurent Pinchart 	for (i = 0; i < state->info->nformats; ++i) {
750539b33b0SLaurent Pinchart 		if (state->info->formats[i].code == code)
751539b33b0SLaurent Pinchart 			return &state->info->formats[i];
752539b33b0SLaurent Pinchart 	}
753539b33b0SLaurent Pinchart 
754539b33b0SLaurent Pinchart 	return NULL;
755539b33b0SLaurent Pinchart }
756539b33b0SLaurent Pinchart 
75754450f59SHans Verkuil /* ----------------------------------------------------------------------- */
75854450f59SHans Verkuil 
7594a31a93aSMats Randgaard static inline bool is_analog_input(struct v4l2_subdev *sd)
7604a31a93aSMats Randgaard {
761b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
7624a31a93aSMats Randgaard 
763c784b1e2SLaurent Pinchart 	return state->selected_input == ADV7604_PAD_VGA_RGB ||
764c784b1e2SLaurent Pinchart 	       state->selected_input == ADV7604_PAD_VGA_COMP;
7654a31a93aSMats Randgaard }
7664a31a93aSMats Randgaard 
7674a31a93aSMats Randgaard static inline bool is_digital_input(struct v4l2_subdev *sd)
7684a31a93aSMats Randgaard {
769b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
7704a31a93aSMats Randgaard 
771b44b2e06SPablo Anton 	return state->selected_input == ADV76XX_PAD_HDMI_PORT_A ||
772c784b1e2SLaurent Pinchart 	       state->selected_input == ADV7604_PAD_HDMI_PORT_B ||
773c784b1e2SLaurent Pinchart 	       state->selected_input == ADV7604_PAD_HDMI_PORT_C ||
774c784b1e2SLaurent Pinchart 	       state->selected_input == ADV7604_PAD_HDMI_PORT_D;
7754a31a93aSMats Randgaard }
7764a31a93aSMats Randgaard 
777bd3e275fSJean-Michel Hautbois static const struct v4l2_dv_timings_cap adv7604_timings_cap_analog = {
778bd3e275fSJean-Michel Hautbois 	.type = V4L2_DV_BT_656_1120,
779bd3e275fSJean-Michel Hautbois 	/* keep this initialization for compatibility with GCC < 4.4.6 */
780bd3e275fSJean-Michel Hautbois 	.reserved = { 0 },
781bd3e275fSJean-Michel Hautbois 	V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000,
782bd3e275fSJean-Michel Hautbois 		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
783bd3e275fSJean-Michel Hautbois 			V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
784bd3e275fSJean-Michel Hautbois 		V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
785bd3e275fSJean-Michel Hautbois 			V4L2_DV_BT_CAP_CUSTOM)
786bd3e275fSJean-Michel Hautbois };
787bd3e275fSJean-Michel Hautbois 
788bd3e275fSJean-Michel Hautbois static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = {
789bd3e275fSJean-Michel Hautbois 	.type = V4L2_DV_BT_656_1120,
790bd3e275fSJean-Michel Hautbois 	/* keep this initialization for compatibility with GCC < 4.4.6 */
791bd3e275fSJean-Michel Hautbois 	.reserved = { 0 },
792bd3e275fSJean-Michel Hautbois 	V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000,
793bd3e275fSJean-Michel Hautbois 		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
794bd3e275fSJean-Michel Hautbois 			V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
795bd3e275fSJean-Michel Hautbois 		V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
796bd3e275fSJean-Michel Hautbois 			V4L2_DV_BT_CAP_CUSTOM)
797bd3e275fSJean-Michel Hautbois };
798bd3e275fSJean-Michel Hautbois 
7999c41e690SLaurent Pinchart /*
8009c41e690SLaurent Pinchart  * Return the DV timings capabilities for the requested sink pad. As a special
8019c41e690SLaurent Pinchart  * case, pad value -1 returns the capabilities for the currently selected input.
8029c41e690SLaurent Pinchart  */
8039c41e690SLaurent Pinchart static const struct v4l2_dv_timings_cap *
8049c41e690SLaurent Pinchart adv76xx_get_dv_timings_cap(struct v4l2_subdev *sd, int pad)
805bd3e275fSJean-Michel Hautbois {
8069c41e690SLaurent Pinchart 	if (pad == -1) {
8079c41e690SLaurent Pinchart 		struct adv76xx_state *state = to_state(sd);
8089c41e690SLaurent Pinchart 
8099c41e690SLaurent Pinchart 		pad = state->selected_input;
8109c41e690SLaurent Pinchart 	}
8119c41e690SLaurent Pinchart 
8129c41e690SLaurent Pinchart 	switch (pad) {
8139c41e690SLaurent Pinchart 	case ADV76XX_PAD_HDMI_PORT_A:
8149c41e690SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_B:
8159c41e690SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_C:
8169c41e690SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_D:
8179c41e690SLaurent Pinchart 		return &adv76xx_timings_cap_digital;
8189c41e690SLaurent Pinchart 
8199c41e690SLaurent Pinchart 	case ADV7604_PAD_VGA_RGB:
8209c41e690SLaurent Pinchart 	case ADV7604_PAD_VGA_COMP:
8219c41e690SLaurent Pinchart 	default:
8229c41e690SLaurent Pinchart 		return &adv7604_timings_cap_analog;
8239c41e690SLaurent Pinchart 	}
824bd3e275fSJean-Michel Hautbois }
825bd3e275fSJean-Michel Hautbois 
826bd3e275fSJean-Michel Hautbois 
8274a31a93aSMats Randgaard /* ----------------------------------------------------------------------- */
8284a31a93aSMats Randgaard 
82954450f59SHans Verkuil #ifdef CONFIG_VIDEO_ADV_DEBUG
830b44b2e06SPablo Anton static void adv76xx_inv_register(struct v4l2_subdev *sd)
83154450f59SHans Verkuil {
83254450f59SHans Verkuil 	v4l2_info(sd, "0x000-0x0ff: IO Map\n");
83354450f59SHans Verkuil 	v4l2_info(sd, "0x100-0x1ff: AVLink Map\n");
83454450f59SHans Verkuil 	v4l2_info(sd, "0x200-0x2ff: CEC Map\n");
83554450f59SHans Verkuil 	v4l2_info(sd, "0x300-0x3ff: InfoFrame Map\n");
83654450f59SHans Verkuil 	v4l2_info(sd, "0x400-0x4ff: ESDP Map\n");
83754450f59SHans Verkuil 	v4l2_info(sd, "0x500-0x5ff: DPP Map\n");
83854450f59SHans Verkuil 	v4l2_info(sd, "0x600-0x6ff: AFE Map\n");
83954450f59SHans Verkuil 	v4l2_info(sd, "0x700-0x7ff: Repeater Map\n");
84054450f59SHans Verkuil 	v4l2_info(sd, "0x800-0x8ff: EDID Map\n");
84154450f59SHans Verkuil 	v4l2_info(sd, "0x900-0x9ff: HDMI Map\n");
84254450f59SHans Verkuil 	v4l2_info(sd, "0xa00-0xaff: Test Map\n");
84354450f59SHans Verkuil 	v4l2_info(sd, "0xb00-0xbff: CP Map\n");
84454450f59SHans Verkuil 	v4l2_info(sd, "0xc00-0xcff: VDP Map\n");
84554450f59SHans Verkuil }
84654450f59SHans Verkuil 
847b44b2e06SPablo Anton static int adv76xx_g_register(struct v4l2_subdev *sd,
84854450f59SHans Verkuil 					struct v4l2_dbg_register *reg)
84954450f59SHans Verkuil {
850d42010a1SLars-Peter Clausen 	int ret;
851d42010a1SLars-Peter Clausen 
852b44b2e06SPablo Anton 	ret = adv76xx_read_reg(sd, reg->reg);
853d42010a1SLars-Peter Clausen 	if (ret < 0) {
85454450f59SHans Verkuil 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
855b44b2e06SPablo Anton 		adv76xx_inv_register(sd);
856d42010a1SLars-Peter Clausen 		return ret;
85754450f59SHans Verkuil 	}
858d42010a1SLars-Peter Clausen 
859d42010a1SLars-Peter Clausen 	reg->size = 1;
860d42010a1SLars-Peter Clausen 	reg->val = ret;
861d42010a1SLars-Peter Clausen 
86254450f59SHans Verkuil 	return 0;
86354450f59SHans Verkuil }
86454450f59SHans Verkuil 
865b44b2e06SPablo Anton static int adv76xx_s_register(struct v4l2_subdev *sd,
866977ba3b1SHans Verkuil 					const struct v4l2_dbg_register *reg)
86754450f59SHans Verkuil {
868d42010a1SLars-Peter Clausen 	int ret;
8691577461bSHans Verkuil 
870b44b2e06SPablo Anton 	ret = adv76xx_write_reg(sd, reg->reg, reg->val);
871d42010a1SLars-Peter Clausen 	if (ret < 0) {
87254450f59SHans Verkuil 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
873b44b2e06SPablo Anton 		adv76xx_inv_register(sd);
874d42010a1SLars-Peter Clausen 		return ret;
87554450f59SHans Verkuil 	}
876d42010a1SLars-Peter Clausen 
87754450f59SHans Verkuil 	return 0;
87854450f59SHans Verkuil }
87954450f59SHans Verkuil #endif
88054450f59SHans Verkuil 
881d42010a1SLars-Peter Clausen static unsigned int adv7604_read_cable_det(struct v4l2_subdev *sd)
882d42010a1SLars-Peter Clausen {
883d42010a1SLars-Peter Clausen 	u8 value = io_read(sd, 0x6f);
884d42010a1SLars-Peter Clausen 
885d42010a1SLars-Peter Clausen 	return ((value & 0x10) >> 4)
886d42010a1SLars-Peter Clausen 	     | ((value & 0x08) >> 2)
887d42010a1SLars-Peter Clausen 	     | ((value & 0x04) << 0)
888d42010a1SLars-Peter Clausen 	     | ((value & 0x02) << 2);
889d42010a1SLars-Peter Clausen }
890d42010a1SLars-Peter Clausen 
891d42010a1SLars-Peter Clausen static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd)
892d42010a1SLars-Peter Clausen {
893d42010a1SLars-Peter Clausen 	u8 value = io_read(sd, 0x6f);
894d42010a1SLars-Peter Clausen 
895d42010a1SLars-Peter Clausen 	return value & 1;
896d42010a1SLars-Peter Clausen }
897d42010a1SLars-Peter Clausen 
8987111cdddSWilliam Towle static unsigned int adv7612_read_cable_det(struct v4l2_subdev *sd)
8997111cdddSWilliam Towle {
9007111cdddSWilliam Towle 	/*  Reads CABLE_DET_A_RAW. For input B support, need to
9017111cdddSWilliam Towle 	 *  account for bit 7 [MSB] of 0x6a (ie. CABLE_DET_B_RAW)
9027111cdddSWilliam Towle 	 */
9037111cdddSWilliam Towle 	u8 value = io_read(sd, 0x6f);
9047111cdddSWilliam Towle 
9057111cdddSWilliam Towle 	return value & 1;
9067111cdddSWilliam Towle }
9077111cdddSWilliam Towle 
908b44b2e06SPablo Anton static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
90954450f59SHans Verkuil {
910b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
911b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
91241a52373SHans Verkuil 	u16 cable_det = info->read_cable_det(sd);
91354450f59SHans Verkuil 
91441a52373SHans Verkuil 	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
91554450f59SHans Verkuil }
91654450f59SHans Verkuil 
917ccbd5bc4SHans Verkuil static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
918ccbd5bc4SHans Verkuil 		u8 prim_mode,
919b44b2e06SPablo Anton 		const struct adv76xx_video_standards *predef_vid_timings,
920ccbd5bc4SHans Verkuil 		const struct v4l2_dv_timings *timings)
92154450f59SHans Verkuil {
922ccbd5bc4SHans Verkuil 	int i;
92354450f59SHans Verkuil 
924ccbd5bc4SHans Verkuil 	for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {
925ef1ed8f5SHans Verkuil 		if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings,
92685f9e06cSHans Verkuil 				is_digital_input(sd) ? 250000 : 1000000, false))
927ccbd5bc4SHans Verkuil 			continue;
928ccbd5bc4SHans Verkuil 		io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */
929ccbd5bc4SHans Verkuil 		io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) +
930ccbd5bc4SHans Verkuil 				prim_mode); /* v_freq and prim mode */
931ccbd5bc4SHans Verkuil 		return 0;
93254450f59SHans Verkuil 	}
93354450f59SHans Verkuil 
934ccbd5bc4SHans Verkuil 	return -1;
935ccbd5bc4SHans Verkuil }
93654450f59SHans Verkuil 
937ccbd5bc4SHans Verkuil static int configure_predefined_video_timings(struct v4l2_subdev *sd,
938ccbd5bc4SHans Verkuil 		struct v4l2_dv_timings *timings)
939ccbd5bc4SHans Verkuil {
940b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
941ccbd5bc4SHans Verkuil 	int err;
942ccbd5bc4SHans Verkuil 
943ccbd5bc4SHans Verkuil 	v4l2_dbg(1, debug, sd, "%s", __func__);
944ccbd5bc4SHans Verkuil 
945b44b2e06SPablo Anton 	if (adv76xx_has_afe(state)) {
94654450f59SHans Verkuil 		/* reset to default values */
94754450f59SHans Verkuil 		io_write(sd, 0x16, 0x43);
94854450f59SHans Verkuil 		io_write(sd, 0x17, 0x5a);
949d42010a1SLars-Peter Clausen 	}
950ccbd5bc4SHans Verkuil 	/* disable embedded syncs for auto graphics mode */
95122d97e56SLaurent Pinchart 	cp_write_clr_set(sd, 0x81, 0x10, 0x00);
952ccbd5bc4SHans Verkuil 	cp_write(sd, 0x8f, 0x00);
953ccbd5bc4SHans Verkuil 	cp_write(sd, 0x90, 0x00);
95454450f59SHans Verkuil 	cp_write(sd, 0xa2, 0x00);
95554450f59SHans Verkuil 	cp_write(sd, 0xa3, 0x00);
95654450f59SHans Verkuil 	cp_write(sd, 0xa4, 0x00);
95754450f59SHans Verkuil 	cp_write(sd, 0xa5, 0x00);
95854450f59SHans Verkuil 	cp_write(sd, 0xa6, 0x00);
95954450f59SHans Verkuil 	cp_write(sd, 0xa7, 0x00);
960ccbd5bc4SHans Verkuil 	cp_write(sd, 0xab, 0x00);
961ccbd5bc4SHans Verkuil 	cp_write(sd, 0xac, 0x00);
962ccbd5bc4SHans Verkuil 
9634a31a93aSMats Randgaard 	if (is_analog_input(sd)) {
964ccbd5bc4SHans Verkuil 		err = find_and_set_predefined_video_timings(sd,
965ccbd5bc4SHans Verkuil 				0x01, adv7604_prim_mode_comp, timings);
966ccbd5bc4SHans Verkuil 		if (err)
967ccbd5bc4SHans Verkuil 			err = find_and_set_predefined_video_timings(sd,
968ccbd5bc4SHans Verkuil 					0x02, adv7604_prim_mode_gr, timings);
9694a31a93aSMats Randgaard 	} else if (is_digital_input(sd)) {
970ccbd5bc4SHans Verkuil 		err = find_and_set_predefined_video_timings(sd,
971b44b2e06SPablo Anton 				0x05, adv76xx_prim_mode_hdmi_comp, timings);
972ccbd5bc4SHans Verkuil 		if (err)
973ccbd5bc4SHans Verkuil 			err = find_and_set_predefined_video_timings(sd,
974b44b2e06SPablo Anton 					0x06, adv76xx_prim_mode_hdmi_gr, timings);
9754a31a93aSMats Randgaard 	} else {
9764a31a93aSMats Randgaard 		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
9774a31a93aSMats Randgaard 				__func__, state->selected_input);
978ccbd5bc4SHans Verkuil 		err = -1;
97954450f59SHans Verkuil 	}
98054450f59SHans Verkuil 
98154450f59SHans Verkuil 
982ccbd5bc4SHans Verkuil 	return err;
983ccbd5bc4SHans Verkuil }
984ccbd5bc4SHans Verkuil 
985ccbd5bc4SHans Verkuil static void configure_custom_video_timings(struct v4l2_subdev *sd,
986ccbd5bc4SHans Verkuil 		const struct v4l2_bt_timings *bt)
987ccbd5bc4SHans Verkuil {
988b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
989ccbd5bc4SHans Verkuil 	u32 width = htotal(bt);
990ccbd5bc4SHans Verkuil 	u32 height = vtotal(bt);
991ccbd5bc4SHans Verkuil 	u16 cp_start_sav = bt->hsync + bt->hbackporch - 4;
992ccbd5bc4SHans Verkuil 	u16 cp_start_eav = width - bt->hfrontporch;
993ccbd5bc4SHans Verkuil 	u16 cp_start_vbi = height - bt->vfrontporch;
994ccbd5bc4SHans Verkuil 	u16 cp_end_vbi = bt->vsync + bt->vbackporch;
995ccbd5bc4SHans Verkuil 	u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ?
996b44b2e06SPablo Anton 		((width * (ADV76XX_FSC / 100)) / ((u32)bt->pixelclock / 100)) : 0;
997ccbd5bc4SHans Verkuil 	const u8 pll[2] = {
998ccbd5bc4SHans Verkuil 		0xc0 | ((width >> 8) & 0x1f),
999ccbd5bc4SHans Verkuil 		width & 0xff
1000ccbd5bc4SHans Verkuil 	};
1001ccbd5bc4SHans Verkuil 
1002ccbd5bc4SHans Verkuil 	v4l2_dbg(2, debug, sd, "%s\n", __func__);
1003ccbd5bc4SHans Verkuil 
10044a31a93aSMats Randgaard 	if (is_analog_input(sd)) {
1005ccbd5bc4SHans Verkuil 		/* auto graphics */
1006ccbd5bc4SHans Verkuil 		io_write(sd, 0x00, 0x07); /* video std */
1007ccbd5bc4SHans Verkuil 		io_write(sd, 0x01, 0x02); /* prim mode */
1008ccbd5bc4SHans Verkuil 		/* enable embedded syncs for auto graphics mode */
100922d97e56SLaurent Pinchart 		cp_write_clr_set(sd, 0x81, 0x10, 0x10);
1010ccbd5bc4SHans Verkuil 
1011ccbd5bc4SHans Verkuil 		/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */
1012ccbd5bc4SHans Verkuil 		/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
1013ccbd5bc4SHans Verkuil 		/* IO-map reg. 0x16 and 0x17 should be written in sequence */
1014f862f57dSPablo Anton 		if (regmap_raw_write(state->regmap[ADV76XX_PAGE_IO],
1015f862f57dSPablo Anton 					0x16, pll, 2))
1016ccbd5bc4SHans Verkuil 			v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n");
1017ccbd5bc4SHans Verkuil 
1018ccbd5bc4SHans Verkuil 		/* active video - horizontal timing */
1019ccbd5bc4SHans Verkuil 		cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff);
1020ccbd5bc4SHans Verkuil 		cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) |
1021ccbd5bc4SHans Verkuil 				   ((cp_start_eav >> 8) & 0x0f));
1022ccbd5bc4SHans Verkuil 		cp_write(sd, 0xa4, cp_start_eav & 0xff);
1023ccbd5bc4SHans Verkuil 
1024ccbd5bc4SHans Verkuil 		/* active video - vertical timing */
1025ccbd5bc4SHans Verkuil 		cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff);
1026ccbd5bc4SHans Verkuil 		cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) |
1027ccbd5bc4SHans Verkuil 				   ((cp_end_vbi >> 8) & 0xf));
1028ccbd5bc4SHans Verkuil 		cp_write(sd, 0xa7, cp_end_vbi & 0xff);
10294a31a93aSMats Randgaard 	} else if (is_digital_input(sd)) {
1030ccbd5bc4SHans Verkuil 		/* set default prim_mode/vid_std for HDMI
103139c1cb2bSJonathan McCrohan 		   according to [REF_03, c. 4.2] */
1032ccbd5bc4SHans Verkuil 		io_write(sd, 0x00, 0x02); /* video std */
1033ccbd5bc4SHans Verkuil 		io_write(sd, 0x01, 0x06); /* prim mode */
10344a31a93aSMats Randgaard 	} else {
10354a31a93aSMats Randgaard 		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
10364a31a93aSMats Randgaard 				__func__, state->selected_input);
1037ccbd5bc4SHans Verkuil 	}
1038ccbd5bc4SHans Verkuil 
1039ccbd5bc4SHans Verkuil 	cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7);
1040ccbd5bc4SHans Verkuil 	cp_write(sd, 0x90, ch1_fr_ll & 0xff);
1041ccbd5bc4SHans Verkuil 	cp_write(sd, 0xab, (height >> 4) & 0xff);
1042ccbd5bc4SHans Verkuil 	cp_write(sd, 0xac, (height & 0x0f) << 4);
1043ccbd5bc4SHans Verkuil }
1044ccbd5bc4SHans Verkuil 
1045b44b2e06SPablo Anton static void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c)
10465c6c6349SMats Randgaard {
1047b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
10485c6c6349SMats Randgaard 	u8 offset_buf[4];
10495c6c6349SMats Randgaard 
10505c6c6349SMats Randgaard 	if (auto_offset) {
10515c6c6349SMats Randgaard 		offset_a = 0x3ff;
10525c6c6349SMats Randgaard 		offset_b = 0x3ff;
10535c6c6349SMats Randgaard 		offset_c = 0x3ff;
10545c6c6349SMats Randgaard 	}
10555c6c6349SMats Randgaard 
10565c6c6349SMats Randgaard 	v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n",
10575c6c6349SMats Randgaard 			__func__, auto_offset ? "Auto" : "Manual",
10585c6c6349SMats Randgaard 			offset_a, offset_b, offset_c);
10595c6c6349SMats Randgaard 
10605c6c6349SMats Randgaard 	offset_buf[0] = (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4);
10615c6c6349SMats Randgaard 	offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6);
10625c6c6349SMats Randgaard 	offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8);
10635c6c6349SMats Randgaard 	offset_buf[3] = offset_c & 0x0ff;
10645c6c6349SMats Randgaard 
10655c6c6349SMats Randgaard 	/* Registers must be written in this order with no i2c access in between */
1066f862f57dSPablo Anton 	if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP],
1067f862f57dSPablo Anton 			0x77, offset_buf, 4))
10685c6c6349SMats Randgaard 		v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
10695c6c6349SMats Randgaard }
10705c6c6349SMats Randgaard 
1071b44b2e06SPablo Anton static void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c)
10725c6c6349SMats Randgaard {
1073b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
10745c6c6349SMats Randgaard 	u8 gain_buf[4];
10755c6c6349SMats Randgaard 	u8 gain_man = 1;
10765c6c6349SMats Randgaard 	u8 agc_mode_man = 1;
10775c6c6349SMats Randgaard 
10785c6c6349SMats Randgaard 	if (auto_gain) {
10795c6c6349SMats Randgaard 		gain_man = 0;
10805c6c6349SMats Randgaard 		agc_mode_man = 0;
10815c6c6349SMats Randgaard 		gain_a = 0x100;
10825c6c6349SMats Randgaard 		gain_b = 0x100;
10835c6c6349SMats Randgaard 		gain_c = 0x100;
10845c6c6349SMats Randgaard 	}
10855c6c6349SMats Randgaard 
10865c6c6349SMats Randgaard 	v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n",
10875c6c6349SMats Randgaard 			__func__, auto_gain ? "Auto" : "Manual",
10885c6c6349SMats Randgaard 			gain_a, gain_b, gain_c);
10895c6c6349SMats Randgaard 
10905c6c6349SMats Randgaard 	gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4));
10915c6c6349SMats Randgaard 	gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6));
10925c6c6349SMats Randgaard 	gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8));
10935c6c6349SMats Randgaard 	gain_buf[3] = ((gain_c & 0x0ff));
10945c6c6349SMats Randgaard 
10955c6c6349SMats Randgaard 	/* Registers must be written in this order with no i2c access in between */
1096f862f57dSPablo Anton 	if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP],
1097f862f57dSPablo Anton 			     0x73, gain_buf, 4))
10985c6c6349SMats Randgaard 		v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
10995c6c6349SMats Randgaard }
11005c6c6349SMats Randgaard 
110154450f59SHans Verkuil static void set_rgb_quantization_range(struct v4l2_subdev *sd)
110254450f59SHans Verkuil {
1103b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
11045c6c6349SMats Randgaard 	bool rgb_output = io_read(sd, 0x02) & 0x02;
11055c6c6349SMats Randgaard 	bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
1106fd74246dSHans Verkuil 	u8 y = HDMI_COLORSPACE_RGB;
1107fd74246dSHans Verkuil 
1108fd74246dSHans Verkuil 	if (hdmi_signal && (io_read(sd, 0x60) & 1))
1109fd74246dSHans Verkuil 		y = infoframe_read(sd, 0x01) >> 5;
111054450f59SHans Verkuil 
11115c6c6349SMats Randgaard 	v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
11125c6c6349SMats Randgaard 			__func__, state->rgb_quantization_range,
11135c6c6349SMats Randgaard 			rgb_output, hdmi_signal);
11145c6c6349SMats Randgaard 
1115b44b2e06SPablo Anton 	adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0);
1116b44b2e06SPablo Anton 	adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0);
1117fd74246dSHans Verkuil 	io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4);
11189833239eSMats Randgaard 
111954450f59SHans Verkuil 	switch (state->rgb_quantization_range) {
112054450f59SHans Verkuil 	case V4L2_DV_RGB_RANGE_AUTO:
1121c784b1e2SLaurent Pinchart 		if (state->selected_input == ADV7604_PAD_VGA_RGB) {
11229833239eSMats Randgaard 			/* Receiving analog RGB signal
11239833239eSMats Randgaard 			 * Set RGB full range (0-255) */
112422d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0x10);
11259833239eSMats Randgaard 			break;
11269833239eSMats Randgaard 		}
112754450f59SHans Verkuil 
1128c784b1e2SLaurent Pinchart 		if (state->selected_input == ADV7604_PAD_VGA_COMP) {
11299833239eSMats Randgaard 			/* Receiving analog YPbPr signal
11309833239eSMats Randgaard 			 * Set automode */
113122d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0xf0);
11329833239eSMats Randgaard 			break;
11339833239eSMats Randgaard 		}
11349833239eSMats Randgaard 
11355c6c6349SMats Randgaard 		if (hdmi_signal) {
11369833239eSMats Randgaard 			/* Receiving HDMI signal
11379833239eSMats Randgaard 			 * Set automode */
113822d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0xf0);
11399833239eSMats Randgaard 			break;
11409833239eSMats Randgaard 		}
11419833239eSMats Randgaard 
11429833239eSMats Randgaard 		/* Receiving DVI-D signal
11439833239eSMats Randgaard 		 * ADV7604 selects RGB limited range regardless of
11449833239eSMats Randgaard 		 * input format (CE/IT) in automatic mode */
1145680fee04SHans Verkuil 		if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
114654450f59SHans Verkuil 			/* RGB limited range (16-235) */
114722d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0x00);
114854450f59SHans Verkuil 		} else {
114954450f59SHans Verkuil 			/* RGB full range (0-255) */
115022d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0x10);
11515c6c6349SMats Randgaard 
11525c6c6349SMats Randgaard 			if (is_digital_input(sd) && rgb_output) {
1153b44b2e06SPablo Anton 				adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40);
11545c6c6349SMats Randgaard 			} else {
1155b44b2e06SPablo Anton 				adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
1156b44b2e06SPablo Anton 				adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70);
11575c6c6349SMats Randgaard 			}
115854450f59SHans Verkuil 		}
115954450f59SHans Verkuil 		break;
116054450f59SHans Verkuil 	case V4L2_DV_RGB_RANGE_LIMITED:
1161c784b1e2SLaurent Pinchart 		if (state->selected_input == ADV7604_PAD_VGA_COMP) {
1162d261e842SMats Randgaard 			/* YCrCb limited range (16-235) */
116322d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0x20);
11645c6c6349SMats Randgaard 			break;
11655c6c6349SMats Randgaard 		}
11665c6c6349SMats Randgaard 
1167fd74246dSHans Verkuil 		if (y != HDMI_COLORSPACE_RGB)
1168fd74246dSHans Verkuil 			break;
1169fd74246dSHans Verkuil 
117054450f59SHans Verkuil 		/* RGB limited range (16-235) */
117122d97e56SLaurent Pinchart 		io_write_clr_set(sd, 0x02, 0xf0, 0x00);
11725c6c6349SMats Randgaard 
117354450f59SHans Verkuil 		break;
117454450f59SHans Verkuil 	case V4L2_DV_RGB_RANGE_FULL:
1175c784b1e2SLaurent Pinchart 		if (state->selected_input == ADV7604_PAD_VGA_COMP) {
1176d261e842SMats Randgaard 			/* YCrCb full range (0-255) */
117722d97e56SLaurent Pinchart 			io_write_clr_set(sd, 0x02, 0xf0, 0x60);
11785c6c6349SMats Randgaard 			break;
11795c6c6349SMats Randgaard 		}
11805c6c6349SMats Randgaard 
1181fd74246dSHans Verkuil 		if (y != HDMI_COLORSPACE_RGB)
1182fd74246dSHans Verkuil 			break;
1183fd74246dSHans Verkuil 
118454450f59SHans Verkuil 		/* RGB full range (0-255) */
118522d97e56SLaurent Pinchart 		io_write_clr_set(sd, 0x02, 0xf0, 0x10);
11865c6c6349SMats Randgaard 
11875c6c6349SMats Randgaard 		if (is_analog_input(sd) || hdmi_signal)
11885c6c6349SMats Randgaard 			break;
11895c6c6349SMats Randgaard 
11905c6c6349SMats Randgaard 		/* Adjust gain/offset for DVI-D signals only */
11915c6c6349SMats Randgaard 		if (rgb_output) {
1192b44b2e06SPablo Anton 			adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40);
11935c6c6349SMats Randgaard 		} else {
1194b44b2e06SPablo Anton 			adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
1195b44b2e06SPablo Anton 			adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70);
1196d261e842SMats Randgaard 		}
119754450f59SHans Verkuil 		break;
119854450f59SHans Verkuil 	}
119954450f59SHans Verkuil }
120054450f59SHans Verkuil 
1201b44b2e06SPablo Anton static int adv76xx_s_ctrl(struct v4l2_ctrl *ctrl)
120254450f59SHans Verkuil {
1203c269887cSLaurent Pinchart 	struct v4l2_subdev *sd =
1204b44b2e06SPablo Anton 		&container_of(ctrl->handler, struct adv76xx_state, hdl)->sd;
1205c269887cSLaurent Pinchart 
1206b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
120754450f59SHans Verkuil 
120854450f59SHans Verkuil 	switch (ctrl->id) {
120954450f59SHans Verkuil 	case V4L2_CID_BRIGHTNESS:
121054450f59SHans Verkuil 		cp_write(sd, 0x3c, ctrl->val);
121154450f59SHans Verkuil 		return 0;
121254450f59SHans Verkuil 	case V4L2_CID_CONTRAST:
121354450f59SHans Verkuil 		cp_write(sd, 0x3a, ctrl->val);
121454450f59SHans Verkuil 		return 0;
121554450f59SHans Verkuil 	case V4L2_CID_SATURATION:
121654450f59SHans Verkuil 		cp_write(sd, 0x3b, ctrl->val);
121754450f59SHans Verkuil 		return 0;
121854450f59SHans Verkuil 	case V4L2_CID_HUE:
121954450f59SHans Verkuil 		cp_write(sd, 0x3d, ctrl->val);
122054450f59SHans Verkuil 		return 0;
122154450f59SHans Verkuil 	case  V4L2_CID_DV_RX_RGB_RANGE:
122254450f59SHans Verkuil 		state->rgb_quantization_range = ctrl->val;
122354450f59SHans Verkuil 		set_rgb_quantization_range(sd);
122454450f59SHans Verkuil 		return 0;
122554450f59SHans Verkuil 	case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE:
1226b44b2e06SPablo Anton 		if (!adv76xx_has_afe(state))
1227d42010a1SLars-Peter Clausen 			return -EINVAL;
122854450f59SHans Verkuil 		/* Set the analog sampling phase. This is needed to find the
122954450f59SHans Verkuil 		   best sampling phase for analog video: an application or
123054450f59SHans Verkuil 		   driver has to try a number of phases and analyze the picture
123154450f59SHans Verkuil 		   quality before settling on the best performing phase. */
123254450f59SHans Verkuil 		afe_write(sd, 0xc8, ctrl->val);
123354450f59SHans Verkuil 		return 0;
123454450f59SHans Verkuil 	case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL:
123554450f59SHans Verkuil 		/* Use the default blue color for free running mode,
123654450f59SHans Verkuil 		   or supply your own. */
123722d97e56SLaurent Pinchart 		cp_write_clr_set(sd, 0xbf, 0x04, ctrl->val << 2);
123854450f59SHans Verkuil 		return 0;
123954450f59SHans Verkuil 	case V4L2_CID_ADV_RX_FREE_RUN_COLOR:
124054450f59SHans Verkuil 		cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16);
124154450f59SHans Verkuil 		cp_write(sd, 0xc1, (ctrl->val & 0x00ff00) >> 8);
124254450f59SHans Verkuil 		cp_write(sd, 0xc2, (u8)(ctrl->val & 0x0000ff));
124354450f59SHans Verkuil 		return 0;
124454450f59SHans Verkuil 	}
124554450f59SHans Verkuil 	return -EINVAL;
124654450f59SHans Verkuil }
124754450f59SHans Verkuil 
1248297a4144SHans Verkuil static int adv76xx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
1249297a4144SHans Verkuil {
1250297a4144SHans Verkuil 	struct v4l2_subdev *sd =
1251297a4144SHans Verkuil 		&container_of(ctrl->handler, struct adv76xx_state, hdl)->sd;
1252297a4144SHans Verkuil 
1253297a4144SHans Verkuil 	if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) {
1254297a4144SHans Verkuil 		ctrl->val = V4L2_DV_IT_CONTENT_TYPE_NO_ITC;
1255297a4144SHans Verkuil 		if ((io_read(sd, 0x60) & 1) && (infoframe_read(sd, 0x03) & 0x80))
1256297a4144SHans Verkuil 			ctrl->val = (infoframe_read(sd, 0x05) >> 4) & 3;
1257297a4144SHans Verkuil 		return 0;
1258297a4144SHans Verkuil 	}
1259297a4144SHans Verkuil 	return -EINVAL;
1260297a4144SHans Verkuil }
1261297a4144SHans Verkuil 
126254450f59SHans Verkuil /* ----------------------------------------------------------------------- */
126354450f59SHans Verkuil 
126454450f59SHans Verkuil static inline bool no_power(struct v4l2_subdev *sd)
126554450f59SHans Verkuil {
126654450f59SHans Verkuil 	/* Entire chip or CP powered off */
126754450f59SHans Verkuil 	return io_read(sd, 0x0c) & 0x24;
126854450f59SHans Verkuil }
126954450f59SHans Verkuil 
127054450f59SHans Verkuil static inline bool no_signal_tmds(struct v4l2_subdev *sd)
127154450f59SHans Verkuil {
1272b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
12734a31a93aSMats Randgaard 
12744a31a93aSMats Randgaard 	return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input));
127554450f59SHans Verkuil }
127654450f59SHans Verkuil 
127754450f59SHans Verkuil static inline bool no_lock_tmds(struct v4l2_subdev *sd)
127854450f59SHans Verkuil {
1279b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1280b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
1281d42010a1SLars-Peter Clausen 
1282d42010a1SLars-Peter Clausen 	return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask;
128354450f59SHans Verkuil }
128454450f59SHans Verkuil 
1285bb88f325SMartin Bugge static inline bool is_hdmi(struct v4l2_subdev *sd)
1286bb88f325SMartin Bugge {
1287bb88f325SMartin Bugge 	return hdmi_read(sd, 0x05) & 0x80;
1288bb88f325SMartin Bugge }
1289bb88f325SMartin Bugge 
129054450f59SHans Verkuil static inline bool no_lock_sspd(struct v4l2_subdev *sd)
129154450f59SHans Verkuil {
1292b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1293d42010a1SLars-Peter Clausen 
1294d42010a1SLars-Peter Clausen 	/*
1295d42010a1SLars-Peter Clausen 	 * Chips without a AFE don't expose registers for the SSPD, so just assume
1296d42010a1SLars-Peter Clausen 	 * that we have a lock.
1297d42010a1SLars-Peter Clausen 	 */
1298b44b2e06SPablo Anton 	if (adv76xx_has_afe(state))
1299d42010a1SLars-Peter Clausen 		return false;
1300d42010a1SLars-Peter Clausen 
130154450f59SHans Verkuil 	/* TODO channel 2 */
130254450f59SHans Verkuil 	return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0);
130354450f59SHans Verkuil }
130454450f59SHans Verkuil 
130554450f59SHans Verkuil static inline bool no_lock_stdi(struct v4l2_subdev *sd)
130654450f59SHans Verkuil {
130754450f59SHans Verkuil 	/* TODO channel 2 */
130854450f59SHans Verkuil 	return !(cp_read(sd, 0xb1) & 0x80);
130954450f59SHans Verkuil }
131054450f59SHans Verkuil 
131154450f59SHans Verkuil static inline bool no_signal(struct v4l2_subdev *sd)
131254450f59SHans Verkuil {
131354450f59SHans Verkuil 	bool ret;
131454450f59SHans Verkuil 
131554450f59SHans Verkuil 	ret = no_power(sd);
131654450f59SHans Verkuil 
131754450f59SHans Verkuil 	ret |= no_lock_stdi(sd);
131854450f59SHans Verkuil 	ret |= no_lock_sspd(sd);
131954450f59SHans Verkuil 
13204a31a93aSMats Randgaard 	if (is_digital_input(sd)) {
132154450f59SHans Verkuil 		ret |= no_lock_tmds(sd);
132254450f59SHans Verkuil 		ret |= no_signal_tmds(sd);
132354450f59SHans Verkuil 	}
132454450f59SHans Verkuil 
132554450f59SHans Verkuil 	return ret;
132654450f59SHans Verkuil }
132754450f59SHans Verkuil 
132854450f59SHans Verkuil static inline bool no_lock_cp(struct v4l2_subdev *sd)
132954450f59SHans Verkuil {
1330b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1331d42010a1SLars-Peter Clausen 
1332b44b2e06SPablo Anton 	if (!adv76xx_has_afe(state))
1333d42010a1SLars-Peter Clausen 		return false;
1334d42010a1SLars-Peter Clausen 
133554450f59SHans Verkuil 	/* CP has detected a non standard number of lines on the incoming
133654450f59SHans Verkuil 	   video compared to what it is configured to receive by s_dv_timings */
133754450f59SHans Verkuil 	return io_read(sd, 0x12) & 0x01;
133854450f59SHans Verkuil }
133954450f59SHans Verkuil 
134058514625Sjean-michel.hautbois@vodalys.com static inline bool in_free_run(struct v4l2_subdev *sd)
134158514625Sjean-michel.hautbois@vodalys.com {
134258514625Sjean-michel.hautbois@vodalys.com 	return cp_read(sd, 0xff) & 0x10;
134358514625Sjean-michel.hautbois@vodalys.com }
134458514625Sjean-michel.hautbois@vodalys.com 
1345b44b2e06SPablo Anton static int adv76xx_g_input_status(struct v4l2_subdev *sd, u32 *status)
134654450f59SHans Verkuil {
134754450f59SHans Verkuil 	*status = 0;
134854450f59SHans Verkuil 	*status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0;
134954450f59SHans Verkuil 	*status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0;
135058514625Sjean-michel.hautbois@vodalys.com 	if (!in_free_run(sd) && no_lock_cp(sd))
135158514625Sjean-michel.hautbois@vodalys.com 		*status |= is_digital_input(sd) ?
135258514625Sjean-michel.hautbois@vodalys.com 			   V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;
135354450f59SHans Verkuil 
135454450f59SHans Verkuil 	v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status);
135554450f59SHans Verkuil 
135654450f59SHans Verkuil 	return 0;
135754450f59SHans Verkuil }
135854450f59SHans Verkuil 
135954450f59SHans Verkuil /* ----------------------------------------------------------------------- */
136054450f59SHans Verkuil 
136154450f59SHans Verkuil struct stdi_readback {
136254450f59SHans Verkuil 	u16 bl, lcf, lcvs;
136354450f59SHans Verkuil 	u8 hs_pol, vs_pol;
136454450f59SHans Verkuil 	bool interlaced;
136554450f59SHans Verkuil };
136654450f59SHans Verkuil 
136754450f59SHans Verkuil static int stdi2dv_timings(struct v4l2_subdev *sd,
136854450f59SHans Verkuil 		struct stdi_readback *stdi,
136954450f59SHans Verkuil 		struct v4l2_dv_timings *timings)
137054450f59SHans Verkuil {
1371b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1372b44b2e06SPablo Anton 	u32 hfreq = (ADV76XX_FSC * 8) / stdi->bl;
137354450f59SHans Verkuil 	u32 pix_clk;
137454450f59SHans Verkuil 	int i;
137554450f59SHans Verkuil 
1376bd3e275fSJean-Michel Hautbois 	for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
1377bd3e275fSJean-Michel Hautbois 		const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
1378bd3e275fSJean-Michel Hautbois 
1379bd3e275fSJean-Michel Hautbois 		if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i],
13809c41e690SLaurent Pinchart 					   adv76xx_get_dv_timings_cap(sd, -1),
1381bd3e275fSJean-Michel Hautbois 					   adv76xx_check_dv_timings, NULL))
138254450f59SHans Verkuil 			continue;
1383bd3e275fSJean-Michel Hautbois 		if (vtotal(bt) != stdi->lcf + 1)
1384bd3e275fSJean-Michel Hautbois 			continue;
1385bd3e275fSJean-Michel Hautbois 		if (bt->vsync != stdi->lcvs)
138654450f59SHans Verkuil 			continue;
138754450f59SHans Verkuil 
1388bd3e275fSJean-Michel Hautbois 		pix_clk = hfreq * htotal(bt);
138954450f59SHans Verkuil 
1390bd3e275fSJean-Michel Hautbois 		if ((pix_clk < bt->pixelclock + 1000000) &&
1391bd3e275fSJean-Michel Hautbois 		    (pix_clk > bt->pixelclock - 1000000)) {
1392bd3e275fSJean-Michel Hautbois 			*timings = v4l2_dv_timings_presets[i];
139354450f59SHans Verkuil 			return 0;
139454450f59SHans Verkuil 		}
139554450f59SHans Verkuil 	}
139654450f59SHans Verkuil 
13975fea1bb7SPrashant Laddha 	if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0,
139854450f59SHans Verkuil 			(stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
139954450f59SHans Verkuil 			(stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
1400061ddda6SPrashant Laddha 			false, timings))
140154450f59SHans Verkuil 		return 0;
140254450f59SHans Verkuil 	if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs,
140354450f59SHans Verkuil 			(stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
140454450f59SHans Verkuil 			(stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
1405061ddda6SPrashant Laddha 			false, state->aspect_ratio, timings))
140654450f59SHans Verkuil 		return 0;
140754450f59SHans Verkuil 
1408ccbd5bc4SHans Verkuil 	v4l2_dbg(2, debug, sd,
1409ccbd5bc4SHans Verkuil 		"%s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync\n",
1410ccbd5bc4SHans Verkuil 		__func__, stdi->lcvs, stdi->lcf, stdi->bl,
1411ccbd5bc4SHans Verkuil 		stdi->hs_pol, stdi->vs_pol);
141254450f59SHans Verkuil 	return -1;
141354450f59SHans Verkuil }
141454450f59SHans Verkuil 
1415d42010a1SLars-Peter Clausen 
141654450f59SHans Verkuil static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
141754450f59SHans Verkuil {
1418b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1419b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
14204a2ccdd2SLaurent Pinchart 	u8 polarity;
14214a2ccdd2SLaurent Pinchart 
142254450f59SHans Verkuil 	if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
142354450f59SHans Verkuil 		v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__);
142454450f59SHans Verkuil 		return -1;
142554450f59SHans Verkuil 	}
142654450f59SHans Verkuil 
142754450f59SHans Verkuil 	/* read STDI */
142851182a94SLaurent Pinchart 	stdi->bl = cp_read16(sd, 0xb1, 0x3fff);
1429d42010a1SLars-Peter Clausen 	stdi->lcf = cp_read16(sd, info->lcf_reg, 0x7ff);
143054450f59SHans Verkuil 	stdi->lcvs = cp_read(sd, 0xb3) >> 3;
143154450f59SHans Verkuil 	stdi->interlaced = io_read(sd, 0x12) & 0x10;
143254450f59SHans Verkuil 
1433b44b2e06SPablo Anton 	if (adv76xx_has_afe(state)) {
143454450f59SHans Verkuil 		/* read SSPD */
14354a2ccdd2SLaurent Pinchart 		polarity = cp_read(sd, 0xb5);
14364a2ccdd2SLaurent Pinchart 		if ((polarity & 0x03) == 0x01) {
14374a2ccdd2SLaurent Pinchart 			stdi->hs_pol = polarity & 0x10
14384a2ccdd2SLaurent Pinchart 				     ? (polarity & 0x08 ? '+' : '-') : 'x';
14394a2ccdd2SLaurent Pinchart 			stdi->vs_pol = polarity & 0x40
14404a2ccdd2SLaurent Pinchart 				     ? (polarity & 0x20 ? '+' : '-') : 'x';
144154450f59SHans Verkuil 		} else {
144254450f59SHans Verkuil 			stdi->hs_pol = 'x';
144354450f59SHans Verkuil 			stdi->vs_pol = 'x';
144454450f59SHans Verkuil 		}
1445d42010a1SLars-Peter Clausen 	} else {
1446d42010a1SLars-Peter Clausen 		polarity = hdmi_read(sd, 0x05);
1447d42010a1SLars-Peter Clausen 		stdi->hs_pol = polarity & 0x20 ? '+' : '-';
1448d42010a1SLars-Peter Clausen 		stdi->vs_pol = polarity & 0x10 ? '+' : '-';
1449d42010a1SLars-Peter Clausen 	}
145054450f59SHans Verkuil 
145154450f59SHans Verkuil 	if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
145254450f59SHans Verkuil 		v4l2_dbg(2, debug, sd,
145354450f59SHans Verkuil 			"%s: signal lost during readout of STDI/SSPD\n", __func__);
145454450f59SHans Verkuil 		return -1;
145554450f59SHans Verkuil 	}
145654450f59SHans Verkuil 
145754450f59SHans Verkuil 	if (stdi->lcf < 239 || stdi->bl < 8 || stdi->bl == 0x3fff) {
145854450f59SHans Verkuil 		v4l2_dbg(2, debug, sd, "%s: invalid signal\n", __func__);
145954450f59SHans Verkuil 		memset(stdi, 0, sizeof(struct stdi_readback));
146054450f59SHans Verkuil 		return -1;
146154450f59SHans Verkuil 	}
146254450f59SHans Verkuil 
146354450f59SHans Verkuil 	v4l2_dbg(2, debug, sd,
146454450f59SHans Verkuil 		"%s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s\n",
146554450f59SHans Verkuil 		__func__, stdi->lcf, stdi->bl, stdi->lcvs,
146654450f59SHans Verkuil 		stdi->hs_pol, stdi->vs_pol,
146754450f59SHans Verkuil 		stdi->interlaced ? "interlaced" : "progressive");
146854450f59SHans Verkuil 
146954450f59SHans Verkuil 	return 0;
147054450f59SHans Verkuil }
147154450f59SHans Verkuil 
1472b44b2e06SPablo Anton static int adv76xx_enum_dv_timings(struct v4l2_subdev *sd,
147354450f59SHans Verkuil 			struct v4l2_enum_dv_timings *timings)
147454450f59SHans Verkuil {
1475b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1476afec5599SLaurent Pinchart 
1477afec5599SLaurent Pinchart 	if (timings->pad >= state->source_pad)
1478afec5599SLaurent Pinchart 		return -EINVAL;
1479afec5599SLaurent Pinchart 
1480bd3e275fSJean-Michel Hautbois 	return v4l2_enum_dv_timings_cap(timings,
14819c41e690SLaurent Pinchart 		adv76xx_get_dv_timings_cap(sd, timings->pad),
14829c41e690SLaurent Pinchart 		adv76xx_check_dv_timings, NULL);
148354450f59SHans Verkuil }
148454450f59SHans Verkuil 
1485b44b2e06SPablo Anton static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
14867515e096SLaurent Pinchart 			struct v4l2_dv_timings_cap *cap)
1487afec5599SLaurent Pinchart {
1488b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
14899c41e690SLaurent Pinchart 	unsigned int pad = cap->pad;
14907515e096SLaurent Pinchart 
14917515e096SLaurent Pinchart 	if (cap->pad >= state->source_pad)
14927515e096SLaurent Pinchart 		return -EINVAL;
14937515e096SLaurent Pinchart 
14949c41e690SLaurent Pinchart 	*cap = *adv76xx_get_dv_timings_cap(sd, pad);
14959c41e690SLaurent Pinchart 	cap->pad = pad;
14969c41e690SLaurent Pinchart 
1497afec5599SLaurent Pinchart 	return 0;
1498afec5599SLaurent Pinchart }
1499afec5599SLaurent Pinchart 
150054450f59SHans Verkuil /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
1501b44b2e06SPablo Anton    if the format is listed in adv76xx_timings[] */
1502b44b2e06SPablo Anton static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
150354450f59SHans Verkuil 		struct v4l2_dv_timings *timings)
150454450f59SHans Verkuil {
15059c41e690SLaurent Pinchart 	v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd, -1),
1506bd3e275fSJean-Michel Hautbois 				 is_digital_input(sd) ? 250000 : 1000000,
1507bd3e275fSJean-Michel Hautbois 				 adv76xx_check_dv_timings, NULL);
150854450f59SHans Verkuil }
150954450f59SHans Verkuil 
1510d42010a1SLars-Peter Clausen static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd)
1511d42010a1SLars-Peter Clausen {
1512d42010a1SLars-Peter Clausen 	unsigned int freq;
1513d42010a1SLars-Peter Clausen 	int a, b;
1514d42010a1SLars-Peter Clausen 
1515d42010a1SLars-Peter Clausen 	a = hdmi_read(sd, 0x06);
1516d42010a1SLars-Peter Clausen 	b = hdmi_read(sd, 0x3b);
1517d42010a1SLars-Peter Clausen 	if (a < 0 || b < 0)
1518d42010a1SLars-Peter Clausen 		return 0;
1519d42010a1SLars-Peter Clausen 	freq =  a * 1000000 + ((b & 0x30) >> 4) * 250000;
1520d42010a1SLars-Peter Clausen 
1521d42010a1SLars-Peter Clausen 	if (is_hdmi(sd)) {
1522d42010a1SLars-Peter Clausen 		/* adjust for deep color mode */
1523d42010a1SLars-Peter Clausen 		unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8;
1524d42010a1SLars-Peter Clausen 
1525d42010a1SLars-Peter Clausen 		freq = freq * 8 / bits_per_channel;
1526d42010a1SLars-Peter Clausen 	}
1527d42010a1SLars-Peter Clausen 
1528d42010a1SLars-Peter Clausen 	return freq;
1529d42010a1SLars-Peter Clausen }
1530d42010a1SLars-Peter Clausen 
1531d42010a1SLars-Peter Clausen static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd)
1532d42010a1SLars-Peter Clausen {
1533d42010a1SLars-Peter Clausen 	int a, b;
1534d42010a1SLars-Peter Clausen 
1535d42010a1SLars-Peter Clausen 	a = hdmi_read(sd, 0x51);
1536d42010a1SLars-Peter Clausen 	b = hdmi_read(sd, 0x52);
1537d42010a1SLars-Peter Clausen 	if (a < 0 || b < 0)
1538d42010a1SLars-Peter Clausen 		return 0;
1539d42010a1SLars-Peter Clausen 	return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128;
1540d42010a1SLars-Peter Clausen }
1541d42010a1SLars-Peter Clausen 
1542b44b2e06SPablo Anton static int adv76xx_query_dv_timings(struct v4l2_subdev *sd,
154354450f59SHans Verkuil 			struct v4l2_dv_timings *timings)
154454450f59SHans Verkuil {
1545b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1546b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
154754450f59SHans Verkuil 	struct v4l2_bt_timings *bt = &timings->bt;
154854450f59SHans Verkuil 	struct stdi_readback stdi;
154954450f59SHans Verkuil 
155054450f59SHans Verkuil 	if (!timings)
155154450f59SHans Verkuil 		return -EINVAL;
155254450f59SHans Verkuil 
155354450f59SHans Verkuil 	memset(timings, 0, sizeof(struct v4l2_dv_timings));
155454450f59SHans Verkuil 
155554450f59SHans Verkuil 	if (no_signal(sd)) {
15561e0b9156SMartin Bugge 		state->restart_stdi_once = true;
155754450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__);
155854450f59SHans Verkuil 		return -ENOLINK;
155954450f59SHans Verkuil 	}
156054450f59SHans Verkuil 
156154450f59SHans Verkuil 	/* read STDI */
156254450f59SHans Verkuil 	if (read_stdi(sd, &stdi)) {
156354450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: STDI/SSPD not locked\n", __func__);
156454450f59SHans Verkuil 		return -ENOLINK;
156554450f59SHans Verkuil 	}
156654450f59SHans Verkuil 	bt->interlaced = stdi.interlaced ?
156754450f59SHans Verkuil 		V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
156854450f59SHans Verkuil 
15694a31a93aSMats Randgaard 	if (is_digital_input(sd)) {
1570827c1f52SHans Verkuil 		bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
1571827c1f52SHans Verkuil 		u8 vic = 0;
1572827c1f52SHans Verkuil 		u32 w, h;
1573827c1f52SHans Verkuil 
1574827c1f52SHans Verkuil 		w = hdmi_read16(sd, 0x07, info->linewidth_mask);
1575827c1f52SHans Verkuil 		h = hdmi_read16(sd, 0x09, info->field0_height_mask);
1576827c1f52SHans Verkuil 
1577827c1f52SHans Verkuil 		if (hdmi_signal && (io_read(sd, 0x60) & 1))
1578827c1f52SHans Verkuil 			vic = infoframe_read(sd, 0x04);
1579827c1f52SHans Verkuil 
1580827c1f52SHans Verkuil 		if (vic && v4l2_find_dv_timings_cea861_vic(timings, vic) &&
1581827c1f52SHans Verkuil 		    bt->width == w && bt->height == h)
1582827c1f52SHans Verkuil 			goto found;
1583827c1f52SHans Verkuil 
158454450f59SHans Verkuil 		timings->type = V4L2_DV_BT_656_1120;
158554450f59SHans Verkuil 
1586827c1f52SHans Verkuil 		bt->width = w;
1587827c1f52SHans Verkuil 		bt->height = h;
1588d42010a1SLars-Peter Clausen 		bt->pixelclock = info->read_hdmi_pixelclock(sd);
15895380baafSjean-michel.hautbois@vodalys.com 		bt->hfrontporch = hdmi_read16(sd, 0x20, info->hfrontporch_mask);
15905380baafSjean-michel.hautbois@vodalys.com 		bt->hsync = hdmi_read16(sd, 0x22, info->hsync_mask);
15915380baafSjean-michel.hautbois@vodalys.com 		bt->hbackporch = hdmi_read16(sd, 0x24, info->hbackporch_mask);
15925380baafSjean-michel.hautbois@vodalys.com 		bt->vfrontporch = hdmi_read16(sd, 0x2a,
15935380baafSjean-michel.hautbois@vodalys.com 			info->field0_vfrontporch_mask) / 2;
15945380baafSjean-michel.hautbois@vodalys.com 		bt->vsync = hdmi_read16(sd, 0x2e, info->field0_vsync_mask) / 2;
15955380baafSjean-michel.hautbois@vodalys.com 		bt->vbackporch = hdmi_read16(sd, 0x32,
15965380baafSjean-michel.hautbois@vodalys.com 			info->field0_vbackporch_mask) / 2;
159754450f59SHans Verkuil 		bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) |
159854450f59SHans Verkuil 			((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0);
159954450f59SHans Verkuil 		if (bt->interlaced == V4L2_DV_INTERLACED) {
16005380baafSjean-michel.hautbois@vodalys.com 			bt->height += hdmi_read16(sd, 0x0b,
16015380baafSjean-michel.hautbois@vodalys.com 				info->field1_height_mask);
16025380baafSjean-michel.hautbois@vodalys.com 			bt->il_vfrontporch = hdmi_read16(sd, 0x2c,
16035380baafSjean-michel.hautbois@vodalys.com 				info->field1_vfrontporch_mask) / 2;
16045380baafSjean-michel.hautbois@vodalys.com 			bt->il_vsync = hdmi_read16(sd, 0x30,
16055380baafSjean-michel.hautbois@vodalys.com 				info->field1_vsync_mask) / 2;
16065380baafSjean-michel.hautbois@vodalys.com 			bt->il_vbackporch = hdmi_read16(sd, 0x34,
16075380baafSjean-michel.hautbois@vodalys.com 				info->field1_vbackporch_mask) / 2;
160854450f59SHans Verkuil 		}
1609b44b2e06SPablo Anton 		adv76xx_fill_optional_dv_timings_fields(sd, timings);
161054450f59SHans Verkuil 	} else {
161154450f59SHans Verkuil 		/* find format
161280939647SHans Verkuil 		 * Since LCVS values are inaccurate [REF_03, p. 275-276],
161354450f59SHans Verkuil 		 * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails.
161454450f59SHans Verkuil 		 */
161554450f59SHans Verkuil 		if (!stdi2dv_timings(sd, &stdi, timings))
161654450f59SHans Verkuil 			goto found;
161754450f59SHans Verkuil 		stdi.lcvs += 1;
161854450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs);
161954450f59SHans Verkuil 		if (!stdi2dv_timings(sd, &stdi, timings))
162054450f59SHans Verkuil 			goto found;
162154450f59SHans Verkuil 		stdi.lcvs -= 2;
162254450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs);
162354450f59SHans Verkuil 		if (stdi2dv_timings(sd, &stdi, timings)) {
1624cf9afb1dSHans Verkuil 			/*
1625cf9afb1dSHans Verkuil 			 * The STDI block may measure wrong values, especially
1626cf9afb1dSHans Verkuil 			 * for lcvs and lcf. If the driver can not find any
1627cf9afb1dSHans Verkuil 			 * valid timing, the STDI block is restarted to measure
1628cf9afb1dSHans Verkuil 			 * the video timings again. The function will return an
1629cf9afb1dSHans Verkuil 			 * error, but the restart of STDI will generate a new
1630cf9afb1dSHans Verkuil 			 * STDI interrupt and the format detection process will
1631cf9afb1dSHans Verkuil 			 * restart.
1632cf9afb1dSHans Verkuil 			 */
1633cf9afb1dSHans Verkuil 			if (state->restart_stdi_once) {
1634cf9afb1dSHans Verkuil 				v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__);
1635cf9afb1dSHans Verkuil 				/* TODO restart STDI for Sync Channel 2 */
1636cf9afb1dSHans Verkuil 				/* enter one-shot mode */
163722d97e56SLaurent Pinchart 				cp_write_clr_set(sd, 0x86, 0x06, 0x00);
1638cf9afb1dSHans Verkuil 				/* trigger STDI restart */
163922d97e56SLaurent Pinchart 				cp_write_clr_set(sd, 0x86, 0x06, 0x04);
1640cf9afb1dSHans Verkuil 				/* reset to continuous mode */
164122d97e56SLaurent Pinchart 				cp_write_clr_set(sd, 0x86, 0x06, 0x02);
1642cf9afb1dSHans Verkuil 				state->restart_stdi_once = false;
1643cf9afb1dSHans Verkuil 				return -ENOLINK;
1644cf9afb1dSHans Verkuil 			}
164554450f59SHans Verkuil 			v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__);
164654450f59SHans Verkuil 			return -ERANGE;
164754450f59SHans Verkuil 		}
1648cf9afb1dSHans Verkuil 		state->restart_stdi_once = true;
164954450f59SHans Verkuil 	}
165054450f59SHans Verkuil found:
165154450f59SHans Verkuil 
165254450f59SHans Verkuil 	if (no_signal(sd)) {
165354450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: signal lost during readout\n", __func__);
165454450f59SHans Verkuil 		memset(timings, 0, sizeof(struct v4l2_dv_timings));
165554450f59SHans Verkuil 		return -ENOLINK;
165654450f59SHans Verkuil 	}
165754450f59SHans Verkuil 
16584a31a93aSMats Randgaard 	if ((is_analog_input(sd) && bt->pixelclock > 170000000) ||
16594a31a93aSMats Randgaard 			(is_digital_input(sd) && bt->pixelclock > 225000000)) {
166054450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",
166154450f59SHans Verkuil 				__func__, (u32)bt->pixelclock);
166254450f59SHans Verkuil 		return -ERANGE;
166354450f59SHans Verkuil 	}
166454450f59SHans Verkuil 
166554450f59SHans Verkuil 	if (debug > 1)
1666b44b2e06SPablo Anton 		v4l2_print_dv_timings(sd->name, "adv76xx_query_dv_timings: ",
166711d034c8SHans Verkuil 				      timings, true);
166854450f59SHans Verkuil 
166954450f59SHans Verkuil 	return 0;
167054450f59SHans Verkuil }
167154450f59SHans Verkuil 
1672b44b2e06SPablo Anton static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
167354450f59SHans Verkuil 		struct v4l2_dv_timings *timings)
167454450f59SHans Verkuil {
1675b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
167654450f59SHans Verkuil 	struct v4l2_bt_timings *bt;
1677ccbd5bc4SHans Verkuil 	int err;
167854450f59SHans Verkuil 
167954450f59SHans Verkuil 	if (!timings)
168054450f59SHans Verkuil 		return -EINVAL;
168154450f59SHans Verkuil 
168285f9e06cSHans Verkuil 	if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) {
1683d48eb48cSMats Randgaard 		v4l2_dbg(1, debug, sd, "%s: no change\n", __func__);
1684d48eb48cSMats Randgaard 		return 0;
1685d48eb48cSMats Randgaard 	}
1686d48eb48cSMats Randgaard 
168754450f59SHans Verkuil 	bt = &timings->bt;
168854450f59SHans Verkuil 
16899c41e690SLaurent Pinchart 	if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd, -1),
1690bd3e275fSJean-Michel Hautbois 				   adv76xx_check_dv_timings, NULL))
169154450f59SHans Verkuil 		return -ERANGE;
1692ccbd5bc4SHans Verkuil 
1693b44b2e06SPablo Anton 	adv76xx_fill_optional_dv_timings_fields(sd, timings);
169454450f59SHans Verkuil 
169554450f59SHans Verkuil 	state->timings = *timings;
169654450f59SHans Verkuil 
169722d97e56SLaurent Pinchart 	cp_write_clr_set(sd, 0x91, 0x40, bt->interlaced ? 0x40 : 0x00);
1698ccbd5bc4SHans Verkuil 
1699ccbd5bc4SHans Verkuil 	/* Use prim_mode and vid_std when available */
1700ccbd5bc4SHans Verkuil 	err = configure_predefined_video_timings(sd, timings);
1701ccbd5bc4SHans Verkuil 	if (err) {
1702ccbd5bc4SHans Verkuil 		/* custom settings when the video format
1703ccbd5bc4SHans Verkuil 		 does not have prim_mode/vid_std */
1704ccbd5bc4SHans Verkuil 		configure_custom_video_timings(sd, bt);
1705ccbd5bc4SHans Verkuil 	}
170654450f59SHans Verkuil 
170754450f59SHans Verkuil 	set_rgb_quantization_range(sd);
170854450f59SHans Verkuil 
170954450f59SHans Verkuil 	if (debug > 1)
1710b44b2e06SPablo Anton 		v4l2_print_dv_timings(sd->name, "adv76xx_s_dv_timings: ",
171111d034c8SHans Verkuil 				      timings, true);
171254450f59SHans Verkuil 	return 0;
171354450f59SHans Verkuil }
171454450f59SHans Verkuil 
1715b44b2e06SPablo Anton static int adv76xx_g_dv_timings(struct v4l2_subdev *sd,
171654450f59SHans Verkuil 		struct v4l2_dv_timings *timings)
171754450f59SHans Verkuil {
1718b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
171954450f59SHans Verkuil 
172054450f59SHans Verkuil 	*timings = state->timings;
172154450f59SHans Verkuil 	return 0;
172254450f59SHans Verkuil }
172354450f59SHans Verkuil 
1724d42010a1SLars-Peter Clausen static void adv7604_set_termination(struct v4l2_subdev *sd, bool enable)
1725d42010a1SLars-Peter Clausen {
1726d42010a1SLars-Peter Clausen 	hdmi_write(sd, 0x01, enable ? 0x00 : 0x78);
1727d42010a1SLars-Peter Clausen }
1728d42010a1SLars-Peter Clausen 
1729d42010a1SLars-Peter Clausen static void adv7611_set_termination(struct v4l2_subdev *sd, bool enable)
1730d42010a1SLars-Peter Clausen {
1731d42010a1SLars-Peter Clausen 	hdmi_write(sd, 0x83, enable ? 0xfe : 0xff);
1732d42010a1SLars-Peter Clausen }
1733d42010a1SLars-Peter Clausen 
17346b0d5d34SHans Verkuil static void enable_input(struct v4l2_subdev *sd)
173554450f59SHans Verkuil {
1736b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
17376b0d5d34SHans Verkuil 
17384a31a93aSMats Randgaard 	if (is_analog_input(sd)) {
173954450f59SHans Verkuil 		io_write(sd, 0x15, 0xb0);   /* Disable Tristate of Pins (no audio) */
17404a31a93aSMats Randgaard 	} else if (is_digital_input(sd)) {
174122d97e56SLaurent Pinchart 		hdmi_write_clr_set(sd, 0x00, 0x03, state->selected_input);
1742d42010a1SLars-Peter Clausen 		state->info->set_termination(sd, true);
174354450f59SHans Verkuil 		io_write(sd, 0x15, 0xa0);   /* Disable Tristate of Pins */
174422d97e56SLaurent Pinchart 		hdmi_write_clr_set(sd, 0x1a, 0x10, 0x00); /* Unmute audio */
17454a31a93aSMats Randgaard 	} else {
17464a31a93aSMats Randgaard 		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
17474a31a93aSMats Randgaard 				__func__, state->selected_input);
174854450f59SHans Verkuil 	}
174954450f59SHans Verkuil }
175054450f59SHans Verkuil 
175154450f59SHans Verkuil static void disable_input(struct v4l2_subdev *sd)
175254450f59SHans Verkuil {
1753b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1754d42010a1SLars-Peter Clausen 
175522d97e56SLaurent Pinchart 	hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */
17565474b983SMats Randgaard 	msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */
175754450f59SHans Verkuil 	io_write(sd, 0x15, 0xbe);   /* Tristate all outputs from video core */
1758d42010a1SLars-Peter Clausen 	state->info->set_termination(sd, false);
175954450f59SHans Verkuil }
176054450f59SHans Verkuil 
17616b0d5d34SHans Verkuil static void select_input(struct v4l2_subdev *sd)
176254450f59SHans Verkuil {
1763b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1764b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
176554450f59SHans Verkuil 
17664a31a93aSMats Randgaard 	if (is_analog_input(sd)) {
1767b44b2e06SPablo Anton 		adv76xx_write_reg_seq(sd, info->recommended_settings[0]);
176854450f59SHans Verkuil 
176954450f59SHans Verkuil 		afe_write(sd, 0x00, 0x08); /* power up ADC */
177054450f59SHans Verkuil 		afe_write(sd, 0x01, 0x06); /* power up Analog Front End */
177154450f59SHans Verkuil 		afe_write(sd, 0xc8, 0x00); /* phase control */
17724a31a93aSMats Randgaard 	} else if (is_digital_input(sd)) {
17734a31a93aSMats Randgaard 		hdmi_write(sd, 0x00, state->selected_input & 0x03);
177454450f59SHans Verkuil 
1775b44b2e06SPablo Anton 		adv76xx_write_reg_seq(sd, info->recommended_settings[1]);
177654450f59SHans Verkuil 
1777b44b2e06SPablo Anton 		if (adv76xx_has_afe(state)) {
177854450f59SHans Verkuil 			afe_write(sd, 0x00, 0xff); /* power down ADC */
177954450f59SHans Verkuil 			afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */
178054450f59SHans Verkuil 			afe_write(sd, 0xc8, 0x40); /* phase control */
1781d42010a1SLars-Peter Clausen 		}
178254450f59SHans Verkuil 
178354450f59SHans Verkuil 		cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */
178454450f59SHans Verkuil 		cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
178554450f59SHans Verkuil 		cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */
17864a31a93aSMats Randgaard 	} else {
17874a31a93aSMats Randgaard 		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
17884a31a93aSMats Randgaard 				__func__, state->selected_input);
178954450f59SHans Verkuil 	}
179054450f59SHans Verkuil }
179154450f59SHans Verkuil 
1792b44b2e06SPablo Anton static int adv76xx_s_routing(struct v4l2_subdev *sd,
179354450f59SHans Verkuil 		u32 input, u32 output, u32 config)
179454450f59SHans Verkuil {
1795b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
179654450f59SHans Verkuil 
1797ff4f80fdSMats Randgaard 	v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d",
1798ff4f80fdSMats Randgaard 			__func__, input, state->selected_input);
1799ff4f80fdSMats Randgaard 
1800ff4f80fdSMats Randgaard 	if (input == state->selected_input)
1801ff4f80fdSMats Randgaard 		return 0;
180254450f59SHans Verkuil 
1803d42010a1SLars-Peter Clausen 	if (input > state->info->max_port)
1804d42010a1SLars-Peter Clausen 		return -EINVAL;
1805d42010a1SLars-Peter Clausen 
18064a31a93aSMats Randgaard 	state->selected_input = input;
180754450f59SHans Verkuil 
180854450f59SHans Verkuil 	disable_input(sd);
18096b0d5d34SHans Verkuil 	select_input(sd);
18106b0d5d34SHans Verkuil 	enable_input(sd);
181154450f59SHans Verkuil 
18126f5bcfc3SLars-Peter Clausen 	v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt);
18136f5bcfc3SLars-Peter Clausen 
181454450f59SHans Verkuil 	return 0;
181554450f59SHans Verkuil }
181654450f59SHans Verkuil 
1817b44b2e06SPablo Anton static int adv76xx_enum_mbus_code(struct v4l2_subdev *sd,
1818f7234138SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
1819539b33b0SLaurent Pinchart 				  struct v4l2_subdev_mbus_code_enum *code)
182054450f59SHans Verkuil {
1821b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
182254450f59SHans Verkuil 
1823539b33b0SLaurent Pinchart 	if (code->index >= state->info->nformats)
1824539b33b0SLaurent Pinchart 		return -EINVAL;
1825539b33b0SLaurent Pinchart 
1826539b33b0SLaurent Pinchart 	code->code = state->info->formats[code->index].code;
1827539b33b0SLaurent Pinchart 
1828539b33b0SLaurent Pinchart 	return 0;
1829539b33b0SLaurent Pinchart }
1830539b33b0SLaurent Pinchart 
1831b44b2e06SPablo Anton static void adv76xx_fill_format(struct adv76xx_state *state,
1832539b33b0SLaurent Pinchart 				struct v4l2_mbus_framefmt *format)
1833539b33b0SLaurent Pinchart {
1834539b33b0SLaurent Pinchart 	memset(format, 0, sizeof(*format));
1835539b33b0SLaurent Pinchart 
1836539b33b0SLaurent Pinchart 	format->width = state->timings.bt.width;
1837539b33b0SLaurent Pinchart 	format->height = state->timings.bt.height;
1838539b33b0SLaurent Pinchart 	format->field = V4L2_FIELD_NONE;
1839680fee04SHans Verkuil 	format->colorspace = V4L2_COLORSPACE_SRGB;
1840539b33b0SLaurent Pinchart 
1841680fee04SHans Verkuil 	if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO)
1842539b33b0SLaurent Pinchart 		format->colorspace = (state->timings.bt.height <= 576) ?
184354450f59SHans Verkuil 			V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
184454450f59SHans Verkuil }
1845539b33b0SLaurent Pinchart 
1846539b33b0SLaurent Pinchart /*
1847539b33b0SLaurent Pinchart  * Compute the op_ch_sel value required to obtain on the bus the component order
1848539b33b0SLaurent Pinchart  * corresponding to the selected format taking into account bus reordering
1849539b33b0SLaurent Pinchart  * applied by the board at the output of the device.
1850539b33b0SLaurent Pinchart  *
1851539b33b0SLaurent Pinchart  * The following table gives the op_ch_value from the format component order
1852539b33b0SLaurent Pinchart  * (expressed as op_ch_sel value in column) and the bus reordering (expressed as
1853b44b2e06SPablo Anton  * adv76xx_bus_order value in row).
1854539b33b0SLaurent Pinchart  *
1855539b33b0SLaurent Pinchart  *           |	GBR(0)	GRB(1)	BGR(2)	RGB(3)	BRG(4)	RBG(5)
1856539b33b0SLaurent Pinchart  * ----------+-------------------------------------------------
1857539b33b0SLaurent Pinchart  * RGB (NOP) |	GBR	GRB	BGR	RGB	BRG	RBG
1858539b33b0SLaurent Pinchart  * GRB (1-2) |	BGR	RGB	GBR	GRB	RBG	BRG
1859539b33b0SLaurent Pinchart  * RBG (2-3) |	GRB	GBR	BRG	RBG	BGR	RGB
1860539b33b0SLaurent Pinchart  * BGR (1-3) |	RBG	BRG	RGB	BGR	GRB	GBR
1861539b33b0SLaurent Pinchart  * BRG (ROR) |	BRG	RBG	GRB	GBR	RGB	BGR
1862539b33b0SLaurent Pinchart  * GBR (ROL) |	RGB	BGR	RBG	BRG	GBR	GRB
1863539b33b0SLaurent Pinchart  */
1864b44b2e06SPablo Anton static unsigned int adv76xx_op_ch_sel(struct adv76xx_state *state)
1865539b33b0SLaurent Pinchart {
1866539b33b0SLaurent Pinchart #define _SEL(a,b,c,d,e,f)	{ \
1867b44b2e06SPablo Anton 	ADV76XX_OP_CH_SEL_##a, ADV76XX_OP_CH_SEL_##b, ADV76XX_OP_CH_SEL_##c, \
1868b44b2e06SPablo Anton 	ADV76XX_OP_CH_SEL_##d, ADV76XX_OP_CH_SEL_##e, ADV76XX_OP_CH_SEL_##f }
1869539b33b0SLaurent Pinchart #define _BUS(x)			[ADV7604_BUS_ORDER_##x]
1870539b33b0SLaurent Pinchart 
1871539b33b0SLaurent Pinchart 	static const unsigned int op_ch_sel[6][6] = {
1872539b33b0SLaurent Pinchart 		_BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG),
1873539b33b0SLaurent Pinchart 		_BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG),
1874539b33b0SLaurent Pinchart 		_BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB),
1875539b33b0SLaurent Pinchart 		_BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR),
1876539b33b0SLaurent Pinchart 		_BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR),
1877539b33b0SLaurent Pinchart 		_BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB),
1878539b33b0SLaurent Pinchart 	};
1879539b33b0SLaurent Pinchart 
1880539b33b0SLaurent Pinchart 	return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5];
1881539b33b0SLaurent Pinchart }
1882539b33b0SLaurent Pinchart 
1883b44b2e06SPablo Anton static void adv76xx_setup_format(struct adv76xx_state *state)
1884539b33b0SLaurent Pinchart {
1885539b33b0SLaurent Pinchart 	struct v4l2_subdev *sd = &state->sd;
1886539b33b0SLaurent Pinchart 
188722d97e56SLaurent Pinchart 	io_write_clr_set(sd, 0x02, 0x02,
1888b44b2e06SPablo Anton 			state->format->rgb_out ? ADV76XX_RGB_OUT : 0);
1889539b33b0SLaurent Pinchart 	io_write(sd, 0x03, state->format->op_format_sel |
1890539b33b0SLaurent Pinchart 		 state->pdata.op_format_mode_sel);
1891b44b2e06SPablo Anton 	io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state));
189222d97e56SLaurent Pinchart 	io_write_clr_set(sd, 0x05, 0x01,
1893b44b2e06SPablo Anton 			state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0);
1894fd74246dSHans Verkuil 	set_rgb_quantization_range(sd);
1895539b33b0SLaurent Pinchart }
1896539b33b0SLaurent Pinchart 
1897f7234138SHans Verkuil static int adv76xx_get_format(struct v4l2_subdev *sd,
1898f7234138SHans Verkuil 			      struct v4l2_subdev_pad_config *cfg,
1899539b33b0SLaurent Pinchart 			      struct v4l2_subdev_format *format)
1900539b33b0SLaurent Pinchart {
1901b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1902539b33b0SLaurent Pinchart 
1903539b33b0SLaurent Pinchart 	if (format->pad != state->source_pad)
1904539b33b0SLaurent Pinchart 		return -EINVAL;
1905539b33b0SLaurent Pinchart 
1906b44b2e06SPablo Anton 	adv76xx_fill_format(state, &format->format);
1907539b33b0SLaurent Pinchart 
1908539b33b0SLaurent Pinchart 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1909539b33b0SLaurent Pinchart 		struct v4l2_mbus_framefmt *fmt;
1910539b33b0SLaurent Pinchart 
1911f7234138SHans Verkuil 		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
1912539b33b0SLaurent Pinchart 		format->format.code = fmt->code;
1913539b33b0SLaurent Pinchart 	} else {
1914539b33b0SLaurent Pinchart 		format->format.code = state->format->code;
1915539b33b0SLaurent Pinchart 	}
1916539b33b0SLaurent Pinchart 
1917539b33b0SLaurent Pinchart 	return 0;
1918539b33b0SLaurent Pinchart }
1919539b33b0SLaurent Pinchart 
1920b7d4d2f8SUlrich Hecht static int adv76xx_get_selection(struct v4l2_subdev *sd,
1921b7d4d2f8SUlrich Hecht 				 struct v4l2_subdev_pad_config *cfg,
1922b7d4d2f8SUlrich Hecht 				 struct v4l2_subdev_selection *sel)
1923b7d4d2f8SUlrich Hecht {
1924b7d4d2f8SUlrich Hecht 	struct adv76xx_state *state = to_state(sd);
1925b7d4d2f8SUlrich Hecht 
1926b7d4d2f8SUlrich Hecht 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
1927b7d4d2f8SUlrich Hecht 		return -EINVAL;
1928b7d4d2f8SUlrich Hecht 	/* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */
1929b7d4d2f8SUlrich Hecht 	if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS)
1930b7d4d2f8SUlrich Hecht 		return -EINVAL;
1931b7d4d2f8SUlrich Hecht 
1932b7d4d2f8SUlrich Hecht 	sel->r.left	= 0;
1933b7d4d2f8SUlrich Hecht 	sel->r.top	= 0;
1934b7d4d2f8SUlrich Hecht 	sel->r.width	= state->timings.bt.width;
1935b7d4d2f8SUlrich Hecht 	sel->r.height	= state->timings.bt.height;
1936b7d4d2f8SUlrich Hecht 
1937b7d4d2f8SUlrich Hecht 	return 0;
1938b7d4d2f8SUlrich Hecht }
1939b7d4d2f8SUlrich Hecht 
1940f7234138SHans Verkuil static int adv76xx_set_format(struct v4l2_subdev *sd,
1941f7234138SHans Verkuil 			      struct v4l2_subdev_pad_config *cfg,
1942539b33b0SLaurent Pinchart 			      struct v4l2_subdev_format *format)
1943539b33b0SLaurent Pinchart {
1944b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
1945b44b2e06SPablo Anton 	const struct adv76xx_format_info *info;
1946539b33b0SLaurent Pinchart 
1947539b33b0SLaurent Pinchart 	if (format->pad != state->source_pad)
1948539b33b0SLaurent Pinchart 		return -EINVAL;
1949539b33b0SLaurent Pinchart 
1950b44b2e06SPablo Anton 	info = adv76xx_format_info(state, format->format.code);
1951539b33b0SLaurent Pinchart 	if (info == NULL)
1952b44b2e06SPablo Anton 		info = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
1953539b33b0SLaurent Pinchart 
1954b44b2e06SPablo Anton 	adv76xx_fill_format(state, &format->format);
1955539b33b0SLaurent Pinchart 	format->format.code = info->code;
1956539b33b0SLaurent Pinchart 
1957539b33b0SLaurent Pinchart 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1958539b33b0SLaurent Pinchart 		struct v4l2_mbus_framefmt *fmt;
1959539b33b0SLaurent Pinchart 
1960f7234138SHans Verkuil 		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
1961539b33b0SLaurent Pinchart 		fmt->code = format->format.code;
1962539b33b0SLaurent Pinchart 	} else {
1963539b33b0SLaurent Pinchart 		state->format = info;
1964b44b2e06SPablo Anton 		adv76xx_setup_format(state);
1965539b33b0SLaurent Pinchart 	}
1966539b33b0SLaurent Pinchart 
196754450f59SHans Verkuil 	return 0;
196854450f59SHans Verkuil }
196954450f59SHans Verkuil 
197041a52373SHans Verkuil #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
197141a52373SHans Verkuil static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
197241a52373SHans Verkuil {
197341a52373SHans Verkuil 	struct adv76xx_state *state = to_state(sd);
197441a52373SHans Verkuil 
197541a52373SHans Verkuil 	if ((cec_read(sd, 0x11) & 0x01) == 0) {
197641a52373SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
197741a52373SHans Verkuil 		return;
197841a52373SHans Verkuil 	}
197941a52373SHans Verkuil 
198041a52373SHans Verkuil 	if (tx_raw_status & 0x02) {
198141a52373SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
198241a52373SHans Verkuil 			 __func__);
198341a52373SHans Verkuil 		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
198441a52373SHans Verkuil 				  1, 0, 0, 0);
198541a52373SHans Verkuil 	}
198641a52373SHans Verkuil 	if (tx_raw_status & 0x04) {
198741a52373SHans Verkuil 		u8 status;
198841a52373SHans Verkuil 		u8 nack_cnt;
198941a52373SHans Verkuil 		u8 low_drive_cnt;
199041a52373SHans Verkuil 
199141a52373SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
199241a52373SHans Verkuil 		/*
199341a52373SHans Verkuil 		 * We set this status bit since this hardware performs
199441a52373SHans Verkuil 		 * retransmissions.
199541a52373SHans Verkuil 		 */
199641a52373SHans Verkuil 		status = CEC_TX_STATUS_MAX_RETRIES;
199741a52373SHans Verkuil 		nack_cnt = cec_read(sd, 0x14) & 0xf;
199841a52373SHans Verkuil 		if (nack_cnt)
199941a52373SHans Verkuil 			status |= CEC_TX_STATUS_NACK;
200041a52373SHans Verkuil 		low_drive_cnt = cec_read(sd, 0x14) >> 4;
200141a52373SHans Verkuil 		if (low_drive_cnt)
200241a52373SHans Verkuil 			status |= CEC_TX_STATUS_LOW_DRIVE;
200341a52373SHans Verkuil 		cec_transmit_done(state->cec_adap, status,
200441a52373SHans Verkuil 				  0, nack_cnt, low_drive_cnt, 0);
200541a52373SHans Verkuil 		return;
200641a52373SHans Verkuil 	}
200741a52373SHans Verkuil 	if (tx_raw_status & 0x01) {
200841a52373SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
200941a52373SHans Verkuil 		cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
201041a52373SHans Verkuil 		return;
201141a52373SHans Verkuil 	}
201241a52373SHans Verkuil }
201341a52373SHans Verkuil 
201441a52373SHans Verkuil static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
201541a52373SHans Verkuil {
201641a52373SHans Verkuil 	struct adv76xx_state *state = to_state(sd);
201741a52373SHans Verkuil 	u8 cec_irq;
201841a52373SHans Verkuil 
201941a52373SHans Verkuil 	/* cec controller */
202041a52373SHans Verkuil 	cec_irq = io_read(sd, 0x4d) & 0x0f;
202141a52373SHans Verkuil 	if (!cec_irq)
202241a52373SHans Verkuil 		return;
202341a52373SHans Verkuil 
202441a52373SHans Verkuil 	v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
202541a52373SHans Verkuil 	adv76xx_cec_tx_raw_status(sd, cec_irq);
202641a52373SHans Verkuil 	if (cec_irq & 0x08) {
202741a52373SHans Verkuil 		struct cec_msg msg;
202841a52373SHans Verkuil 
202941a52373SHans Verkuil 		msg.len = cec_read(sd, 0x25) & 0x1f;
203041a52373SHans Verkuil 		if (msg.len > 16)
203141a52373SHans Verkuil 			msg.len = 16;
203241a52373SHans Verkuil 
203341a52373SHans Verkuil 		if (msg.len) {
203441a52373SHans Verkuil 			u8 i;
203541a52373SHans Verkuil 
203641a52373SHans Verkuil 			for (i = 0; i < msg.len; i++)
203741a52373SHans Verkuil 				msg.msg[i] = cec_read(sd, i + 0x15);
203841a52373SHans Verkuil 			cec_write(sd, 0x26, 0x01); /* re-enable rx */
203941a52373SHans Verkuil 			cec_received_msg(state->cec_adap, &msg);
204041a52373SHans Verkuil 		}
204141a52373SHans Verkuil 	}
204241a52373SHans Verkuil 
204341a52373SHans Verkuil 	/* note: the bit order is swapped between 0x4d and 0x4e */
204441a52373SHans Verkuil 	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
204541a52373SHans Verkuil 		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
204641a52373SHans Verkuil 	io_write(sd, 0x4e, cec_irq);
204741a52373SHans Verkuil 
204841a52373SHans Verkuil 	if (handled)
204941a52373SHans Verkuil 		*handled = true;
205041a52373SHans Verkuil }
205141a52373SHans Verkuil 
205241a52373SHans Verkuil static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
205341a52373SHans Verkuil {
2054eb10790fSJose Abreu 	struct adv76xx_state *state = cec_get_drvdata(adap);
205541a52373SHans Verkuil 	struct v4l2_subdev *sd = &state->sd;
205641a52373SHans Verkuil 
205741a52373SHans Verkuil 	if (!state->cec_enabled_adap && enable) {
205841a52373SHans Verkuil 		cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
205941a52373SHans Verkuil 		cec_write(sd, 0x2c, 0x01);	/* cec soft reset */
206041a52373SHans Verkuil 		cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
206141a52373SHans Verkuil 		/* enabled irqs: */
206241a52373SHans Verkuil 		/* tx: ready */
206341a52373SHans Verkuil 		/* tx: arbitration lost */
206441a52373SHans Verkuil 		/* tx: retry timeout */
206541a52373SHans Verkuil 		/* rx: ready */
206641a52373SHans Verkuil 		io_write_clr_set(sd, 0x50, 0x0f, 0x0f);
206741a52373SHans Verkuil 		cec_write(sd, 0x26, 0x01);            /* enable rx */
206841a52373SHans Verkuil 	} else if (state->cec_enabled_adap && !enable) {
206941a52373SHans Verkuil 		/* disable cec interrupts */
207041a52373SHans Verkuil 		io_write_clr_set(sd, 0x50, 0x0f, 0x00);
207141a52373SHans Verkuil 		/* disable address mask 1-3 */
207241a52373SHans Verkuil 		cec_write_clr_set(sd, 0x27, 0x70, 0x00);
207341a52373SHans Verkuil 		/* power down cec section */
207441a52373SHans Verkuil 		cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
207541a52373SHans Verkuil 		state->cec_valid_addrs = 0;
207641a52373SHans Verkuil 	}
207741a52373SHans Verkuil 	state->cec_enabled_adap = enable;
207841a52373SHans Verkuil 	adv76xx_s_detect_tx_5v_ctrl(sd);
207941a52373SHans Verkuil 	return 0;
208041a52373SHans Verkuil }
208141a52373SHans Verkuil 
208241a52373SHans Verkuil static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
208341a52373SHans Verkuil {
2084eb10790fSJose Abreu 	struct adv76xx_state *state = cec_get_drvdata(adap);
208541a52373SHans Verkuil 	struct v4l2_subdev *sd = &state->sd;
208641a52373SHans Verkuil 	unsigned int i, free_idx = ADV76XX_MAX_ADDRS;
208741a52373SHans Verkuil 
208841a52373SHans Verkuil 	if (!state->cec_enabled_adap)
208941a52373SHans Verkuil 		return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
209041a52373SHans Verkuil 
209141a52373SHans Verkuil 	if (addr == CEC_LOG_ADDR_INVALID) {
209241a52373SHans Verkuil 		cec_write_clr_set(sd, 0x27, 0x70, 0);
209341a52373SHans Verkuil 		state->cec_valid_addrs = 0;
209441a52373SHans Verkuil 		return 0;
209541a52373SHans Verkuil 	}
209641a52373SHans Verkuil 
209741a52373SHans Verkuil 	for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
209841a52373SHans Verkuil 		bool is_valid = state->cec_valid_addrs & (1 << i);
209941a52373SHans Verkuil 
210041a52373SHans Verkuil 		if (free_idx == ADV76XX_MAX_ADDRS && !is_valid)
210141a52373SHans Verkuil 			free_idx = i;
210241a52373SHans Verkuil 		if (is_valid && state->cec_addr[i] == addr)
210341a52373SHans Verkuil 			return 0;
210441a52373SHans Verkuil 	}
210541a52373SHans Verkuil 	if (i == ADV76XX_MAX_ADDRS) {
210641a52373SHans Verkuil 		i = free_idx;
210741a52373SHans Verkuil 		if (i == ADV76XX_MAX_ADDRS)
210841a52373SHans Verkuil 			return -ENXIO;
210941a52373SHans Verkuil 	}
211041a52373SHans Verkuil 	state->cec_addr[i] = addr;
211141a52373SHans Verkuil 	state->cec_valid_addrs |= 1 << i;
211241a52373SHans Verkuil 
211341a52373SHans Verkuil 	switch (i) {
211441a52373SHans Verkuil 	case 0:
211541a52373SHans Verkuil 		/* enable address mask 0 */
211641a52373SHans Verkuil 		cec_write_clr_set(sd, 0x27, 0x10, 0x10);
211741a52373SHans Verkuil 		/* set address for mask 0 */
211841a52373SHans Verkuil 		cec_write_clr_set(sd, 0x28, 0x0f, addr);
211941a52373SHans Verkuil 		break;
212041a52373SHans Verkuil 	case 1:
212141a52373SHans Verkuil 		/* enable address mask 1 */
212241a52373SHans Verkuil 		cec_write_clr_set(sd, 0x27, 0x20, 0x20);
212341a52373SHans Verkuil 		/* set address for mask 1 */
212441a52373SHans Verkuil 		cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
212541a52373SHans Verkuil 		break;
212641a52373SHans Verkuil 	case 2:
212741a52373SHans Verkuil 		/* enable address mask 2 */
212841a52373SHans Verkuil 		cec_write_clr_set(sd, 0x27, 0x40, 0x40);
212941a52373SHans Verkuil 		/* set address for mask 1 */
213041a52373SHans Verkuil 		cec_write_clr_set(sd, 0x29, 0x0f, addr);
213141a52373SHans Verkuil 		break;
213241a52373SHans Verkuil 	}
213341a52373SHans Verkuil 	return 0;
213441a52373SHans Verkuil }
213541a52373SHans Verkuil 
213641a52373SHans Verkuil static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
213741a52373SHans Verkuil 				     u32 signal_free_time, struct cec_msg *msg)
213841a52373SHans Verkuil {
2139eb10790fSJose Abreu 	struct adv76xx_state *state = cec_get_drvdata(adap);
214041a52373SHans Verkuil 	struct v4l2_subdev *sd = &state->sd;
214141a52373SHans Verkuil 	u8 len = msg->len;
214241a52373SHans Verkuil 	unsigned int i;
214341a52373SHans Verkuil 
214441a52373SHans Verkuil 	/*
214541a52373SHans Verkuil 	 * The number of retries is the number of attempts - 1, but retry
214641a52373SHans Verkuil 	 * at least once. It's not clear if a value of 0 is allowed, so
214741a52373SHans Verkuil 	 * let's do at least one retry.
214841a52373SHans Verkuil 	 */
214941a52373SHans Verkuil 	cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
215041a52373SHans Verkuil 
215141a52373SHans Verkuil 	if (len > 16) {
215241a52373SHans Verkuil 		v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
215341a52373SHans Verkuil 		return -EINVAL;
215441a52373SHans Verkuil 	}
215541a52373SHans Verkuil 
215641a52373SHans Verkuil 	/* write data */
215741a52373SHans Verkuil 	for (i = 0; i < len; i++)
215841a52373SHans Verkuil 		cec_write(sd, i, msg->msg[i]);
215941a52373SHans Verkuil 
216041a52373SHans Verkuil 	/* set length (data + header) */
216141a52373SHans Verkuil 	cec_write(sd, 0x10, len);
216241a52373SHans Verkuil 	/* start transmit, enable tx */
216341a52373SHans Verkuil 	cec_write(sd, 0x11, 0x01);
216441a52373SHans Verkuil 	return 0;
216541a52373SHans Verkuil }
216641a52373SHans Verkuil 
216741a52373SHans Verkuil static const struct cec_adap_ops adv76xx_cec_adap_ops = {
216841a52373SHans Verkuil 	.adap_enable = adv76xx_cec_adap_enable,
216941a52373SHans Verkuil 	.adap_log_addr = adv76xx_cec_adap_log_addr,
217041a52373SHans Verkuil 	.adap_transmit = adv76xx_cec_adap_transmit,
217141a52373SHans Verkuil };
217241a52373SHans Verkuil #endif
217341a52373SHans Verkuil 
2174b44b2e06SPablo Anton static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
217554450f59SHans Verkuil {
2176b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
2177b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
2178f24d229cSMats Randgaard 	const u8 irq_reg_0x43 = io_read(sd, 0x43);
2179f24d229cSMats Randgaard 	const u8 irq_reg_0x6b = io_read(sd, 0x6b);
2180f24d229cSMats Randgaard 	const u8 irq_reg_0x70 = io_read(sd, 0x70);
2181f24d229cSMats Randgaard 	u8 fmt_change_digital;
2182f24d229cSMats Randgaard 	u8 fmt_change;
2183f24d229cSMats Randgaard 	u8 tx_5v;
2184f24d229cSMats Randgaard 
2185f24d229cSMats Randgaard 	if (irq_reg_0x43)
2186f24d229cSMats Randgaard 		io_write(sd, 0x44, irq_reg_0x43);
2187f24d229cSMats Randgaard 	if (irq_reg_0x70)
2188f24d229cSMats Randgaard 		io_write(sd, 0x71, irq_reg_0x70);
2189f24d229cSMats Randgaard 	if (irq_reg_0x6b)
2190f24d229cSMats Randgaard 		io_write(sd, 0x6c, irq_reg_0x6b);
219154450f59SHans Verkuil 
2192ff4f80fdSMats Randgaard 	v4l2_dbg(2, debug, sd, "%s: ", __func__);
2193ff4f80fdSMats Randgaard 
219454450f59SHans Verkuil 	/* format change */
2195f24d229cSMats Randgaard 	fmt_change = irq_reg_0x43 & 0x98;
2196d42010a1SLars-Peter Clausen 	fmt_change_digital = is_digital_input(sd)
2197d42010a1SLars-Peter Clausen 			   ? irq_reg_0x6b & info->fmt_change_digital_mask
2198d42010a1SLars-Peter Clausen 			   : 0;
219914d03233SMats Randgaard 
220054450f59SHans Verkuil 	if (fmt_change || fmt_change_digital) {
220154450f59SHans Verkuil 		v4l2_dbg(1, debug, sd,
220225a64ac9SMats Randgaard 			"%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
220354450f59SHans Verkuil 			__func__, fmt_change, fmt_change_digital);
220425a64ac9SMats Randgaard 
22056f5bcfc3SLars-Peter Clausen 		v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt);
220625a64ac9SMats Randgaard 
220754450f59SHans Verkuil 		if (handled)
220854450f59SHans Verkuil 			*handled = true;
220954450f59SHans Verkuil 	}
2210f24d229cSMats Randgaard 	/* HDMI/DVI mode */
2211f24d229cSMats Randgaard 	if (irq_reg_0x6b & 0x01) {
2212f24d229cSMats Randgaard 		v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__,
2213f24d229cSMats Randgaard 			(io_read(sd, 0x6a) & 0x01) ? "HDMI" : "DVI");
2214f24d229cSMats Randgaard 		set_rgb_quantization_range(sd);
2215f24d229cSMats Randgaard 		if (handled)
2216f24d229cSMats Randgaard 			*handled = true;
2217f24d229cSMats Randgaard 	}
2218f24d229cSMats Randgaard 
221941a52373SHans Verkuil #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
222041a52373SHans Verkuil 	/* cec */
222141a52373SHans Verkuil 	adv76xx_cec_isr(sd, handled);
222241a52373SHans Verkuil #endif
222341a52373SHans Verkuil 
222454450f59SHans Verkuil 	/* tx 5v detect */
22250ba4581cSHans Verkuil 	tx_5v = irq_reg_0x70 & info->cable_det_mask;
222654450f59SHans Verkuil 	if (tx_5v) {
222754450f59SHans Verkuil 		v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v);
2228b44b2e06SPablo Anton 		adv76xx_s_detect_tx_5v_ctrl(sd);
222954450f59SHans Verkuil 		if (handled)
223054450f59SHans Verkuil 			*handled = true;
223154450f59SHans Verkuil 	}
223254450f59SHans Verkuil 	return 0;
223354450f59SHans Verkuil }
223454450f59SHans Verkuil 
2235b44b2e06SPablo Anton static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
223654450f59SHans Verkuil {
2237b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
22384a31a93aSMats Randgaard 	u8 *data = NULL;
223954450f59SHans Verkuil 
2240dd9ac11aSHans Verkuil 	memset(edid->reserved, 0, sizeof(edid->reserved));
22414a31a93aSMats Randgaard 
22424a31a93aSMats Randgaard 	switch (edid->pad) {
2243b44b2e06SPablo Anton 	case ADV76XX_PAD_HDMI_PORT_A:
2244c784b1e2SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_B:
2245c784b1e2SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_C:
2246c784b1e2SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_D:
22474a31a93aSMats Randgaard 		if (state->edid.present & (1 << edid->pad))
22484a31a93aSMats Randgaard 			data = state->edid.edid;
22494a31a93aSMats Randgaard 		break;
22504a31a93aSMats Randgaard 	default:
22514a31a93aSMats Randgaard 		return -EINVAL;
22524a31a93aSMats Randgaard 	}
2253dd9ac11aSHans Verkuil 
2254dd9ac11aSHans Verkuil 	if (edid->start_block == 0 && edid->blocks == 0) {
2255dd9ac11aSHans Verkuil 		edid->blocks = data ? state->edid.blocks : 0;
2256dd9ac11aSHans Verkuil 		return 0;
2257dd9ac11aSHans Verkuil 	}
2258dd9ac11aSHans Verkuil 
2259dd9ac11aSHans Verkuil 	if (data == NULL)
22604a31a93aSMats Randgaard 		return -ENODATA;
22614a31a93aSMats Randgaard 
2262dd9ac11aSHans Verkuil 	if (edid->start_block >= state->edid.blocks)
2263dd9ac11aSHans Verkuil 		return -EINVAL;
2264dd9ac11aSHans Verkuil 
2265dd9ac11aSHans Verkuil 	if (edid->start_block + edid->blocks > state->edid.blocks)
2266dd9ac11aSHans Verkuil 		edid->blocks = state->edid.blocks - edid->start_block;
2267dd9ac11aSHans Verkuil 
2268dd9ac11aSHans Verkuil 	memcpy(edid->edid, data + edid->start_block * 128, edid->blocks * 128);
2269dd9ac11aSHans Verkuil 
227054450f59SHans Verkuil 	return 0;
227154450f59SHans Verkuil }
227254450f59SHans Verkuil 
2273b44b2e06SPablo Anton static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
227454450f59SHans Verkuil {
2275b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
2276b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
227741a52373SHans Verkuil 	unsigned int spa_loc;
227841a52373SHans Verkuil 	u16 pa;
227954450f59SHans Verkuil 	int err;
2280dd08beb9SMats Randgaard 	int i;
228154450f59SHans Verkuil 
2282dd9ac11aSHans Verkuil 	memset(edid->reserved, 0, sizeof(edid->reserved));
2283dd9ac11aSHans Verkuil 
2284c784b1e2SLaurent Pinchart 	if (edid->pad > ADV7604_PAD_HDMI_PORT_D)
228554450f59SHans Verkuil 		return -EINVAL;
228654450f59SHans Verkuil 	if (edid->start_block != 0)
228754450f59SHans Verkuil 		return -EINVAL;
228854450f59SHans Verkuil 	if (edid->blocks == 0) {
22893e86aa85SMats Randgaard 		/* Disable hotplug and I2C access to EDID RAM from DDC port */
22904a31a93aSMats Randgaard 		state->edid.present &= ~(1 << edid->pad);
2291b44b2e06SPablo Anton 		adv76xx_set_hpd(state, state->edid.present);
229222d97e56SLaurent Pinchart 		rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present);
22933e86aa85SMats Randgaard 
229454450f59SHans Verkuil 		/* Fall back to a 16:9 aspect ratio */
229554450f59SHans Verkuil 		state->aspect_ratio.numerator = 16;
229654450f59SHans Verkuil 		state->aspect_ratio.denominator = 9;
22973e86aa85SMats Randgaard 
22983e86aa85SMats Randgaard 		if (!state->edid.present)
22993e86aa85SMats Randgaard 			state->edid.blocks = 0;
23003e86aa85SMats Randgaard 
23013e86aa85SMats Randgaard 		v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n",
23023e86aa85SMats Randgaard 				__func__, edid->pad, state->edid.present);
230354450f59SHans Verkuil 		return 0;
230454450f59SHans Verkuil 	}
23054a31a93aSMats Randgaard 	if (edid->blocks > 2) {
23064a31a93aSMats Randgaard 		edid->blocks = 2;
230754450f59SHans Verkuil 		return -E2BIG;
23084a31a93aSMats Randgaard 	}
230941a52373SHans Verkuil 	pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
231041a52373SHans Verkuil 	err = cec_phys_addr_validate(pa, &pa, NULL);
231141a52373SHans Verkuil 	if (err)
231241a52373SHans Verkuil 		return err;
23134a31a93aSMats Randgaard 
2314dd08beb9SMats Randgaard 	v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
2315dd08beb9SMats Randgaard 			__func__, edid->pad, state->edid.present);
2316dd08beb9SMats Randgaard 
23173e86aa85SMats Randgaard 	/* Disable hotplug and I2C access to EDID RAM from DDC port */
23184a31a93aSMats Randgaard 	cancel_delayed_work_sync(&state->delayed_work_enable_hotplug);
2319b44b2e06SPablo Anton 	adv76xx_set_hpd(state, 0);
232022d97e56SLaurent Pinchart 	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
23213e86aa85SMats Randgaard 
232241a52373SHans Verkuil 	/*
232341a52373SHans Verkuil 	 * Return an error if no location of the source physical address
232441a52373SHans Verkuil 	 * was found.
232541a52373SHans Verkuil 	 */
232641a52373SHans Verkuil 	if (spa_loc == 0)
232741a52373SHans Verkuil 		return -EINVAL;
2328dd08beb9SMats Randgaard 
23293e86aa85SMats Randgaard 	switch (edid->pad) {
2330b44b2e06SPablo Anton 	case ADV76XX_PAD_HDMI_PORT_A:
2331dd08beb9SMats Randgaard 		state->spa_port_a[0] = edid->edid[spa_loc];
2332dd08beb9SMats Randgaard 		state->spa_port_a[1] = edid->edid[spa_loc + 1];
23333e86aa85SMats Randgaard 		break;
2334c784b1e2SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_B:
2335dd08beb9SMats Randgaard 		rep_write(sd, 0x70, edid->edid[spa_loc]);
2336dd08beb9SMats Randgaard 		rep_write(sd, 0x71, edid->edid[spa_loc + 1]);
23373e86aa85SMats Randgaard 		break;
2338c784b1e2SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_C:
2339dd08beb9SMats Randgaard 		rep_write(sd, 0x72, edid->edid[spa_loc]);
2340dd08beb9SMats Randgaard 		rep_write(sd, 0x73, edid->edid[spa_loc + 1]);
23413e86aa85SMats Randgaard 		break;
2342c784b1e2SLaurent Pinchart 	case ADV7604_PAD_HDMI_PORT_D:
2343dd08beb9SMats Randgaard 		rep_write(sd, 0x74, edid->edid[spa_loc]);
2344dd08beb9SMats Randgaard 		rep_write(sd, 0x75, edid->edid[spa_loc + 1]);
23453e86aa85SMats Randgaard 		break;
2346dd08beb9SMats Randgaard 	default:
2347dd08beb9SMats Randgaard 		return -EINVAL;
23483e86aa85SMats Randgaard 	}
2349d42010a1SLars-Peter Clausen 
2350d42010a1SLars-Peter Clausen 	if (info->type == ADV7604) {
2351dd08beb9SMats Randgaard 		rep_write(sd, 0x76, spa_loc & 0xff);
235222d97e56SLaurent Pinchart 		rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2);
2353d42010a1SLars-Peter Clausen 	} else {
2354b5a442aaSUlrich Hecht 		/* ADV7612 Software Manual Rev. A, p. 15 */
2355b5a442aaSUlrich Hecht 		rep_write(sd, 0x70, spa_loc & 0xff);
235622d97e56SLaurent Pinchart 		rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8);
2357d42010a1SLars-Peter Clausen 	}
23583e86aa85SMats Randgaard 
2359dd08beb9SMats Randgaard 	edid->edid[spa_loc] = state->spa_port_a[0];
2360dd08beb9SMats Randgaard 	edid->edid[spa_loc + 1] = state->spa_port_a[1];
23614a31a93aSMats Randgaard 
23624a31a93aSMats Randgaard 	memcpy(state->edid.edid, edid->edid, 128 * edid->blocks);
23634a31a93aSMats Randgaard 	state->edid.blocks = edid->blocks;
236454450f59SHans Verkuil 	state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15],
236554450f59SHans Verkuil 			edid->edid[0x16]);
23663e86aa85SMats Randgaard 	state->edid.present |= 1 << edid->pad;
23674a31a93aSMats Randgaard 
23684a31a93aSMats Randgaard 	err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid);
23694a31a93aSMats Randgaard 	if (err < 0) {
23703e86aa85SMats Randgaard 		v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad);
237154450f59SHans Verkuil 		return err;
237254450f59SHans Verkuil 	}
237354450f59SHans Verkuil 
2374b44b2e06SPablo Anton 	/* adv76xx calculates the checksums and enables I2C access to internal
2375dd08beb9SMats Randgaard 	   EDID RAM from DDC port. */
237622d97e56SLaurent Pinchart 	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present);
2377dd08beb9SMats Randgaard 
2378dd08beb9SMats Randgaard 	for (i = 0; i < 1000; i++) {
2379d42010a1SLars-Peter Clausen 		if (rep_read(sd, info->edid_status_reg) & state->edid.present)
2380dd08beb9SMats Randgaard 			break;
2381dd08beb9SMats Randgaard 		mdelay(1);
2382dd08beb9SMats Randgaard 	}
2383dd08beb9SMats Randgaard 	if (i == 1000) {
2384dd08beb9SMats Randgaard 		v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
2385dd08beb9SMats Randgaard 		return -EIO;
2386dd08beb9SMats Randgaard 	}
238741a52373SHans Verkuil 	cec_s_phys_addr(state->cec_adap, pa, false);
2388dd08beb9SMats Randgaard 
23894a31a93aSMats Randgaard 	/* enable hotplug after 100 ms */
23900423ff9bSBhaktipriya Shridhar 	schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
23914a31a93aSMats Randgaard 	return 0;
23924a31a93aSMats Randgaard }
23934a31a93aSMats Randgaard 
239454450f59SHans Verkuil /*********** avi info frame CEA-861-E **************/
239554450f59SHans Verkuil 
2396516613c1SHans Verkuil static const struct adv76xx_cfg_read_infoframe adv76xx_cri[] = {
2397516613c1SHans Verkuil 	{ "AVI", 0x01, 0xe0, 0x00 },
2398516613c1SHans Verkuil 	{ "Audio", 0x02, 0xe3, 0x1c },
2399516613c1SHans Verkuil 	{ "SDP", 0x04, 0xe6, 0x2a },
2400516613c1SHans Verkuil 	{ "Vendor", 0x10, 0xec, 0x54 }
2401516613c1SHans Verkuil };
2402516613c1SHans Verkuil 
2403516613c1SHans Verkuil static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index,
2404516613c1SHans Verkuil 				  union hdmi_infoframe *frame)
2405516613c1SHans Verkuil {
2406516613c1SHans Verkuil 	uint8_t buffer[32];
2407516613c1SHans Verkuil 	u8 len;
2408516613c1SHans Verkuil 	int i;
2409516613c1SHans Verkuil 
2410516613c1SHans Verkuil 	if (!(io_read(sd, 0x60) & adv76xx_cri[index].present_mask)) {
2411516613c1SHans Verkuil 		v4l2_info(sd, "%s infoframe not received\n",
2412516613c1SHans Verkuil 			  adv76xx_cri[index].desc);
2413516613c1SHans Verkuil 		return -ENOENT;
2414516613c1SHans Verkuil 	}
2415516613c1SHans Verkuil 
2416516613c1SHans Verkuil 	for (i = 0; i < 3; i++)
2417516613c1SHans Verkuil 		buffer[i] = infoframe_read(sd,
2418516613c1SHans Verkuil 					   adv76xx_cri[index].head_addr + i);
2419516613c1SHans Verkuil 
2420516613c1SHans Verkuil 	len = buffer[2] + 1;
2421516613c1SHans Verkuil 
2422516613c1SHans Verkuil 	if (len + 3 > sizeof(buffer)) {
2423516613c1SHans Verkuil 		v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__,
2424516613c1SHans Verkuil 			 adv76xx_cri[index].desc, len);
2425516613c1SHans Verkuil 		return -ENOENT;
2426516613c1SHans Verkuil 	}
2427516613c1SHans Verkuil 
2428516613c1SHans Verkuil 	for (i = 0; i < len; i++)
2429516613c1SHans Verkuil 		buffer[i + 3] = infoframe_read(sd,
2430516613c1SHans Verkuil 				       adv76xx_cri[index].payload_addr + i);
2431516613c1SHans Verkuil 
2432516613c1SHans Verkuil 	if (hdmi_infoframe_unpack(frame, buffer) < 0) {
2433516613c1SHans Verkuil 		v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__,
2434516613c1SHans Verkuil 			 adv76xx_cri[index].desc);
2435516613c1SHans Verkuil 		return -ENOENT;
2436516613c1SHans Verkuil 	}
2437516613c1SHans Verkuil 	return 0;
2438516613c1SHans Verkuil }
2439516613c1SHans Verkuil 
2440516613c1SHans Verkuil static void adv76xx_log_infoframes(struct v4l2_subdev *sd)
244154450f59SHans Verkuil {
244254450f59SHans Verkuil 	int i;
244354450f59SHans Verkuil 
2444bb88f325SMartin Bugge 	if (!is_hdmi(sd)) {
2445516613c1SHans Verkuil 		v4l2_info(sd, "receive DVI-D signal, no infoframes\n");
244654450f59SHans Verkuil 		return;
244754450f59SHans Verkuil 	}
244854450f59SHans Verkuil 
2449516613c1SHans Verkuil 	for (i = 0; i < ARRAY_SIZE(adv76xx_cri); i++) {
2450516613c1SHans Verkuil 		union hdmi_infoframe frame;
2451516613c1SHans Verkuil 		struct i2c_client *client = v4l2_get_subdevdata(sd);
245254450f59SHans Verkuil 
2453516613c1SHans Verkuil 		if (adv76xx_read_infoframe(sd, i, &frame))
245454450f59SHans Verkuil 			return;
2455516613c1SHans Verkuil 		hdmi_infoframe_log(KERN_INFO, &client->dev, &frame);
2456516613c1SHans Verkuil 	}
245754450f59SHans Verkuil }
245854450f59SHans Verkuil 
2459b44b2e06SPablo Anton static int adv76xx_log_status(struct v4l2_subdev *sd)
246054450f59SHans Verkuil {
2461b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
2462b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
246354450f59SHans Verkuil 	struct v4l2_dv_timings timings;
246454450f59SHans Verkuil 	struct stdi_readback stdi;
246554450f59SHans Verkuil 	u8 reg_io_0x02 = io_read(sd, 0x02);
24664a2ccdd2SLaurent Pinchart 	u8 edid_enabled;
24674a2ccdd2SLaurent Pinchart 	u8 cable_det;
246854450f59SHans Verkuil 
2469f216ccb3SLars-Peter Clausen 	static const char * const csc_coeff_sel_rb[16] = {
247054450f59SHans Verkuil 		"bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB",
247154450f59SHans Verkuil 		"reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709",
247254450f59SHans Verkuil 		"reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709",
247354450f59SHans Verkuil 		"reserved", "reserved", "reserved", "reserved", "manual"
247454450f59SHans Verkuil 	};
2475f216ccb3SLars-Peter Clausen 	static const char * const input_color_space_txt[16] = {
247654450f59SHans Verkuil 		"RGB limited range (16-235)", "RGB full range (0-255)",
247754450f59SHans Verkuil 		"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)",
24789833239eSMats Randgaard 		"xvYCC Bt.601", "xvYCC Bt.709",
247954450f59SHans Verkuil 		"YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",
248054450f59SHans Verkuil 		"invalid", "invalid", "invalid", "invalid", "invalid",
248154450f59SHans Verkuil 		"invalid", "invalid", "automatic"
248254450f59SHans Verkuil 	};
24837a5d99e7SHans Verkuil 	static const char * const hdmi_color_space_txt[16] = {
24847a5d99e7SHans Verkuil 		"RGB limited range (16-235)", "RGB full range (0-255)",
24857a5d99e7SHans Verkuil 		"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)",
24867a5d99e7SHans Verkuil 		"xvYCC Bt.601", "xvYCC Bt.709",
24877a5d99e7SHans Verkuil 		"YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",
24887a5d99e7SHans Verkuil 		"sYCC", "Adobe YCC 601", "AdobeRGB", "invalid", "invalid",
24897a5d99e7SHans Verkuil 		"invalid", "invalid", "invalid"
24907a5d99e7SHans Verkuil 	};
2491f216ccb3SLars-Peter Clausen 	static const char * const rgb_quantization_range_txt[] = {
249254450f59SHans Verkuil 		"Automatic",
249354450f59SHans Verkuil 		"RGB limited range (16-235)",
249454450f59SHans Verkuil 		"RGB full range (0-255)",
249554450f59SHans Verkuil 	};
2496f216ccb3SLars-Peter Clausen 	static const char * const deep_color_mode_txt[4] = {
2497bb88f325SMartin Bugge 		"8-bits per channel",
2498bb88f325SMartin Bugge 		"10-bits per channel",
2499bb88f325SMartin Bugge 		"12-bits per channel",
2500bb88f325SMartin Bugge 		"16-bits per channel (not supported)"
2501bb88f325SMartin Bugge 	};
250254450f59SHans Verkuil 
250354450f59SHans Verkuil 	v4l2_info(sd, "-----Chip status-----\n");
250454450f59SHans Verkuil 	v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
2505d42010a1SLars-Peter Clausen 	edid_enabled = rep_read(sd, info->edid_status_reg);
25064a31a93aSMats Randgaard 	v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n",
25074a2ccdd2SLaurent Pinchart 			((edid_enabled & 0x01) ? "Yes" : "No"),
25084a2ccdd2SLaurent Pinchart 			((edid_enabled & 0x02) ? "Yes" : "No"),
25094a2ccdd2SLaurent Pinchart 			((edid_enabled & 0x04) ? "Yes" : "No"),
25104a2ccdd2SLaurent Pinchart 			((edid_enabled & 0x08) ? "Yes" : "No"));
251141a52373SHans Verkuil 	v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
251254450f59SHans Verkuil 			"enabled" : "disabled");
251341a52373SHans Verkuil 	if (state->cec_enabled_adap) {
251441a52373SHans Verkuil 		int i;
251541a52373SHans Verkuil 
251641a52373SHans Verkuil 		for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
251741a52373SHans Verkuil 			bool is_valid = state->cec_valid_addrs & (1 << i);
251841a52373SHans Verkuil 
251941a52373SHans Verkuil 			if (is_valid)
252041a52373SHans Verkuil 				v4l2_info(sd, "CEC Logical Address: 0x%x\n",
252141a52373SHans Verkuil 					  state->cec_addr[i]);
252241a52373SHans Verkuil 		}
252341a52373SHans Verkuil 	}
252454450f59SHans Verkuil 
252554450f59SHans Verkuil 	v4l2_info(sd, "-----Signal status-----\n");
2526d42010a1SLars-Peter Clausen 	cable_det = info->read_cable_det(sd);
25274a31a93aSMats Randgaard 	v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n",
2528d42010a1SLars-Peter Clausen 			((cable_det & 0x01) ? "Yes" : "No"),
2529d42010a1SLars-Peter Clausen 			((cable_det & 0x02) ? "Yes" : "No"),
25304a2ccdd2SLaurent Pinchart 			((cable_det & 0x04) ? "Yes" : "No"),
2531d42010a1SLars-Peter Clausen 			((cable_det & 0x08) ? "Yes" : "No"));
253254450f59SHans Verkuil 	v4l2_info(sd, "TMDS signal detected: %s\n",
253354450f59SHans Verkuil 			no_signal_tmds(sd) ? "false" : "true");
253454450f59SHans Verkuil 	v4l2_info(sd, "TMDS signal locked: %s\n",
253554450f59SHans Verkuil 			no_lock_tmds(sd) ? "false" : "true");
253654450f59SHans Verkuil 	v4l2_info(sd, "SSPD locked: %s\n", no_lock_sspd(sd) ? "false" : "true");
253754450f59SHans Verkuil 	v4l2_info(sd, "STDI locked: %s\n", no_lock_stdi(sd) ? "false" : "true");
253854450f59SHans Verkuil 	v4l2_info(sd, "CP locked: %s\n", no_lock_cp(sd) ? "false" : "true");
253954450f59SHans Verkuil 	v4l2_info(sd, "CP free run: %s\n",
254058514625Sjean-michel.hautbois@vodalys.com 			(in_free_run(sd)) ? "on" : "off");
2541ccbd5bc4SHans Verkuil 	v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n",
2542ccbd5bc4SHans Verkuil 			io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f,
2543ccbd5bc4SHans Verkuil 			(io_read(sd, 0x01) & 0x70) >> 4);
254454450f59SHans Verkuil 
254554450f59SHans Verkuil 	v4l2_info(sd, "-----Video Timings-----\n");
254654450f59SHans Verkuil 	if (read_stdi(sd, &stdi))
254754450f59SHans Verkuil 		v4l2_info(sd, "STDI: not locked\n");
254854450f59SHans Verkuil 	else
254954450f59SHans Verkuil 		v4l2_info(sd, "STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %s, %chsync, %cvsync\n",
255054450f59SHans Verkuil 				stdi.lcf, stdi.bl, stdi.lcvs,
255154450f59SHans Verkuil 				stdi.interlaced ? "interlaced" : "progressive",
255254450f59SHans Verkuil 				stdi.hs_pol, stdi.vs_pol);
2553b44b2e06SPablo Anton 	if (adv76xx_query_dv_timings(sd, &timings))
255454450f59SHans Verkuil 		v4l2_info(sd, "No video detected\n");
255554450f59SHans Verkuil 	else
255611d034c8SHans Verkuil 		v4l2_print_dv_timings(sd->name, "Detected format: ",
255711d034c8SHans Verkuil 				      &timings, true);
255811d034c8SHans Verkuil 	v4l2_print_dv_timings(sd->name, "Configured format: ",
255911d034c8SHans Verkuil 			      &state->timings, true);
256054450f59SHans Verkuil 
256176eb2d30SMats Randgaard 	if (no_signal(sd))
256276eb2d30SMats Randgaard 		return 0;
256376eb2d30SMats Randgaard 
256454450f59SHans Verkuil 	v4l2_info(sd, "-----Color space-----\n");
256554450f59SHans Verkuil 	v4l2_info(sd, "RGB quantization range ctrl: %s\n",
256654450f59SHans Verkuil 			rgb_quantization_range_txt[state->rgb_quantization_range]);
256754450f59SHans Verkuil 	v4l2_info(sd, "Input color space: %s\n",
256854450f59SHans Verkuil 			input_color_space_txt[reg_io_0x02 >> 4]);
2569fd74246dSHans Verkuil 	v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n",
257054450f59SHans Verkuil 			(reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
25715dd7d88aSHans Verkuil 			(((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
2572fd74246dSHans Verkuil 				"(16-235)" : "(0-255)",
25737a5d99e7SHans Verkuil 			(reg_io_0x02 & 0x08) ? "enabled" : "disabled");
257454450f59SHans Verkuil 	v4l2_info(sd, "Color space conversion: %s\n",
257580f4944eSjean-michel.hautbois@vodalys.com 			csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
257654450f59SHans Verkuil 
25774a31a93aSMats Randgaard 	if (!is_digital_input(sd))
257876eb2d30SMats Randgaard 		return 0;
257976eb2d30SMats Randgaard 
258076eb2d30SMats Randgaard 	v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D");
25814a31a93aSMats Randgaard 	v4l2_info(sd, "Digital video port selected: %c\n",
25824a31a93aSMats Randgaard 			(hdmi_read(sd, 0x00) & 0x03) + 'A');
25834a31a93aSMats Randgaard 	v4l2_info(sd, "HDCP encrypted content: %s\n",
25844a31a93aSMats Randgaard 			(hdmi_read(sd, 0x05) & 0x40) ? "true" : "false");
258576eb2d30SMats Randgaard 	v4l2_info(sd, "HDCP keys read: %s%s\n",
258676eb2d30SMats Randgaard 			(hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no",
258776eb2d30SMats Randgaard 			(hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : "");
258877639ff2SHans Verkuil 	if (is_hdmi(sd)) {
258976eb2d30SMats Randgaard 		bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01;
259076eb2d30SMats Randgaard 		bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01;
259176eb2d30SMats Randgaard 		bool audio_mute = io_read(sd, 0x65) & 0x40;
259276eb2d30SMats Randgaard 
259376eb2d30SMats Randgaard 		v4l2_info(sd, "Audio: pll %s, samples %s, %s\n",
259476eb2d30SMats Randgaard 				audio_pll_locked ? "locked" : "not locked",
259576eb2d30SMats Randgaard 				audio_sample_packet_detect ? "detected" : "not detected",
259676eb2d30SMats Randgaard 				audio_mute ? "muted" : "enabled");
259776eb2d30SMats Randgaard 		if (audio_pll_locked && audio_sample_packet_detect) {
259876eb2d30SMats Randgaard 			v4l2_info(sd, "Audio format: %s\n",
259976eb2d30SMats Randgaard 					(hdmi_read(sd, 0x07) & 0x20) ? "multi-channel" : "stereo");
260076eb2d30SMats Randgaard 		}
260176eb2d30SMats Randgaard 		v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) +
260276eb2d30SMats Randgaard 				(hdmi_read(sd, 0x5c) << 8) +
260376eb2d30SMats Randgaard 				(hdmi_read(sd, 0x5d) & 0xf0));
260476eb2d30SMats Randgaard 		v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) +
260576eb2d30SMats Randgaard 				(hdmi_read(sd, 0x5e) << 8) +
260676eb2d30SMats Randgaard 				hdmi_read(sd, 0x5f));
260776eb2d30SMats Randgaard 		v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off");
260876eb2d30SMats Randgaard 
260976eb2d30SMats Randgaard 		v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[(hdmi_read(sd, 0x0b) & 0x60) >> 5]);
26107a5d99e7SHans Verkuil 		v4l2_info(sd, "HDMI colorspace: %s\n", hdmi_color_space_txt[hdmi_read(sd, 0x53) & 0xf]);
261176eb2d30SMats Randgaard 
2612516613c1SHans Verkuil 		adv76xx_log_infoframes(sd);
261354450f59SHans Verkuil 	}
261454450f59SHans Verkuil 
261554450f59SHans Verkuil 	return 0;
261654450f59SHans Verkuil }
261754450f59SHans Verkuil 
26186f5bcfc3SLars-Peter Clausen static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
26196f5bcfc3SLars-Peter Clausen 				   struct v4l2_fh *fh,
26206f5bcfc3SLars-Peter Clausen 				   struct v4l2_event_subscription *sub)
26216f5bcfc3SLars-Peter Clausen {
26226f5bcfc3SLars-Peter Clausen 	switch (sub->type) {
26236f5bcfc3SLars-Peter Clausen 	case V4L2_EVENT_SOURCE_CHANGE:
26246f5bcfc3SLars-Peter Clausen 		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
26256f5bcfc3SLars-Peter Clausen 	case V4L2_EVENT_CTRL:
26266f5bcfc3SLars-Peter Clausen 		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
26276f5bcfc3SLars-Peter Clausen 	default:
26286f5bcfc3SLars-Peter Clausen 		return -EINVAL;
26296f5bcfc3SLars-Peter Clausen 	}
26306f5bcfc3SLars-Peter Clausen }
26316f5bcfc3SLars-Peter Clausen 
263241a52373SHans Verkuil static int adv76xx_registered(struct v4l2_subdev *sd)
263341a52373SHans Verkuil {
263441a52373SHans Verkuil 	struct adv76xx_state *state = to_state(sd);
2635f51e8080SHans Verkuil 	struct i2c_client *client = v4l2_get_subdevdata(sd);
263641a52373SHans Verkuil 	int err;
263741a52373SHans Verkuil 
2638f51e8080SHans Verkuil 	err = cec_register_adapter(state->cec_adap, &client->dev);
263941a52373SHans Verkuil 	if (err)
264041a52373SHans Verkuil 		cec_delete_adapter(state->cec_adap);
264141a52373SHans Verkuil 	return err;
264241a52373SHans Verkuil }
264341a52373SHans Verkuil 
264441a52373SHans Verkuil static void adv76xx_unregistered(struct v4l2_subdev *sd)
264541a52373SHans Verkuil {
264641a52373SHans Verkuil 	struct adv76xx_state *state = to_state(sd);
264741a52373SHans Verkuil 
264841a52373SHans Verkuil 	cec_unregister_adapter(state->cec_adap);
264941a52373SHans Verkuil }
265041a52373SHans Verkuil 
265154450f59SHans Verkuil /* ----------------------------------------------------------------------- */
265254450f59SHans Verkuil 
2653b44b2e06SPablo Anton static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
2654b44b2e06SPablo Anton 	.s_ctrl = adv76xx_s_ctrl,
2655297a4144SHans Verkuil 	.g_volatile_ctrl = adv76xx_g_volatile_ctrl,
265654450f59SHans Verkuil };
265754450f59SHans Verkuil 
2658b44b2e06SPablo Anton static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
2659b44b2e06SPablo Anton 	.log_status = adv76xx_log_status,
2660b44b2e06SPablo Anton 	.interrupt_service_routine = adv76xx_isr,
26616f5bcfc3SLars-Peter Clausen 	.subscribe_event = adv76xx_subscribe_event,
26620975626dSLars-Peter Clausen 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
266354450f59SHans Verkuil #ifdef CONFIG_VIDEO_ADV_DEBUG
2664b44b2e06SPablo Anton 	.g_register = adv76xx_g_register,
2665b44b2e06SPablo Anton 	.s_register = adv76xx_s_register,
266654450f59SHans Verkuil #endif
266754450f59SHans Verkuil };
266854450f59SHans Verkuil 
2669b44b2e06SPablo Anton static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
2670b44b2e06SPablo Anton 	.s_routing = adv76xx_s_routing,
2671b44b2e06SPablo Anton 	.g_input_status = adv76xx_g_input_status,
2672b44b2e06SPablo Anton 	.s_dv_timings = adv76xx_s_dv_timings,
2673b44b2e06SPablo Anton 	.g_dv_timings = adv76xx_g_dv_timings,
2674b44b2e06SPablo Anton 	.query_dv_timings = adv76xx_query_dv_timings,
267554450f59SHans Verkuil };
267654450f59SHans Verkuil 
2677b44b2e06SPablo Anton static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
2678b44b2e06SPablo Anton 	.enum_mbus_code = adv76xx_enum_mbus_code,
2679b7d4d2f8SUlrich Hecht 	.get_selection = adv76xx_get_selection,
2680b44b2e06SPablo Anton 	.get_fmt = adv76xx_get_format,
2681b44b2e06SPablo Anton 	.set_fmt = adv76xx_set_format,
2682b44b2e06SPablo Anton 	.get_edid = adv76xx_get_edid,
2683b44b2e06SPablo Anton 	.set_edid = adv76xx_set_edid,
2684b44b2e06SPablo Anton 	.dv_timings_cap = adv76xx_dv_timings_cap,
2685b44b2e06SPablo Anton 	.enum_dv_timings = adv76xx_enum_dv_timings,
268654450f59SHans Verkuil };
268754450f59SHans Verkuil 
2688b44b2e06SPablo Anton static const struct v4l2_subdev_ops adv76xx_ops = {
2689b44b2e06SPablo Anton 	.core = &adv76xx_core_ops,
2690b44b2e06SPablo Anton 	.video = &adv76xx_video_ops,
2691b44b2e06SPablo Anton 	.pad = &adv76xx_pad_ops,
269254450f59SHans Verkuil };
269354450f59SHans Verkuil 
269441a52373SHans Verkuil static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
269541a52373SHans Verkuil 	.registered = adv76xx_registered,
269641a52373SHans Verkuil 	.unregistered = adv76xx_unregistered,
269741a52373SHans Verkuil };
269841a52373SHans Verkuil 
269954450f59SHans Verkuil /* -------------------------- custom ctrls ---------------------------------- */
270054450f59SHans Verkuil 
270154450f59SHans Verkuil static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
2702b44b2e06SPablo Anton 	.ops = &adv76xx_ctrl_ops,
270354450f59SHans Verkuil 	.id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE,
270454450f59SHans Verkuil 	.name = "Analog Sampling Phase",
270554450f59SHans Verkuil 	.type = V4L2_CTRL_TYPE_INTEGER,
270654450f59SHans Verkuil 	.min = 0,
270754450f59SHans Verkuil 	.max = 0x1f,
270854450f59SHans Verkuil 	.step = 1,
270954450f59SHans Verkuil 	.def = 0,
271054450f59SHans Verkuil };
271154450f59SHans Verkuil 
2712b44b2e06SPablo Anton static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color_manual = {
2713b44b2e06SPablo Anton 	.ops = &adv76xx_ctrl_ops,
271454450f59SHans Verkuil 	.id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL,
271554450f59SHans Verkuil 	.name = "Free Running Color, Manual",
271654450f59SHans Verkuil 	.type = V4L2_CTRL_TYPE_BOOLEAN,
271754450f59SHans Verkuil 	.min = false,
271854450f59SHans Verkuil 	.max = true,
271954450f59SHans Verkuil 	.step = 1,
272054450f59SHans Verkuil 	.def = false,
272154450f59SHans Verkuil };
272254450f59SHans Verkuil 
2723b44b2e06SPablo Anton static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = {
2724b44b2e06SPablo Anton 	.ops = &adv76xx_ctrl_ops,
272554450f59SHans Verkuil 	.id = V4L2_CID_ADV_RX_FREE_RUN_COLOR,
272654450f59SHans Verkuil 	.name = "Free Running Color",
272754450f59SHans Verkuil 	.type = V4L2_CTRL_TYPE_INTEGER,
272854450f59SHans Verkuil 	.min = 0x0,
272954450f59SHans Verkuil 	.max = 0xffffff,
273054450f59SHans Verkuil 	.step = 0x1,
273154450f59SHans Verkuil 	.def = 0x0,
273254450f59SHans Verkuil };
273354450f59SHans Verkuil 
273454450f59SHans Verkuil /* ----------------------------------------------------------------------- */
273554450f59SHans Verkuil 
2736b44b2e06SPablo Anton static int adv76xx_core_init(struct v4l2_subdev *sd)
273754450f59SHans Verkuil {
2738b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
2739b44b2e06SPablo Anton 	const struct adv76xx_chip_info *info = state->info;
2740b44b2e06SPablo Anton 	struct adv76xx_platform_data *pdata = &state->pdata;
274154450f59SHans Verkuil 
274254450f59SHans Verkuil 	hdmi_write(sd, 0x48,
274354450f59SHans Verkuil 		(pdata->disable_pwrdnb ? 0x80 : 0) |
274454450f59SHans Verkuil 		(pdata->disable_cable_det_rst ? 0x40 : 0));
274554450f59SHans Verkuil 
274654450f59SHans Verkuil 	disable_input(sd);
274754450f59SHans Verkuil 
27485ef54b59SLaurent Pinchart 	if (pdata->default_input >= 0 &&
27495ef54b59SLaurent Pinchart 	    pdata->default_input < state->source_pad) {
27505ef54b59SLaurent Pinchart 		state->selected_input = pdata->default_input;
27515ef54b59SLaurent Pinchart 		select_input(sd);
27525ef54b59SLaurent Pinchart 		enable_input(sd);
27535ef54b59SLaurent Pinchart 	}
27545ef54b59SLaurent Pinchart 
275554450f59SHans Verkuil 	/* power */
275654450f59SHans Verkuil 	io_write(sd, 0x0c, 0x42);   /* Power up part and power down VDP */
275754450f59SHans Verkuil 	io_write(sd, 0x0b, 0x44);   /* Power down ESDP block */
275854450f59SHans Verkuil 	cp_write(sd, 0xcf, 0x01);   /* Power down macrovision */
275954450f59SHans Verkuil 
276054450f59SHans Verkuil 	/* video format */
2761fd74246dSHans Verkuil 	io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3);
276222d97e56SLaurent Pinchart 	io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 |
276354450f59SHans Verkuil 			pdata->insert_av_codes << 2 |
2764539b33b0SLaurent Pinchart 			pdata->replicate_av_codes << 1);
2765b44b2e06SPablo Anton 	adv76xx_setup_format(state);
276654450f59SHans Verkuil 
276754450f59SHans Verkuil 	cp_write(sd, 0x69, 0x30);   /* Enable CP CSC */
276898908696SMartin Bugge 
276998908696SMartin Bugge 	/* VS, HS polarities */
27701b5ab875SLaurent Pinchart 	io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 |
27711b5ab875SLaurent Pinchart 		 pdata->inv_hs_pol << 1 | pdata->inv_llc_pol);
2772f31b62e1SMikhail Khelik 
2773f31b62e1SMikhail Khelik 	/* Adjust drive strength */
2774f31b62e1SMikhail Khelik 	io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 |
2775f31b62e1SMikhail Khelik 				pdata->dr_str_clk << 2 |
2776f31b62e1SMikhail Khelik 				pdata->dr_str_sync);
2777f31b62e1SMikhail Khelik 
277854450f59SHans Verkuil 	cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */
277954450f59SHans Verkuil 	cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */
278054450f59SHans Verkuil 	cp_write(sd, 0xf9, 0x23); /*  STDI ch. 1 - LCVS change threshold -
278180939647SHans Verkuil 				      ADI recommended setting [REF_01, c. 2.3.3] */
278254450f59SHans Verkuil 	cp_write(sd, 0x45, 0x23); /*  STDI ch. 2 - LCVS change threshold -
278380939647SHans Verkuil 				      ADI recommended setting [REF_01, c. 2.3.3] */
278454450f59SHans Verkuil 	cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution
278554450f59SHans Verkuil 				     for digital formats */
278654450f59SHans Verkuil 
27875474b983SMats Randgaard 	/* HDMI audio */
278822d97e56SLaurent Pinchart 	hdmi_write_clr_set(sd, 0x15, 0x03, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */
278922d97e56SLaurent Pinchart 	hdmi_write_clr_set(sd, 0x1a, 0x0e, 0x08); /* Wait 1 s before unmute */
279022d97e56SLaurent Pinchart 	hdmi_write_clr_set(sd, 0x68, 0x06, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */
27915474b983SMats Randgaard 
279254450f59SHans Verkuil 	/* TODO from platform data */
279354450f59SHans Verkuil 	afe_write(sd, 0xb5, 0x01);  /* Setting MCLK to 256Fs */
279454450f59SHans Verkuil 
2795b44b2e06SPablo Anton 	if (adv76xx_has_afe(state)) {
279654450f59SHans Verkuil 		afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */
279722d97e56SLaurent Pinchart 		io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4);
2798d42010a1SLars-Peter Clausen 	}
279954450f59SHans Verkuil 
280054450f59SHans Verkuil 	/* interrupts */
2801d42010a1SLars-Peter Clausen 	io_write(sd, 0x40, 0xc0 | pdata->int1_config); /* Configure INT1 */
280254450f59SHans Verkuil 	io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */
2803d42010a1SLars-Peter Clausen 	io_write(sd, 0x6e, info->fmt_change_digital_mask); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */
2804d42010a1SLars-Peter Clausen 	io_write(sd, 0x73, info->cable_det_mask); /* Enable cable detection (+5v) interrupts */
2805d42010a1SLars-Peter Clausen 	info->setup_irqs(sd);
280654450f59SHans Verkuil 
280754450f59SHans Verkuil 	return v4l2_ctrl_handler_setup(sd->ctrl_handler);
280854450f59SHans Verkuil }
280954450f59SHans Verkuil 
2810d42010a1SLars-Peter Clausen static void adv7604_setup_irqs(struct v4l2_subdev *sd)
2811d42010a1SLars-Peter Clausen {
2812d42010a1SLars-Peter Clausen 	io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */
2813d42010a1SLars-Peter Clausen }
2814d42010a1SLars-Peter Clausen 
2815d42010a1SLars-Peter Clausen static void adv7611_setup_irqs(struct v4l2_subdev *sd)
2816d42010a1SLars-Peter Clausen {
2817d42010a1SLars-Peter Clausen 	io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */
2818d42010a1SLars-Peter Clausen }
2819d42010a1SLars-Peter Clausen 
28208331d30bSWilliam Towle static void adv7612_setup_irqs(struct v4l2_subdev *sd)
28218331d30bSWilliam Towle {
28228331d30bSWilliam Towle 	io_write(sd, 0x41, 0xd0); /* disable INT2 */
28238331d30bSWilliam Towle }
28248331d30bSWilliam Towle 
2825b44b2e06SPablo Anton static void adv76xx_unregister_clients(struct adv76xx_state *state)
282654450f59SHans Verkuil {
282705cacb17SLaurent Pinchart 	unsigned int i;
282805cacb17SLaurent Pinchart 
282905cacb17SLaurent Pinchart 	for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) {
283005cacb17SLaurent Pinchart 		if (state->i2c_clients[i])
283105cacb17SLaurent Pinchart 			i2c_unregister_device(state->i2c_clients[i]);
283205cacb17SLaurent Pinchart 	}
283354450f59SHans Verkuil }
283454450f59SHans Verkuil 
2835b44b2e06SPablo Anton static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd,
283654450f59SHans Verkuil 							u8 addr, u8 io_reg)
283754450f59SHans Verkuil {
283854450f59SHans Verkuil 	struct i2c_client *client = v4l2_get_subdevdata(sd);
283954450f59SHans Verkuil 
284054450f59SHans Verkuil 	if (addr)
284154450f59SHans Verkuil 		io_write(sd, io_reg, addr << 1);
284254450f59SHans Verkuil 	return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
284354450f59SHans Verkuil }
284454450f59SHans Verkuil 
2845b44b2e06SPablo Anton static const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = {
2846d42010a1SLars-Peter Clausen 	/* reset ADI recommended settings for HDMI: */
2847d42010a1SLars-Peter Clausen 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
2848b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
2849b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
2850b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */
2851b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */
2852b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
2853b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */
2854b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */
2855b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
2856b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
2857b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */
2858b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */
2859b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */
2860d42010a1SLars-Peter Clausen 
2861d42010a1SLars-Peter Clausen 	/* set ADI recommended settings for digitizer */
2862d42010a1SLars-Peter Clausen 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
2863b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */
2864b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */
2865b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */
2866b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */
2867b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */
2868d42010a1SLars-Peter Clausen 
2869b44b2e06SPablo Anton 	{ ADV76XX_REG_SEQ_TERM, 0 },
2870d42010a1SLars-Peter Clausen };
2871d42010a1SLars-Peter Clausen 
2872b44b2e06SPablo Anton static const struct adv76xx_reg_seq adv7604_recommended_settings_hdmi[] = {
2873d42010a1SLars-Peter Clausen 	/* set ADI recommended settings for HDMI: */
2874d42010a1SLars-Peter Clausen 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
2875b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */
2876b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */
2877b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */
2878b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
2879b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */
2880b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */
2881b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
2882b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
2883b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */
2884b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */
2885b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */
2886d42010a1SLars-Peter Clausen 
2887d42010a1SLars-Peter Clausen 	/* reset ADI recommended settings for digitizer */
2888d42010a1SLars-Peter Clausen 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
2889b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */
2890b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */
2891d42010a1SLars-Peter Clausen 
2892b44b2e06SPablo Anton 	{ ADV76XX_REG_SEQ_TERM, 0 },
2893d42010a1SLars-Peter Clausen };
2894d42010a1SLars-Peter Clausen 
2895b44b2e06SPablo Anton static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = {
2896c41ad9c3SLars-Peter Clausen 	/* ADV7611 Register Settings Recommendations Rev 1.5, May 2014 */
2897b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 },
2898b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 },
2899b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 },
2900b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f },
2901b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 },
2902b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda },
2903b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 },
2904b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 },
2905b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 },
2906b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x04 },
2907b44b2e06SPablo Anton 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x1e },
2908d42010a1SLars-Peter Clausen 
2909b44b2e06SPablo Anton 	{ ADV76XX_REG_SEQ_TERM, 0 },
2910d42010a1SLars-Peter Clausen };
2911d42010a1SLars-Peter Clausen 
29128331d30bSWilliam Towle static const struct adv76xx_reg_seq adv7612_recommended_settings_hdmi[] = {
29138331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 },
29148331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 },
29158331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 },
29168331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f },
29178331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 },
29188331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda },
29198331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 },
29208331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 },
29218331d30bSWilliam Towle 	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 },
29228331d30bSWilliam Towle 	{ ADV76XX_REG_SEQ_TERM, 0 },
29238331d30bSWilliam Towle };
29248331d30bSWilliam Towle 
2925b44b2e06SPablo Anton static const struct adv76xx_chip_info adv76xx_chip_info[] = {
2926d42010a1SLars-Peter Clausen 	[ADV7604] = {
2927d42010a1SLars-Peter Clausen 		.type = ADV7604,
2928d42010a1SLars-Peter Clausen 		.has_afe = true,
2929c784b1e2SLaurent Pinchart 		.max_port = ADV7604_PAD_VGA_COMP,
2930d42010a1SLars-Peter Clausen 		.num_dv_ports = 4,
2931d42010a1SLars-Peter Clausen 		.edid_enable_reg = 0x77,
2932d42010a1SLars-Peter Clausen 		.edid_status_reg = 0x7d,
2933d42010a1SLars-Peter Clausen 		.lcf_reg = 0xb3,
2934d42010a1SLars-Peter Clausen 		.tdms_lock_mask = 0xe0,
2935d42010a1SLars-Peter Clausen 		.cable_det_mask = 0x1e,
2936d42010a1SLars-Peter Clausen 		.fmt_change_digital_mask = 0xc1,
293780f4944eSjean-michel.hautbois@vodalys.com 		.cp_csc = 0xfc,
2938539b33b0SLaurent Pinchart 		.formats = adv7604_formats,
2939539b33b0SLaurent Pinchart 		.nformats = ARRAY_SIZE(adv7604_formats),
2940d42010a1SLars-Peter Clausen 		.set_termination = adv7604_set_termination,
2941d42010a1SLars-Peter Clausen 		.setup_irqs = adv7604_setup_irqs,
2942d42010a1SLars-Peter Clausen 		.read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock,
2943d42010a1SLars-Peter Clausen 		.read_cable_det = adv7604_read_cable_det,
2944d42010a1SLars-Peter Clausen 		.recommended_settings = {
2945d42010a1SLars-Peter Clausen 		    [0] = adv7604_recommended_settings_afe,
2946d42010a1SLars-Peter Clausen 		    [1] = adv7604_recommended_settings_hdmi,
2947d42010a1SLars-Peter Clausen 		},
2948d42010a1SLars-Peter Clausen 		.num_recommended_settings = {
2949d42010a1SLars-Peter Clausen 		    [0] = ARRAY_SIZE(adv7604_recommended_settings_afe),
2950d42010a1SLars-Peter Clausen 		    [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi),
2951d42010a1SLars-Peter Clausen 		},
2952b44b2e06SPablo Anton 		.page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) |
2953b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_CEC) | BIT(ADV76XX_PAGE_INFOFRAME) |
2954d42010a1SLars-Peter Clausen 			BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) |
2955b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_AFE) | BIT(ADV76XX_PAGE_REP) |
2956b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_EDID) | BIT(ADV76XX_PAGE_HDMI) |
2957b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_TEST) | BIT(ADV76XX_PAGE_CP) |
2958d42010a1SLars-Peter Clausen 			BIT(ADV7604_PAGE_VDP),
29595380baafSjean-michel.hautbois@vodalys.com 		.linewidth_mask = 0xfff,
29605380baafSjean-michel.hautbois@vodalys.com 		.field0_height_mask = 0xfff,
29615380baafSjean-michel.hautbois@vodalys.com 		.field1_height_mask = 0xfff,
29625380baafSjean-michel.hautbois@vodalys.com 		.hfrontporch_mask = 0x3ff,
29635380baafSjean-michel.hautbois@vodalys.com 		.hsync_mask = 0x3ff,
29645380baafSjean-michel.hautbois@vodalys.com 		.hbackporch_mask = 0x3ff,
29655380baafSjean-michel.hautbois@vodalys.com 		.field0_vfrontporch_mask = 0x1fff,
29665380baafSjean-michel.hautbois@vodalys.com 		.field0_vsync_mask = 0x1fff,
29675380baafSjean-michel.hautbois@vodalys.com 		.field0_vbackporch_mask = 0x1fff,
29685380baafSjean-michel.hautbois@vodalys.com 		.field1_vfrontporch_mask = 0x1fff,
29695380baafSjean-michel.hautbois@vodalys.com 		.field1_vsync_mask = 0x1fff,
29705380baafSjean-michel.hautbois@vodalys.com 		.field1_vbackporch_mask = 0x1fff,
2971d42010a1SLars-Peter Clausen 	},
2972d42010a1SLars-Peter Clausen 	[ADV7611] = {
2973d42010a1SLars-Peter Clausen 		.type = ADV7611,
2974d42010a1SLars-Peter Clausen 		.has_afe = false,
2975b44b2e06SPablo Anton 		.max_port = ADV76XX_PAD_HDMI_PORT_A,
2976d42010a1SLars-Peter Clausen 		.num_dv_ports = 1,
2977d42010a1SLars-Peter Clausen 		.edid_enable_reg = 0x74,
2978d42010a1SLars-Peter Clausen 		.edid_status_reg = 0x76,
2979d42010a1SLars-Peter Clausen 		.lcf_reg = 0xa3,
2980d42010a1SLars-Peter Clausen 		.tdms_lock_mask = 0x43,
2981d42010a1SLars-Peter Clausen 		.cable_det_mask = 0x01,
2982d42010a1SLars-Peter Clausen 		.fmt_change_digital_mask = 0x03,
298380f4944eSjean-michel.hautbois@vodalys.com 		.cp_csc = 0xf4,
2984539b33b0SLaurent Pinchart 		.formats = adv7611_formats,
2985539b33b0SLaurent Pinchart 		.nformats = ARRAY_SIZE(adv7611_formats),
2986d42010a1SLars-Peter Clausen 		.set_termination = adv7611_set_termination,
2987d42010a1SLars-Peter Clausen 		.setup_irqs = adv7611_setup_irqs,
2988d42010a1SLars-Peter Clausen 		.read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock,
2989d42010a1SLars-Peter Clausen 		.read_cable_det = adv7611_read_cable_det,
2990d42010a1SLars-Peter Clausen 		.recommended_settings = {
2991d42010a1SLars-Peter Clausen 		    [1] = adv7611_recommended_settings_hdmi,
2992d42010a1SLars-Peter Clausen 		},
2993d42010a1SLars-Peter Clausen 		.num_recommended_settings = {
2994d42010a1SLars-Peter Clausen 		    [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi),
2995d42010a1SLars-Peter Clausen 		},
2996b44b2e06SPablo Anton 		.page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) |
2997b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) |
2998b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_REP) |  BIT(ADV76XX_PAGE_EDID) |
2999b44b2e06SPablo Anton 			BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP),
30005380baafSjean-michel.hautbois@vodalys.com 		.linewidth_mask = 0x1fff,
30015380baafSjean-michel.hautbois@vodalys.com 		.field0_height_mask = 0x1fff,
30025380baafSjean-michel.hautbois@vodalys.com 		.field1_height_mask = 0x1fff,
30035380baafSjean-michel.hautbois@vodalys.com 		.hfrontporch_mask = 0x1fff,
30045380baafSjean-michel.hautbois@vodalys.com 		.hsync_mask = 0x1fff,
30055380baafSjean-michel.hautbois@vodalys.com 		.hbackporch_mask = 0x1fff,
30065380baafSjean-michel.hautbois@vodalys.com 		.field0_vfrontporch_mask = 0x3fff,
30075380baafSjean-michel.hautbois@vodalys.com 		.field0_vsync_mask = 0x3fff,
30085380baafSjean-michel.hautbois@vodalys.com 		.field0_vbackporch_mask = 0x3fff,
30095380baafSjean-michel.hautbois@vodalys.com 		.field1_vfrontporch_mask = 0x3fff,
30105380baafSjean-michel.hautbois@vodalys.com 		.field1_vsync_mask = 0x3fff,
30115380baafSjean-michel.hautbois@vodalys.com 		.field1_vbackporch_mask = 0x3fff,
3012d42010a1SLars-Peter Clausen 	},
30138331d30bSWilliam Towle 	[ADV7612] = {
30148331d30bSWilliam Towle 		.type = ADV7612,
30158331d30bSWilliam Towle 		.has_afe = false,
30167111cdddSWilliam Towle 		.max_port = ADV76XX_PAD_HDMI_PORT_A,	/* B not supported */
30177111cdddSWilliam Towle 		.num_dv_ports = 1,			/* normally 2 */
30188331d30bSWilliam Towle 		.edid_enable_reg = 0x74,
30198331d30bSWilliam Towle 		.edid_status_reg = 0x76,
30208331d30bSWilliam Towle 		.lcf_reg = 0xa3,
30218331d30bSWilliam Towle 		.tdms_lock_mask = 0x43,
30228331d30bSWilliam Towle 		.cable_det_mask = 0x01,
30238331d30bSWilliam Towle 		.fmt_change_digital_mask = 0x03,
30247111cdddSWilliam Towle 		.cp_csc = 0xf4,
30258331d30bSWilliam Towle 		.formats = adv7612_formats,
30268331d30bSWilliam Towle 		.nformats = ARRAY_SIZE(adv7612_formats),
30278331d30bSWilliam Towle 		.set_termination = adv7611_set_termination,
30288331d30bSWilliam Towle 		.setup_irqs = adv7612_setup_irqs,
30298331d30bSWilliam Towle 		.read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock,
30307111cdddSWilliam Towle 		.read_cable_det = adv7612_read_cable_det,
30318331d30bSWilliam Towle 		.recommended_settings = {
30328331d30bSWilliam Towle 		    [1] = adv7612_recommended_settings_hdmi,
30338331d30bSWilliam Towle 		},
30348331d30bSWilliam Towle 		.num_recommended_settings = {
30358331d30bSWilliam Towle 		    [1] = ARRAY_SIZE(adv7612_recommended_settings_hdmi),
30368331d30bSWilliam Towle 		},
30378331d30bSWilliam Towle 		.page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) |
30388331d30bSWilliam Towle 			BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) |
30398331d30bSWilliam Towle 			BIT(ADV76XX_PAGE_REP) |  BIT(ADV76XX_PAGE_EDID) |
30408331d30bSWilliam Towle 			BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP),
30418331d30bSWilliam Towle 		.linewidth_mask = 0x1fff,
30428331d30bSWilliam Towle 		.field0_height_mask = 0x1fff,
30438331d30bSWilliam Towle 		.field1_height_mask = 0x1fff,
30448331d30bSWilliam Towle 		.hfrontporch_mask = 0x1fff,
30458331d30bSWilliam Towle 		.hsync_mask = 0x1fff,
30468331d30bSWilliam Towle 		.hbackporch_mask = 0x1fff,
30478331d30bSWilliam Towle 		.field0_vfrontporch_mask = 0x3fff,
30488331d30bSWilliam Towle 		.field0_vsync_mask = 0x3fff,
30498331d30bSWilliam Towle 		.field0_vbackporch_mask = 0x3fff,
30508331d30bSWilliam Towle 		.field1_vfrontporch_mask = 0x3fff,
30518331d30bSWilliam Towle 		.field1_vsync_mask = 0x3fff,
30528331d30bSWilliam Towle 		.field1_vbackporch_mask = 0x3fff,
30538331d30bSWilliam Towle 	},
3054d42010a1SLars-Peter Clausen };
3055d42010a1SLars-Peter Clausen 
30567f099a75SFabian Frederick static const struct i2c_device_id adv76xx_i2c_id[] = {
3057b44b2e06SPablo Anton 	{ "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] },
3058b44b2e06SPablo Anton 	{ "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
30598331d30bSWilliam Towle 	{ "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] },
3060f82f313eSLaurent Pinchart 	{ }
3061f82f313eSLaurent Pinchart };
3062b44b2e06SPablo Anton MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id);
3063f82f313eSLaurent Pinchart 
30647f099a75SFabian Frederick static const struct of_device_id adv76xx_of_id[] __maybe_unused = {
3065b44b2e06SPablo Anton 	{ .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] },
30668331d30bSWilliam Towle 	{ .compatible = "adi,adv7612", .data = &adv76xx_chip_info[ADV7612] },
3067f82f313eSLaurent Pinchart 	{ }
3068f82f313eSLaurent Pinchart };
3069b44b2e06SPablo Anton MODULE_DEVICE_TABLE(of, adv76xx_of_id);
3070f82f313eSLaurent Pinchart 
3071b44b2e06SPablo Anton static int adv76xx_parse_dt(struct adv76xx_state *state)
3072f82f313eSLaurent Pinchart {
3073859969b3SSakari Ailus 	struct v4l2_fwnode_endpoint bus_cfg;
30746fa88045SLaurent Pinchart 	struct device_node *endpoint;
30756fa88045SLaurent Pinchart 	struct device_node *np;
30766fa88045SLaurent Pinchart 	unsigned int flags;
30777f6cd6c4SJavier Martinez Canillas 	int ret;
3078bf9c8227SIan Molton 	u32 v;
30796fa88045SLaurent Pinchart 
3080b44b2e06SPablo Anton 	np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node;
30816fa88045SLaurent Pinchart 
30826fa88045SLaurent Pinchart 	/* Parse the endpoint. */
30836fa88045SLaurent Pinchart 	endpoint = of_graph_get_next_endpoint(np, NULL);
30846fa88045SLaurent Pinchart 	if (!endpoint)
30856fa88045SLaurent Pinchart 		return -EINVAL;
30866fa88045SLaurent Pinchart 
3087859969b3SSakari Ailus 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg);
30887f6cd6c4SJavier Martinez Canillas 	if (ret) {
30897f6cd6c4SJavier Martinez Canillas 		of_node_put(endpoint);
30907f6cd6c4SJavier Martinez Canillas 		return ret;
30917f6cd6c4SJavier Martinez Canillas 	}
3092bf9c8227SIan Molton 
3093c57a68a1SUlrich Hecht 	of_node_put(endpoint);
3094c57a68a1SUlrich Hecht 
3095c57a68a1SUlrich Hecht 	if (!of_property_read_u32(np, "default-input", &v))
3096bf9c8227SIan Molton 		state->pdata.default_input = v;
3097bf9c8227SIan Molton 	else
3098bf9c8227SIan Molton 		state->pdata.default_input = -1;
3099bf9c8227SIan Molton 
31006fa88045SLaurent Pinchart 	flags = bus_cfg.bus.parallel.flags;
31016fa88045SLaurent Pinchart 
31026fa88045SLaurent Pinchart 	if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
31036fa88045SLaurent Pinchart 		state->pdata.inv_hs_pol = 1;
31046fa88045SLaurent Pinchart 
31056fa88045SLaurent Pinchart 	if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
31066fa88045SLaurent Pinchart 		state->pdata.inv_vs_pol = 1;
31076fa88045SLaurent Pinchart 
31086fa88045SLaurent Pinchart 	if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
31096fa88045SLaurent Pinchart 		state->pdata.inv_llc_pol = 1;
31106fa88045SLaurent Pinchart 
3111fd74246dSHans Verkuil 	if (bus_cfg.bus_type == V4L2_MBUS_BT656)
31126fa88045SLaurent Pinchart 		state->pdata.insert_av_codes = 1;
31136fa88045SLaurent Pinchart 
3114f82f313eSLaurent Pinchart 	/* Disable the interrupt for now as no DT-based board uses it. */
3115b44b2e06SPablo Anton 	state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED;
3116f82f313eSLaurent Pinchart 
3117f82f313eSLaurent Pinchart 	/* Use the default I2C addresses. */
3118f82f313eSLaurent Pinchart 	state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42;
3119b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_CEC] = 0x40;
3120b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_INFOFRAME] = 0x3e;
3121f82f313eSLaurent Pinchart 	state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38;
3122f82f313eSLaurent Pinchart 	state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c;
3123b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_AFE] = 0x26;
3124b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_REP] = 0x32;
3125b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_EDID] = 0x36;
3126b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_HDMI] = 0x34;
3127b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_TEST] = 0x30;
3128b44b2e06SPablo Anton 	state->pdata.i2c_addresses[ADV76XX_PAGE_CP] = 0x22;
3129f82f313eSLaurent Pinchart 	state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24;
3130f82f313eSLaurent Pinchart 
3131f82f313eSLaurent Pinchart 	/* Hardcode the remaining platform data fields. */
3132f82f313eSLaurent Pinchart 	state->pdata.disable_pwrdnb = 0;
3133f82f313eSLaurent Pinchart 	state->pdata.disable_cable_det_rst = 0;
3134f82f313eSLaurent Pinchart 	state->pdata.blank_data = 1;
3135f82f313eSLaurent Pinchart 	state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0;
3136f82f313eSLaurent Pinchart 	state->pdata.bus_order = ADV7604_BUS_ORDER_RGB;
3137da8892d4SLars-Peter Clausen 	state->pdata.dr_str_data = ADV76XX_DR_STR_MEDIUM_HIGH;
3138da8892d4SLars-Peter Clausen 	state->pdata.dr_str_clk = ADV76XX_DR_STR_MEDIUM_HIGH;
3139da8892d4SLars-Peter Clausen 	state->pdata.dr_str_sync = ADV76XX_DR_STR_MEDIUM_HIGH;
3140f82f313eSLaurent Pinchart 
3141f82f313eSLaurent Pinchart 	return 0;
3142f82f313eSLaurent Pinchart }
3143f82f313eSLaurent Pinchart 
3144f862f57dSPablo Anton static const struct regmap_config adv76xx_regmap_cnf[] = {
3145f862f57dSPablo Anton 	{
3146f862f57dSPablo Anton 		.name			= "io",
3147f862f57dSPablo Anton 		.reg_bits		= 8,
3148f862f57dSPablo Anton 		.val_bits		= 8,
3149f862f57dSPablo Anton 
3150f862f57dSPablo Anton 		.max_register		= 0xff,
3151f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3152f862f57dSPablo Anton 	},
3153f862f57dSPablo Anton 	{
3154f862f57dSPablo Anton 		.name			= "avlink",
3155f862f57dSPablo Anton 		.reg_bits		= 8,
3156f862f57dSPablo Anton 		.val_bits		= 8,
3157f862f57dSPablo Anton 
3158f862f57dSPablo Anton 		.max_register		= 0xff,
3159f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3160f862f57dSPablo Anton 	},
3161f862f57dSPablo Anton 	{
3162f862f57dSPablo Anton 		.name			= "cec",
3163f862f57dSPablo Anton 		.reg_bits		= 8,
3164f862f57dSPablo Anton 		.val_bits		= 8,
3165f862f57dSPablo Anton 
3166f862f57dSPablo Anton 		.max_register		= 0xff,
3167f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3168f862f57dSPablo Anton 	},
3169f862f57dSPablo Anton 	{
3170f862f57dSPablo Anton 		.name			= "infoframe",
3171f862f57dSPablo Anton 		.reg_bits		= 8,
3172f862f57dSPablo Anton 		.val_bits		= 8,
3173f862f57dSPablo Anton 
3174f862f57dSPablo Anton 		.max_register		= 0xff,
3175f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3176f862f57dSPablo Anton 	},
3177f862f57dSPablo Anton 	{
3178f862f57dSPablo Anton 		.name			= "esdp",
3179f862f57dSPablo Anton 		.reg_bits		= 8,
3180f862f57dSPablo Anton 		.val_bits		= 8,
3181f862f57dSPablo Anton 
3182f862f57dSPablo Anton 		.max_register		= 0xff,
3183f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3184f862f57dSPablo Anton 	},
3185f862f57dSPablo Anton 	{
3186f862f57dSPablo Anton 		.name			= "epp",
3187f862f57dSPablo Anton 		.reg_bits		= 8,
3188f862f57dSPablo Anton 		.val_bits		= 8,
3189f862f57dSPablo Anton 
3190f862f57dSPablo Anton 		.max_register		= 0xff,
3191f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3192f862f57dSPablo Anton 	},
3193f862f57dSPablo Anton 	{
3194f862f57dSPablo Anton 		.name			= "afe",
3195f862f57dSPablo Anton 		.reg_bits		= 8,
3196f862f57dSPablo Anton 		.val_bits		= 8,
3197f862f57dSPablo Anton 
3198f862f57dSPablo Anton 		.max_register		= 0xff,
3199f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3200f862f57dSPablo Anton 	},
3201f862f57dSPablo Anton 	{
3202f862f57dSPablo Anton 		.name			= "rep",
3203f862f57dSPablo Anton 		.reg_bits		= 8,
3204f862f57dSPablo Anton 		.val_bits		= 8,
3205f862f57dSPablo Anton 
3206f862f57dSPablo Anton 		.max_register		= 0xff,
3207f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3208f862f57dSPablo Anton 	},
3209f862f57dSPablo Anton 	{
3210f862f57dSPablo Anton 		.name			= "edid",
3211f862f57dSPablo Anton 		.reg_bits		= 8,
3212f862f57dSPablo Anton 		.val_bits		= 8,
3213f862f57dSPablo Anton 
3214f862f57dSPablo Anton 		.max_register		= 0xff,
3215f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3216f862f57dSPablo Anton 	},
3217f862f57dSPablo Anton 
3218f862f57dSPablo Anton 	{
3219f862f57dSPablo Anton 		.name			= "hdmi",
3220f862f57dSPablo Anton 		.reg_bits		= 8,
3221f862f57dSPablo Anton 		.val_bits		= 8,
3222f862f57dSPablo Anton 
3223f862f57dSPablo Anton 		.max_register		= 0xff,
3224f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3225f862f57dSPablo Anton 	},
3226f862f57dSPablo Anton 	{
3227f862f57dSPablo Anton 		.name			= "test",
3228f862f57dSPablo Anton 		.reg_bits		= 8,
3229f862f57dSPablo Anton 		.val_bits		= 8,
3230f862f57dSPablo Anton 
3231f862f57dSPablo Anton 		.max_register		= 0xff,
3232f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3233f862f57dSPablo Anton 	},
3234f862f57dSPablo Anton 	{
3235f862f57dSPablo Anton 		.name			= "cp",
3236f862f57dSPablo Anton 		.reg_bits		= 8,
3237f862f57dSPablo Anton 		.val_bits		= 8,
3238f862f57dSPablo Anton 
3239f862f57dSPablo Anton 		.max_register		= 0xff,
3240f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3241f862f57dSPablo Anton 	},
3242f862f57dSPablo Anton 	{
3243f862f57dSPablo Anton 		.name			= "vdp",
3244f862f57dSPablo Anton 		.reg_bits		= 8,
3245f862f57dSPablo Anton 		.val_bits		= 8,
3246f862f57dSPablo Anton 
3247f862f57dSPablo Anton 		.max_register		= 0xff,
3248f862f57dSPablo Anton 		.cache_type		= REGCACHE_NONE,
3249f862f57dSPablo Anton 	},
3250f862f57dSPablo Anton };
3251f862f57dSPablo Anton 
3252f862f57dSPablo Anton static int configure_regmap(struct adv76xx_state *state, int region)
3253f862f57dSPablo Anton {
3254f862f57dSPablo Anton 	int err;
3255f862f57dSPablo Anton 
3256f862f57dSPablo Anton 	if (!state->i2c_clients[region])
3257f862f57dSPablo Anton 		return -ENODEV;
3258f862f57dSPablo Anton 
3259f862f57dSPablo Anton 	state->regmap[region] =
3260f862f57dSPablo Anton 		devm_regmap_init_i2c(state->i2c_clients[region],
3261f862f57dSPablo Anton 				     &adv76xx_regmap_cnf[region]);
3262f862f57dSPablo Anton 
3263f862f57dSPablo Anton 	if (IS_ERR(state->regmap[region])) {
3264f862f57dSPablo Anton 		err = PTR_ERR(state->regmap[region]);
3265f862f57dSPablo Anton 		v4l_err(state->i2c_clients[region],
3266f862f57dSPablo Anton 			"Error initializing regmap %d with error %d\n",
3267f862f57dSPablo Anton 			region, err);
3268f862f57dSPablo Anton 		return -EINVAL;
3269f862f57dSPablo Anton 	}
3270f862f57dSPablo Anton 
3271f862f57dSPablo Anton 	return 0;
3272f862f57dSPablo Anton }
3273f862f57dSPablo Anton 
3274f862f57dSPablo Anton static int configure_regmaps(struct adv76xx_state *state)
3275f862f57dSPablo Anton {
3276f862f57dSPablo Anton 	int i, err;
3277f862f57dSPablo Anton 
3278f862f57dSPablo Anton 	for (i = ADV7604_PAGE_AVLINK ; i < ADV76XX_PAGE_MAX; i++) {
3279f862f57dSPablo Anton 		err = configure_regmap(state, i);
3280f862f57dSPablo Anton 		if (err && (err != -ENODEV))
3281f862f57dSPablo Anton 			return err;
3282f862f57dSPablo Anton 	}
3283f862f57dSPablo Anton 	return 0;
3284f862f57dSPablo Anton }
3285f862f57dSPablo Anton 
3286f5591da9SDragos Bogdan static void adv76xx_reset(struct adv76xx_state *state)
3287f5591da9SDragos Bogdan {
3288f5591da9SDragos Bogdan 	if (state->reset_gpio) {
3289f5591da9SDragos Bogdan 		/* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */
3290f5591da9SDragos Bogdan 		gpiod_set_value_cansleep(state->reset_gpio, 0);
3291f5591da9SDragos Bogdan 		usleep_range(5000, 10000);
3292f5591da9SDragos Bogdan 		gpiod_set_value_cansleep(state->reset_gpio, 1);
3293f5591da9SDragos Bogdan 		/* It is recommended to wait 5 ms after the low pulse before */
3294f5591da9SDragos Bogdan 		/* an I2C write is performed to the ADV76XX. */
3295f5591da9SDragos Bogdan 		usleep_range(5000, 10000);
3296f5591da9SDragos Bogdan 	}
3297f5591da9SDragos Bogdan }
3298f5591da9SDragos Bogdan 
3299b44b2e06SPablo Anton static int adv76xx_probe(struct i2c_client *client,
330054450f59SHans Verkuil 			 const struct i2c_device_id *id)
330154450f59SHans Verkuil {
3302591b72feSHans Verkuil 	static const struct v4l2_dv_timings cea640x480 =
3303591b72feSHans Verkuil 		V4L2_DV_BT_CEA_640X480P59_94;
3304b44b2e06SPablo Anton 	struct adv76xx_state *state;
330554450f59SHans Verkuil 	struct v4l2_ctrl_handler *hdl;
3306297a4144SHans Verkuil 	struct v4l2_ctrl *ctrl;
330754450f59SHans Verkuil 	struct v4l2_subdev *sd;
3308c784b1e2SLaurent Pinchart 	unsigned int i;
3309f862f57dSPablo Anton 	unsigned int val, val2;
331054450f59SHans Verkuil 	int err;
331154450f59SHans Verkuil 
331254450f59SHans Verkuil 	/* Check if the adapter supports the needed features */
331354450f59SHans Verkuil 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
331454450f59SHans Verkuil 		return -EIO;
3315b44b2e06SPablo Anton 	v4l_dbg(1, debug, client, "detecting adv76xx client on address 0x%x\n",
331654450f59SHans Verkuil 			client->addr << 1);
331754450f59SHans Verkuil 
3318c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
331954450f59SHans Verkuil 	if (!state) {
3320b44b2e06SPablo Anton 		v4l_err(client, "Could not allocate adv76xx_state memory!\n");
332154450f59SHans Verkuil 		return -ENOMEM;
332254450f59SHans Verkuil 	}
332354450f59SHans Verkuil 
3324b44b2e06SPablo Anton 	state->i2c_clients[ADV76XX_PAGE_IO] = client;
3325d42010a1SLars-Peter Clausen 
332625a64ac9SMats Randgaard 	/* initialize variables */
332725a64ac9SMats Randgaard 	state->restart_stdi_once = true;
3328ff4f80fdSMats Randgaard 	state->selected_input = ~0;
332925a64ac9SMats Randgaard 
3330f82f313eSLaurent Pinchart 	if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
3331f82f313eSLaurent Pinchart 		const struct of_device_id *oid;
3332f82f313eSLaurent Pinchart 
3333b44b2e06SPablo Anton 		oid = of_match_node(adv76xx_of_id, client->dev.of_node);
3334f82f313eSLaurent Pinchart 		state->info = oid->data;
3335f82f313eSLaurent Pinchart 
3336b44b2e06SPablo Anton 		err = adv76xx_parse_dt(state);
3337f82f313eSLaurent Pinchart 		if (err < 0) {
3338f82f313eSLaurent Pinchart 			v4l_err(client, "DT parsing error\n");
3339f82f313eSLaurent Pinchart 			return err;
3340f82f313eSLaurent Pinchart 		}
3341f82f313eSLaurent Pinchart 	} else if (client->dev.platform_data) {
3342b44b2e06SPablo Anton 		struct adv76xx_platform_data *pdata = client->dev.platform_data;
3343f82f313eSLaurent Pinchart 
3344b44b2e06SPablo Anton 		state->info = (const struct adv76xx_chip_info *)id->driver_data;
3345f82f313eSLaurent Pinchart 		state->pdata = *pdata;
3346f82f313eSLaurent Pinchart 	} else {
334754450f59SHans Verkuil 		v4l_err(client, "No platform data!\n");
3348c02b211dSLaurent Pinchart 		return -ENODEV;
334954450f59SHans Verkuil 	}
3350e9d50e9eSLaurent Pinchart 
3351e9d50e9eSLaurent Pinchart 	/* Request GPIOs. */
3352e9d50e9eSLaurent Pinchart 	for (i = 0; i < state->info->num_dv_ports; ++i) {
3353e9d50e9eSLaurent Pinchart 		state->hpd_gpio[i] =
3354269bd132SUwe Kleine-König 			devm_gpiod_get_index_optional(&client->dev, "hpd", i,
3355269bd132SUwe Kleine-König 						      GPIOD_OUT_LOW);
3356e9d50e9eSLaurent Pinchart 		if (IS_ERR(state->hpd_gpio[i]))
3357269bd132SUwe Kleine-König 			return PTR_ERR(state->hpd_gpio[i]);
3358e9d50e9eSLaurent Pinchart 
3359269bd132SUwe Kleine-König 		if (state->hpd_gpio[i])
3360e9d50e9eSLaurent Pinchart 			v4l_info(client, "Handling HPD %u GPIO\n", i);
3361e9d50e9eSLaurent Pinchart 	}
3362f5591da9SDragos Bogdan 	state->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
3363f5591da9SDragos Bogdan 								GPIOD_OUT_HIGH);
3364f5591da9SDragos Bogdan 	if (IS_ERR(state->reset_gpio))
3365f5591da9SDragos Bogdan 		return PTR_ERR(state->reset_gpio);
3366f5591da9SDragos Bogdan 
3367f5591da9SDragos Bogdan 	adv76xx_reset(state);
3368e9d50e9eSLaurent Pinchart 
3369591b72feSHans Verkuil 	state->timings = cea640x480;
3370b44b2e06SPablo Anton 	state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
337154450f59SHans Verkuil 
337254450f59SHans Verkuil 	sd = &state->sd;
3373b44b2e06SPablo Anton 	v4l2_i2c_subdev_init(sd, client, &adv76xx_ops);
3374d42010a1SLars-Peter Clausen 	snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
3375d42010a1SLars-Peter Clausen 		id->name, i2c_adapter_id(client->adapter),
3376d42010a1SLars-Peter Clausen 		client->addr);
33770975626dSLars-Peter Clausen 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
337841a52373SHans Verkuil 	sd->internal_ops = &adv76xx_int_ops;
337954450f59SHans Verkuil 
3380f862f57dSPablo Anton 	/* Configure IO Regmap region */
3381f862f57dSPablo Anton 	err = configure_regmap(state, ADV76XX_PAGE_IO);
3382f862f57dSPablo Anton 
3383f862f57dSPablo Anton 	if (err) {
3384f862f57dSPablo Anton 		v4l2_err(sd, "Error configuring IO regmap region\n");
3385f862f57dSPablo Anton 		return -ENODEV;
3386f862f57dSPablo Anton 	}
3387f862f57dSPablo Anton 
3388d42010a1SLars-Peter Clausen 	/*
3389d42010a1SLars-Peter Clausen 	 * Verify that the chip is present. On ADV7604 the RD_INFO register only
3390d42010a1SLars-Peter Clausen 	 * identifies the revision, while on ADV7611 it identifies the model as
3391d42010a1SLars-Peter Clausen 	 * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611.
3392d42010a1SLars-Peter Clausen 	 */
33938331d30bSWilliam Towle 	switch (state->info->type) {
33948331d30bSWilliam Towle 	case ADV7604:
3395f862f57dSPablo Anton 		err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xfb, &val);
3396f862f57dSPablo Anton 		if (err) {
3397f862f57dSPablo Anton 			v4l2_err(sd, "Error %d reading IO Regmap\n", err);
3398f862f57dSPablo Anton 			return -ENODEV;
3399f862f57dSPablo Anton 		}
3400d42010a1SLars-Peter Clausen 		if (val != 0x68) {
3401f862f57dSPablo Anton 			v4l2_err(sd, "not an adv7604 on address 0x%x\n",
340254450f59SHans Verkuil 					client->addr << 1);
3403c02b211dSLaurent Pinchart 			return -ENODEV;
340454450f59SHans Verkuil 		}
34058331d30bSWilliam Towle 		break;
34068331d30bSWilliam Towle 	case ADV7611:
34078331d30bSWilliam Towle 	case ADV7612:
3408f862f57dSPablo Anton 		err = regmap_read(state->regmap[ADV76XX_PAGE_IO],
3409f862f57dSPablo Anton 				0xea,
3410f862f57dSPablo Anton 				&val);
3411f862f57dSPablo Anton 		if (err) {
3412f862f57dSPablo Anton 			v4l2_err(sd, "Error %d reading IO Regmap\n", err);
3413f862f57dSPablo Anton 			return -ENODEV;
3414f862f57dSPablo Anton 		}
3415f862f57dSPablo Anton 		val2 = val << 8;
3416f862f57dSPablo Anton 		err = regmap_read(state->regmap[ADV76XX_PAGE_IO],
3417f862f57dSPablo Anton 			    0xeb,
3418f862f57dSPablo Anton 			    &val);
3419f862f57dSPablo Anton 		if (err) {
3420f862f57dSPablo Anton 			v4l2_err(sd, "Error %d reading IO Regmap\n", err);
3421f862f57dSPablo Anton 			return -ENODEV;
3422f862f57dSPablo Anton 		}
3423c1362384SWilliam Towle 		val |= val2;
34248331d30bSWilliam Towle 		if ((state->info->type == ADV7611 && val != 0x2051) ||
34258331d30bSWilliam Towle 			(state->info->type == ADV7612 && val != 0x2041)) {
34268331d30bSWilliam Towle 			v4l2_err(sd, "not an adv761x on address 0x%x\n",
3427d42010a1SLars-Peter Clausen 					client->addr << 1);
3428d42010a1SLars-Peter Clausen 			return -ENODEV;
3429d42010a1SLars-Peter Clausen 		}
34308331d30bSWilliam Towle 		break;
3431d42010a1SLars-Peter Clausen 	}
343254450f59SHans Verkuil 
343354450f59SHans Verkuil 	/* control handlers */
343454450f59SHans Verkuil 	hdl = &state->hdl;
3435b44b2e06SPablo Anton 	v4l2_ctrl_handler_init(hdl, adv76xx_has_afe(state) ? 9 : 8);
343654450f59SHans Verkuil 
3437b44b2e06SPablo Anton 	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
343854450f59SHans Verkuil 			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
3439b44b2e06SPablo Anton 	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
344054450f59SHans Verkuil 			V4L2_CID_CONTRAST, 0, 255, 1, 128);
3441b44b2e06SPablo Anton 	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
344254450f59SHans Verkuil 			V4L2_CID_SATURATION, 0, 255, 1, 128);
3443b44b2e06SPablo Anton 	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
344454450f59SHans Verkuil 			V4L2_CID_HUE, 0, 128, 1, 0);
3445297a4144SHans Verkuil 	ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops,
3446297a4144SHans Verkuil 			V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC,
3447297a4144SHans Verkuil 			0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
3448297a4144SHans Verkuil 	if (ctrl)
3449297a4144SHans Verkuil 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
345054450f59SHans Verkuil 
345154450f59SHans Verkuil 	state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
3452d42010a1SLars-Peter Clausen 			V4L2_CID_DV_RX_POWER_PRESENT, 0,
3453d42010a1SLars-Peter Clausen 			(1 << state->info->num_dv_ports) - 1, 0, 0);
345454450f59SHans Verkuil 	state->rgb_quantization_range_ctrl =
3455b44b2e06SPablo Anton 		v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops,
345654450f59SHans Verkuil 			V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
345754450f59SHans Verkuil 			0, V4L2_DV_RGB_RANGE_AUTO);
345854450f59SHans Verkuil 
345954450f59SHans Verkuil 	/* custom controls */
3460b44b2e06SPablo Anton 	if (adv76xx_has_afe(state))
346154450f59SHans Verkuil 		state->analog_sampling_phase_ctrl =
346254450f59SHans Verkuil 			v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
346354450f59SHans Verkuil 	state->free_run_color_manual_ctrl =
3464b44b2e06SPablo Anton 		v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color_manual, NULL);
346554450f59SHans Verkuil 	state->free_run_color_ctrl =
3466b44b2e06SPablo Anton 		v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color, NULL);
346754450f59SHans Verkuil 
346854450f59SHans Verkuil 	sd->ctrl_handler = hdl;
346954450f59SHans Verkuil 	if (hdl->error) {
347054450f59SHans Verkuil 		err = hdl->error;
347154450f59SHans Verkuil 		goto err_hdl;
347254450f59SHans Verkuil 	}
3473b44b2e06SPablo Anton 	if (adv76xx_s_detect_tx_5v_ctrl(sd)) {
347454450f59SHans Verkuil 		err = -ENODEV;
347554450f59SHans Verkuil 		goto err_hdl;
347654450f59SHans Verkuil 	}
347754450f59SHans Verkuil 
3478b44b2e06SPablo Anton 	for (i = 1; i < ADV76XX_PAGE_MAX; ++i) {
347905cacb17SLaurent Pinchart 		if (!(BIT(i) & state->info->page_mask))
348005cacb17SLaurent Pinchart 			continue;
348105cacb17SLaurent Pinchart 
348205cacb17SLaurent Pinchart 		state->i2c_clients[i] =
3483b44b2e06SPablo Anton 			adv76xx_dummy_client(sd, state->pdata.i2c_addresses[i],
348405cacb17SLaurent Pinchart 					     0xf2 + i);
348505cacb17SLaurent Pinchart 		if (state->i2c_clients[i] == NULL) {
348654450f59SHans Verkuil 			err = -ENOMEM;
348705cacb17SLaurent Pinchart 			v4l2_err(sd, "failed to create i2c client %u\n", i);
348854450f59SHans Verkuil 			goto err_i2c;
348954450f59SHans Verkuil 		}
349005cacb17SLaurent Pinchart 	}
349154450f59SHans Verkuil 
349254450f59SHans Verkuil 	INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,
3493b44b2e06SPablo Anton 			adv76xx_delayed_work_enable_hotplug);
349454450f59SHans Verkuil 
3495c784b1e2SLaurent Pinchart 	state->source_pad = state->info->num_dv_ports
3496c784b1e2SLaurent Pinchart 			  + (state->info->has_afe ? 2 : 0);
3497c784b1e2SLaurent Pinchart 	for (i = 0; i < state->source_pad; ++i)
3498c784b1e2SLaurent Pinchart 		state->pads[i].flags = MEDIA_PAD_FL_SINK;
3499c784b1e2SLaurent Pinchart 	state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE;
3500c784b1e2SLaurent Pinchart 
3501ab22e77cSMauro Carvalho Chehab 	err = media_entity_pads_init(&sd->entity, state->source_pad + 1,
350218095107SMauro Carvalho Chehab 				state->pads);
350354450f59SHans Verkuil 	if (err)
350454450f59SHans Verkuil 		goto err_work_queues;
350554450f59SHans Verkuil 
3506f862f57dSPablo Anton 	/* Configure regmaps */
3507f862f57dSPablo Anton 	err = configure_regmaps(state);
3508f862f57dSPablo Anton 	if (err)
3509f862f57dSPablo Anton 		goto err_entity;
3510f862f57dSPablo Anton 
3511b44b2e06SPablo Anton 	err = adv76xx_core_init(sd);
351254450f59SHans Verkuil 	if (err)
351354450f59SHans Verkuil 		goto err_entity;
351441a52373SHans Verkuil 
351541a52373SHans Verkuil #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
351641a52373SHans Verkuil 	state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
351741a52373SHans Verkuil 		state, dev_name(&client->dev),
351857b79636SHans Verkuil 		CEC_CAP_DEFAULTS, ADV76XX_MAX_ADDRS);
351941a52373SHans Verkuil 	err = PTR_ERR_OR_ZERO(state->cec_adap);
352041a52373SHans Verkuil 	if (err)
352141a52373SHans Verkuil 		goto err_entity;
352241a52373SHans Verkuil #endif
352341a52373SHans Verkuil 
352454450f59SHans Verkuil 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
352554450f59SHans Verkuil 			client->addr << 1, client->adapter->name);
3526bedc3939SLars-Peter Clausen 
3527bedc3939SLars-Peter Clausen 	err = v4l2_async_register_subdev(sd);
3528bedc3939SLars-Peter Clausen 	if (err)
3529bedc3939SLars-Peter Clausen 		goto err_entity;
3530bedc3939SLars-Peter Clausen 
353154450f59SHans Verkuil 	return 0;
353254450f59SHans Verkuil 
353354450f59SHans Verkuil err_entity:
353454450f59SHans Verkuil 	media_entity_cleanup(&sd->entity);
353554450f59SHans Verkuil err_work_queues:
353654450f59SHans Verkuil 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
353754450f59SHans Verkuil err_i2c:
3538b44b2e06SPablo Anton 	adv76xx_unregister_clients(state);
353954450f59SHans Verkuil err_hdl:
354054450f59SHans Verkuil 	v4l2_ctrl_handler_free(hdl);
354154450f59SHans Verkuil 	return err;
354254450f59SHans Verkuil }
354354450f59SHans Verkuil 
354454450f59SHans Verkuil /* ----------------------------------------------------------------------- */
354554450f59SHans Verkuil 
3546b44b2e06SPablo Anton static int adv76xx_remove(struct i2c_client *client)
354754450f59SHans Verkuil {
354854450f59SHans Verkuil 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
3549b44b2e06SPablo Anton 	struct adv76xx_state *state = to_state(sd);
355054450f59SHans Verkuil 
355141a52373SHans Verkuil 	/* disable interrupts */
355241a52373SHans Verkuil 	io_write(sd, 0x40, 0);
355341a52373SHans Verkuil 	io_write(sd, 0x41, 0);
355441a52373SHans Verkuil 	io_write(sd, 0x46, 0);
355541a52373SHans Verkuil 	io_write(sd, 0x6e, 0);
355641a52373SHans Verkuil 	io_write(sd, 0x73, 0);
355741a52373SHans Verkuil 
355854450f59SHans Verkuil 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
3559bedc3939SLars-Peter Clausen 	v4l2_async_unregister_subdev(sd);
356054450f59SHans Verkuil 	media_entity_cleanup(&sd->entity);
3561b44b2e06SPablo Anton 	adv76xx_unregister_clients(to_state(sd));
356254450f59SHans Verkuil 	v4l2_ctrl_handler_free(sd->ctrl_handler);
356354450f59SHans Verkuil 	return 0;
356454450f59SHans Verkuil }
356554450f59SHans Verkuil 
356654450f59SHans Verkuil /* ----------------------------------------------------------------------- */
356754450f59SHans Verkuil 
3568b44b2e06SPablo Anton static struct i2c_driver adv76xx_driver = {
356954450f59SHans Verkuil 	.driver = {
357054450f59SHans Verkuil 		.name = "adv7604",
3571b44b2e06SPablo Anton 		.of_match_table = of_match_ptr(adv76xx_of_id),
357254450f59SHans Verkuil 	},
3573b44b2e06SPablo Anton 	.probe = adv76xx_probe,
3574b44b2e06SPablo Anton 	.remove = adv76xx_remove,
3575b44b2e06SPablo Anton 	.id_table = adv76xx_i2c_id,
357654450f59SHans Verkuil };
357754450f59SHans Verkuil 
3578b44b2e06SPablo Anton module_i2c_driver(adv76xx_driver);
3579