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