18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * adv7604 - Analog Devices ADV7604 video decoder driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * References (c = chapter, p = page): 118c2ecf20Sopenharmony_ci * REF_01 - Analog devices, ADV7604, Register Settings Recommendations, 128c2ecf20Sopenharmony_ci * Revision 2.5, June 2010 138c2ecf20Sopenharmony_ci * REF_02 - Analog devices, Register map documentation, Documentation of 148c2ecf20Sopenharmony_ci * the register maps, Software manual, Rev. F, June 2010 158c2ecf20Sopenharmony_ci * REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 218c2ecf20Sopenharmony_ci#include <linux/i2c.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 278c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 288c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 298c2ecf20Sopenharmony_ci#include <linux/regmap.h> 308c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <media/i2c/adv7604.h> 338c2ecf20Sopenharmony_ci#include <media/cec.h> 348c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 358c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 368c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 378c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 388c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int debug; 418c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-2)"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices ADV7604 video decoder driver"); 458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); 468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>"); 478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* ADV7604 system clock frequency */ 508c2ecf20Sopenharmony_ci#define ADV76XX_FSC (28636360) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define ADV76XX_RGB_OUT (1 << 1) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define ADV76XX_OP_FORMAT_SEL_8BIT (0 << 0) 558c2ecf20Sopenharmony_ci#define ADV7604_OP_FORMAT_SEL_10BIT (1 << 0) 568c2ecf20Sopenharmony_ci#define ADV76XX_OP_FORMAT_SEL_12BIT (2 << 0) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define ADV76XX_OP_MODE_SEL_SDR_422 (0 << 5) 598c2ecf20Sopenharmony_ci#define ADV7604_OP_MODE_SEL_DDR_422 (1 << 5) 608c2ecf20Sopenharmony_ci#define ADV76XX_OP_MODE_SEL_SDR_444 (2 << 5) 618c2ecf20Sopenharmony_ci#define ADV7604_OP_MODE_SEL_DDR_444 (3 << 5) 628c2ecf20Sopenharmony_ci#define ADV76XX_OP_MODE_SEL_SDR_422_2X (4 << 5) 638c2ecf20Sopenharmony_ci#define ADV7604_OP_MODE_SEL_ADI_CM (5 << 5) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define ADV76XX_OP_CH_SEL_GBR (0 << 5) 668c2ecf20Sopenharmony_ci#define ADV76XX_OP_CH_SEL_GRB (1 << 5) 678c2ecf20Sopenharmony_ci#define ADV76XX_OP_CH_SEL_BGR (2 << 5) 688c2ecf20Sopenharmony_ci#define ADV76XX_OP_CH_SEL_RGB (3 << 5) 698c2ecf20Sopenharmony_ci#define ADV76XX_OP_CH_SEL_BRG (4 << 5) 708c2ecf20Sopenharmony_ci#define ADV76XX_OP_CH_SEL_RBG (5 << 5) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define ADV76XX_OP_SWAP_CB_CR (1 << 0) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define ADV76XX_MAX_ADDRS (3) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cienum adv76xx_type { 778c2ecf20Sopenharmony_ci ADV7604, 788c2ecf20Sopenharmony_ci ADV7611, 798c2ecf20Sopenharmony_ci ADV7612, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct adv76xx_reg_seq { 838c2ecf20Sopenharmony_ci unsigned int reg; 848c2ecf20Sopenharmony_ci u8 val; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct adv76xx_format_info { 888c2ecf20Sopenharmony_ci u32 code; 898c2ecf20Sopenharmony_ci u8 op_ch_sel; 908c2ecf20Sopenharmony_ci bool rgb_out; 918c2ecf20Sopenharmony_ci bool swap_cb_cr; 928c2ecf20Sopenharmony_ci u8 op_format_sel; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct adv76xx_cfg_read_infoframe { 968c2ecf20Sopenharmony_ci const char *desc; 978c2ecf20Sopenharmony_ci u8 present_mask; 988c2ecf20Sopenharmony_ci u8 head_addr; 998c2ecf20Sopenharmony_ci u8 payload_addr; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct adv76xx_chip_info { 1038c2ecf20Sopenharmony_ci enum adv76xx_type type; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci bool has_afe; 1068c2ecf20Sopenharmony_ci unsigned int max_port; 1078c2ecf20Sopenharmony_ci unsigned int num_dv_ports; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci unsigned int edid_enable_reg; 1108c2ecf20Sopenharmony_ci unsigned int edid_status_reg; 1118c2ecf20Sopenharmony_ci unsigned int lcf_reg; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci unsigned int cable_det_mask; 1148c2ecf20Sopenharmony_ci unsigned int tdms_lock_mask; 1158c2ecf20Sopenharmony_ci unsigned int fmt_change_digital_mask; 1168c2ecf20Sopenharmony_ci unsigned int cp_csc; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci unsigned int cec_irq_status; 1198c2ecf20Sopenharmony_ci unsigned int cec_rx_enable; 1208c2ecf20Sopenharmony_ci unsigned int cec_rx_enable_mask; 1218c2ecf20Sopenharmony_ci bool cec_irq_swap; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci const struct adv76xx_format_info *formats; 1248c2ecf20Sopenharmony_ci unsigned int nformats; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci void (*set_termination)(struct v4l2_subdev *sd, bool enable); 1278c2ecf20Sopenharmony_ci void (*setup_irqs)(struct v4l2_subdev *sd); 1288c2ecf20Sopenharmony_ci unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd); 1298c2ecf20Sopenharmony_ci unsigned int (*read_cable_det)(struct v4l2_subdev *sd); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* 0 = AFE, 1 = HDMI */ 1328c2ecf20Sopenharmony_ci const struct adv76xx_reg_seq *recommended_settings[2]; 1338c2ecf20Sopenharmony_ci unsigned int num_recommended_settings[2]; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci unsigned long page_mask; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Masks for timings */ 1388c2ecf20Sopenharmony_ci unsigned int linewidth_mask; 1398c2ecf20Sopenharmony_ci unsigned int field0_height_mask; 1408c2ecf20Sopenharmony_ci unsigned int field1_height_mask; 1418c2ecf20Sopenharmony_ci unsigned int hfrontporch_mask; 1428c2ecf20Sopenharmony_ci unsigned int hsync_mask; 1438c2ecf20Sopenharmony_ci unsigned int hbackporch_mask; 1448c2ecf20Sopenharmony_ci unsigned int field0_vfrontporch_mask; 1458c2ecf20Sopenharmony_ci unsigned int field1_vfrontporch_mask; 1468c2ecf20Sopenharmony_ci unsigned int field0_vsync_mask; 1478c2ecf20Sopenharmony_ci unsigned int field1_vsync_mask; 1488c2ecf20Sopenharmony_ci unsigned int field0_vbackporch_mask; 1498c2ecf20Sopenharmony_ci unsigned int field1_vbackporch_mask; 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci ********************************************************************** 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * Arrays with configuration parameters for the ADV7604 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci ********************************************************************** 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistruct adv76xx_state { 1618c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info; 1628c2ecf20Sopenharmony_ci struct adv76xx_platform_data pdata; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci struct gpio_desc *hpd_gpio[4]; 1658c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 1688c2ecf20Sopenharmony_ci struct media_pad pads[ADV76XX_PAD_MAX]; 1698c2ecf20Sopenharmony_ci unsigned int source_pad; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci enum adv76xx_pad selected_input; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 1768c2ecf20Sopenharmony_ci const struct adv76xx_format_info *format; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci struct { 1798c2ecf20Sopenharmony_ci u8 edid[256]; 1808c2ecf20Sopenharmony_ci u32 present; 1818c2ecf20Sopenharmony_ci unsigned blocks; 1828c2ecf20Sopenharmony_ci } edid; 1838c2ecf20Sopenharmony_ci u16 spa_port_a[2]; 1848c2ecf20Sopenharmony_ci struct v4l2_fract aspect_ratio; 1858c2ecf20Sopenharmony_ci u32 rgb_quantization_range; 1868c2ecf20Sopenharmony_ci struct delayed_work delayed_work_enable_hotplug; 1878c2ecf20Sopenharmony_ci bool restart_stdi_once; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* CEC */ 1908c2ecf20Sopenharmony_ci struct cec_adapter *cec_adap; 1918c2ecf20Sopenharmony_ci u8 cec_addr[ADV76XX_MAX_ADDRS]; 1928c2ecf20Sopenharmony_ci u8 cec_valid_addrs; 1938c2ecf20Sopenharmony_ci bool cec_enabled_adap; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* i2c clients */ 1968c2ecf20Sopenharmony_ci struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX]; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Regmaps */ 1998c2ecf20Sopenharmony_ci struct regmap *regmap[ADV76XX_PAGE_MAX]; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* controls */ 2028c2ecf20Sopenharmony_ci struct v4l2_ctrl *detect_tx_5v_ctrl; 2038c2ecf20Sopenharmony_ci struct v4l2_ctrl *analog_sampling_phase_ctrl; 2048c2ecf20Sopenharmony_ci struct v4l2_ctrl *free_run_color_manual_ctrl; 2058c2ecf20Sopenharmony_ci struct v4l2_ctrl *free_run_color_ctrl; 2068c2ecf20Sopenharmony_ci struct v4l2_ctrl *rgb_quantization_range_ctrl; 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic bool adv76xx_has_afe(struct adv76xx_state *state) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci return state->info->has_afe; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* Unsupported timings. This device cannot support 720p30. */ 2158c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings adv76xx_timings_exceptions[] = { 2168c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P30, 2178c2ecf20Sopenharmony_ci { } 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic bool adv76xx_check_dv_timings(const struct v4l2_dv_timings *t, void *hdl) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci int i; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci for (i = 0; adv76xx_timings_exceptions[i].bt.width; i++) 2258c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(t, adv76xx_timings_exceptions + i, 0, false)) 2268c2ecf20Sopenharmony_ci return false; 2278c2ecf20Sopenharmony_ci return true; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistruct adv76xx_video_standards { 2318c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 2328c2ecf20Sopenharmony_ci u8 vid_std; 2338c2ecf20Sopenharmony_ci u8 v_freq; 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* sorted by number of lines */ 2378c2ecf20Sopenharmony_cistatic const struct adv76xx_video_standards adv7604_prim_mode_comp[] = { 2388c2ecf20Sopenharmony_ci /* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */ 2398c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, 2408c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 }, 2418c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1280X720P60, 0x19, 0x00 }, 2428c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, 2438c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, 2448c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, 2458c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, 2468c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, 2478c2ecf20Sopenharmony_ci /* TODO add 1920x1080P60_RB (CVT timing) */ 2488c2ecf20Sopenharmony_ci { }, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* sorted by number of lines */ 2528c2ecf20Sopenharmony_cistatic const struct adv76xx_video_standards adv7604_prim_mode_gr[] = { 2538c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, 2548c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, 2558c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, 2568c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, 2578c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, 2588c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, 2598c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, 2608c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, 2618c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, 2628c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, 2638c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, 2648c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, 2658c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, 2668c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, 2678c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, 2688c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1360X768P60, 0x12, 0x00 }, 2698c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1366X768P60, 0x13, 0x00 }, 2708c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1400X1050P60, 0x14, 0x00 }, 2718c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1400X1050P75, 0x15, 0x00 }, 2728c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1600X1200P60, 0x16, 0x00 }, /* TODO not tested */ 2738c2ecf20Sopenharmony_ci /* TODO add 1600X1200P60_RB (not a DMT timing) */ 2748c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1680X1050P60, 0x18, 0x00 }, 2758c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1920X1200P60_RB, 0x19, 0x00 }, /* TODO not tested */ 2768c2ecf20Sopenharmony_ci { }, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* sorted by number of lines */ 2808c2ecf20Sopenharmony_cistatic const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_comp[] = { 2818c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, 2828c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, 2838c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 }, 2848c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1280X720P60, 0x13, 0x00 }, 2858c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, 2868c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, 2878c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, 2888c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, 2898c2ecf20Sopenharmony_ci { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, 2908c2ecf20Sopenharmony_ci { }, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* sorted by number of lines */ 2948c2ecf20Sopenharmony_cistatic const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_gr[] = { 2958c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, 2968c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, 2978c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, 2988c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, 2998c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, 3008c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, 3018c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, 3028c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, 3038c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, 3048c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, 3058c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, 3068c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, 3078c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, 3088c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, 3098c2ecf20Sopenharmony_ci { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, 3108c2ecf20Sopenharmony_ci { }, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic const struct v4l2_event adv76xx_ev_fmt = { 3148c2ecf20Sopenharmony_ci .type = V4L2_EVENT_SOURCE_CHANGE, 3158c2ecf20Sopenharmony_ci .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic inline struct adv76xx_state *to_state(struct v4l2_subdev *sd) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci return container_of(sd, struct adv76xx_state, sd); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic inline unsigned htotal(const struct v4l2_bt_timings *t) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci return V4L2_DV_BT_FRAME_WIDTH(t); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic inline unsigned vtotal(const struct v4l2_bt_timings *t) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci return V4L2_DV_BT_FRAME_HEIGHT(t); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int adv76xx_read_check(struct adv76xx_state *state, 3388c2ecf20Sopenharmony_ci int client_page, u8 reg) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct i2c_client *client = state->i2c_clients[client_page]; 3418c2ecf20Sopenharmony_ci int err; 3428c2ecf20Sopenharmony_ci unsigned int val; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci err = regmap_read(state->regmap[client_page], reg, &val); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (err) { 3478c2ecf20Sopenharmony_ci v4l_err(client, "error reading %02x, %02x\n", 3488c2ecf20Sopenharmony_ci client->addr, reg); 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci return val; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* adv76xx_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX 3558c2ecf20Sopenharmony_ci * size to one or more registers. 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * A value of zero will be returned on success, a negative errno will 3588c2ecf20Sopenharmony_ci * be returned in error cases. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_cistatic int adv76xx_write_block(struct adv76xx_state *state, int client_page, 3618c2ecf20Sopenharmony_ci unsigned int init_reg, const void *val, 3628c2ecf20Sopenharmony_ci size_t val_len) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct regmap *regmap = state->regmap[client_page]; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (val_len > I2C_SMBUS_BLOCK_MAX) 3678c2ecf20Sopenharmony_ci val_len = I2C_SMBUS_BLOCK_MAX; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return regmap_raw_write(regmap, init_reg, val, val_len); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic inline int io_read(struct v4l2_subdev *sd, u8 reg) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_IO, reg); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, 3898c2ecf20Sopenharmony_ci u8 val) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic inline int avlink_read(struct v4l2_subdev *sd, u8 reg) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV7604_PAGE_AVLINK, reg); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV7604_PAGE_AVLINK], reg, val); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic inline int cec_read(struct v4l2_subdev *sd, u8 reg) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_CEC, reg); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, 4238c2ecf20Sopenharmony_ci u8 val) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_INFOFRAME, reg); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_INFOFRAME], reg, val); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic inline int afe_read(struct v4l2_subdev *sd, u8 reg) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_AFE, reg); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_AFE], reg, val); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic inline int rep_read(struct v4l2_subdev *sd, u8 reg) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_REP, reg); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_REP], reg, val); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci return rep_write(sd, reg, (rep_read(sd, reg) & ~mask) | val); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic inline int edid_read(struct v4l2_subdev *sd, u8 reg) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_EDID, reg); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_EDID], reg, val); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic inline int edid_write_block(struct v4l2_subdev *sd, 4908c2ecf20Sopenharmony_ci unsigned int total_len, const u8 *val) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 4938c2ecf20Sopenharmony_ci int err = 0; 4948c2ecf20Sopenharmony_ci int i = 0; 4958c2ecf20Sopenharmony_ci int len = 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", 4988c2ecf20Sopenharmony_ci __func__, total_len); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci while (!err && i < total_len) { 5018c2ecf20Sopenharmony_ci len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ? 5028c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX : 5038c2ecf20Sopenharmony_ci (total_len - i); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci err = adv76xx_write_block(state, ADV76XX_PAGE_EDID, 5068c2ecf20Sopenharmony_ci i, val + i, len); 5078c2ecf20Sopenharmony_ci i += len; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return err; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void adv76xx_set_hpd(struct adv76xx_state *state, unsigned int hpd) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci unsigned int i; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci for (i = 0; i < state->info->num_dv_ports; ++i) 5188c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i)); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci v4l2_subdev_notify(&state->sd, ADV76XX_HOTPLUG, &hpd); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic void adv76xx_delayed_work_enable_hotplug(struct work_struct *work) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 5268c2ecf20Sopenharmony_ci struct adv76xx_state *state = container_of(dwork, struct adv76xx_state, 5278c2ecf20Sopenharmony_ci delayed_work_enable_hotplug); 5288c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci adv76xx_set_hpd(state, state->edid.present); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_HDMI, reg); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci return ((hdmi_read(sd, reg) << 8) | hdmi_read(sd, reg + 1)) & mask; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_HDMI], reg, val); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_TEST], reg, val); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic inline int cp_read(struct v4l2_subdev *sd, u8 reg) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV76XX_PAGE_CP, reg); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci return ((cp_read(sd, reg) << 8) | cp_read(sd, reg + 1)) & mask; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV76XX_PAGE_CP], reg, val); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return cp_write(sd, reg, (cp_read(sd, reg) & ~mask) | val); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic inline int vdp_read(struct v4l2_subdev *sd, u8 reg) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return adv76xx_read_check(state, ADV7604_PAGE_VDP, reg); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return regmap_write(state->regmap[ADV7604_PAGE_VDP], reg, val); 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci#define ADV76XX_REG(page, offset) (((page) << 8) | (offset)) 6058c2ecf20Sopenharmony_ci#define ADV76XX_REG_SEQ_TERM 0xffff 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 6088c2ecf20Sopenharmony_cistatic int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 6118c2ecf20Sopenharmony_ci unsigned int page = reg >> 8; 6128c2ecf20Sopenharmony_ci unsigned int val; 6138c2ecf20Sopenharmony_ci int err; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask)) 6168c2ecf20Sopenharmony_ci return -EINVAL; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci reg &= 0xff; 6198c2ecf20Sopenharmony_ci err = regmap_read(state->regmap[page], reg, &val); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return err ? err : val; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci#endif 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 6288c2ecf20Sopenharmony_ci unsigned int page = reg >> 8; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (page >= ADV76XX_PAGE_MAX || !(BIT(page) & state->info->page_mask)) 6318c2ecf20Sopenharmony_ci return -EINVAL; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci reg &= 0xff; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci return regmap_write(state->regmap[page], reg, val); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic void adv76xx_write_reg_seq(struct v4l2_subdev *sd, 6398c2ecf20Sopenharmony_ci const struct adv76xx_reg_seq *reg_seq) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci unsigned int i; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci for (i = 0; reg_seq[i].reg != ADV76XX_REG_SEQ_TERM; i++) 6448c2ecf20Sopenharmony_ci adv76xx_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6488c2ecf20Sopenharmony_ci * Format helpers 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic const struct adv76xx_format_info adv7604_formats[] = { 6528c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, 6538c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, 6548c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, 6558c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, 6568c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, 6578c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, 6588c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV10_2X10, ADV76XX_OP_CH_SEL_RGB, false, false, 6598c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, 6608c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU10_2X10, ADV76XX_OP_CH_SEL_RGB, false, true, 6618c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, 6628c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false, 6638c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, 6648c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true, 6658c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, 6668c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, 6678c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 6688c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, 6698c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 6708c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, 6718c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 6728c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, 6738c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 6748c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, false, 6758c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, 6768c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, true, 6778c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, 6788c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV10_1X20, ADV76XX_OP_CH_SEL_RGB, false, false, 6798c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, 6808c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU10_1X20, ADV76XX_OP_CH_SEL_RGB, false, true, 6818c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, 6828c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false, 6838c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 6848c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true, 6858c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 6868c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false, 6878c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 6888c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true, 6898c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 6908c2ecf20Sopenharmony_ci}; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic const struct adv76xx_format_info adv7611_formats[] = { 6938c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, 6948c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, 6958c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, 6968c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, 6978c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, 6988c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, 6998c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false, 7008c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, 7018c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true, 7028c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT }, 7038c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, 7048c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7058c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, 7068c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7078c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, 7088c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7098c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, 7108c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7118c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false, 7128c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 7138c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true, 7148c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 7158c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false, 7168c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 7178c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true, 7188c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, 7198c2ecf20Sopenharmony_ci}; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic const struct adv76xx_format_info adv7612_formats[] = { 7228c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, 7238c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, 7248c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, 7258c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, 7268c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, 7278c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, 7288c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, 7298c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7308c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, 7318c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7328c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, 7338c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7348c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, 7358c2ecf20Sopenharmony_ci ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, 7368c2ecf20Sopenharmony_ci}; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic const struct adv76xx_format_info * 7398c2ecf20Sopenharmony_ciadv76xx_format_info(struct adv76xx_state *state, u32 code) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci unsigned int i; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci for (i = 0; i < state->info->nformats; ++i) { 7448c2ecf20Sopenharmony_ci if (state->info->formats[i].code == code) 7458c2ecf20Sopenharmony_ci return &state->info->formats[i]; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return NULL; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic inline bool is_analog_input(struct v4l2_subdev *sd) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return state->selected_input == ADV7604_PAD_VGA_RGB || 7588c2ecf20Sopenharmony_ci state->selected_input == ADV7604_PAD_VGA_COMP; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic inline bool is_digital_input(struct v4l2_subdev *sd) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return state->selected_input == ADV76XX_PAD_HDMI_PORT_A || 7668c2ecf20Sopenharmony_ci state->selected_input == ADV7604_PAD_HDMI_PORT_B || 7678c2ecf20Sopenharmony_ci state->selected_input == ADV7604_PAD_HDMI_PORT_C || 7688c2ecf20Sopenharmony_ci state->selected_input == ADV7604_PAD_HDMI_PORT_D; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap adv7604_timings_cap_analog = { 7728c2ecf20Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 7738c2ecf20Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 7748c2ecf20Sopenharmony_ci .reserved = { 0 }, 7758c2ecf20Sopenharmony_ci V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 170000000, 7768c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 7778c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, 7788c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | 7798c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_CUSTOM) 7808c2ecf20Sopenharmony_ci}; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = { 7838c2ecf20Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 7848c2ecf20Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 7858c2ecf20Sopenharmony_ci .reserved = { 0 }, 7868c2ecf20Sopenharmony_ci V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 225000000, 7878c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 7888c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, 7898c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | 7908c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_CUSTOM) 7918c2ecf20Sopenharmony_ci}; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci/* 7948c2ecf20Sopenharmony_ci * Return the DV timings capabilities for the requested sink pad. As a special 7958c2ecf20Sopenharmony_ci * case, pad value -1 returns the capabilities for the currently selected input. 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap * 7988c2ecf20Sopenharmony_ciadv76xx_get_dv_timings_cap(struct v4l2_subdev *sd, int pad) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci if (pad == -1) { 8018c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci pad = state->selected_input; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci switch (pad) { 8078c2ecf20Sopenharmony_ci case ADV76XX_PAD_HDMI_PORT_A: 8088c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_B: 8098c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_C: 8108c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_D: 8118c2ecf20Sopenharmony_ci return &adv76xx_timings_cap_digital; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci case ADV7604_PAD_VGA_RGB: 8148c2ecf20Sopenharmony_ci case ADV7604_PAD_VGA_COMP: 8158c2ecf20Sopenharmony_ci default: 8168c2ecf20Sopenharmony_ci return &adv7604_timings_cap_analog; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 8248c2ecf20Sopenharmony_cistatic void adv76xx_inv_register(struct v4l2_subdev *sd) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci v4l2_info(sd, "0x000-0x0ff: IO Map\n"); 8278c2ecf20Sopenharmony_ci v4l2_info(sd, "0x100-0x1ff: AVLink Map\n"); 8288c2ecf20Sopenharmony_ci v4l2_info(sd, "0x200-0x2ff: CEC Map\n"); 8298c2ecf20Sopenharmony_ci v4l2_info(sd, "0x300-0x3ff: InfoFrame Map\n"); 8308c2ecf20Sopenharmony_ci v4l2_info(sd, "0x400-0x4ff: ESDP Map\n"); 8318c2ecf20Sopenharmony_ci v4l2_info(sd, "0x500-0x5ff: DPP Map\n"); 8328c2ecf20Sopenharmony_ci v4l2_info(sd, "0x600-0x6ff: AFE Map\n"); 8338c2ecf20Sopenharmony_ci v4l2_info(sd, "0x700-0x7ff: Repeater Map\n"); 8348c2ecf20Sopenharmony_ci v4l2_info(sd, "0x800-0x8ff: EDID Map\n"); 8358c2ecf20Sopenharmony_ci v4l2_info(sd, "0x900-0x9ff: HDMI Map\n"); 8368c2ecf20Sopenharmony_ci v4l2_info(sd, "0xa00-0xaff: Test Map\n"); 8378c2ecf20Sopenharmony_ci v4l2_info(sd, "0xb00-0xbff: CP Map\n"); 8388c2ecf20Sopenharmony_ci v4l2_info(sd, "0xc00-0xcff: VDP Map\n"); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic int adv76xx_g_register(struct v4l2_subdev *sd, 8428c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci int ret; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ret = adv76xx_read_reg(sd, reg->reg); 8478c2ecf20Sopenharmony_ci if (ret < 0) { 8488c2ecf20Sopenharmony_ci v4l2_info(sd, "Register %03llx not supported\n", reg->reg); 8498c2ecf20Sopenharmony_ci adv76xx_inv_register(sd); 8508c2ecf20Sopenharmony_ci return ret; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci reg->size = 1; 8548c2ecf20Sopenharmony_ci reg->val = ret; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int adv76xx_s_register(struct v4l2_subdev *sd, 8608c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci int ret; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci ret = adv76xx_write_reg(sd, reg->reg, reg->val); 8658c2ecf20Sopenharmony_ci if (ret < 0) { 8668c2ecf20Sopenharmony_ci v4l2_info(sd, "Register %03llx not supported\n", reg->reg); 8678c2ecf20Sopenharmony_ci adv76xx_inv_register(sd); 8688c2ecf20Sopenharmony_ci return ret; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci#endif 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic unsigned int adv7604_read_cable_det(struct v4l2_subdev *sd) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci u8 value = io_read(sd, 0x6f); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return ((value & 0x10) >> 4) 8808c2ecf20Sopenharmony_ci | ((value & 0x08) >> 2) 8818c2ecf20Sopenharmony_ci | ((value & 0x04) << 0) 8828c2ecf20Sopenharmony_ci | ((value & 0x02) << 2); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci u8 value = io_read(sd, 0x6f); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci return value & 1; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic unsigned int adv7612_read_cable_det(struct v4l2_subdev *sd) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci /* Reads CABLE_DET_A_RAW. For input B support, need to 8958c2ecf20Sopenharmony_ci * account for bit 7 [MSB] of 0x6a (ie. CABLE_DET_B_RAW) 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci u8 value = io_read(sd, 0x6f); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return value & 1; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 9058c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 9068c2ecf20Sopenharmony_ci u16 cable_det = info->read_cable_det(sd); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, 9128c2ecf20Sopenharmony_ci u8 prim_mode, 9138c2ecf20Sopenharmony_ci const struct adv76xx_video_standards *predef_vid_timings, 9148c2ecf20Sopenharmony_ci const struct v4l2_dv_timings *timings) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci int i; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { 9198c2ecf20Sopenharmony_ci if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, 9208c2ecf20Sopenharmony_ci is_digital_input(sd) ? 250000 : 1000000, false)) 9218c2ecf20Sopenharmony_ci continue; 9228c2ecf20Sopenharmony_ci io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ 9238c2ecf20Sopenharmony_ci io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + 9248c2ecf20Sopenharmony_ci prim_mode); /* v_freq and prim mode */ 9258c2ecf20Sopenharmony_ci return 0; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return -1; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic int configure_predefined_video_timings(struct v4l2_subdev *sd, 9328c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 9358c2ecf20Sopenharmony_ci int err; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s", __func__); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (adv76xx_has_afe(state)) { 9408c2ecf20Sopenharmony_ci /* reset to default values */ 9418c2ecf20Sopenharmony_ci io_write(sd, 0x16, 0x43); 9428c2ecf20Sopenharmony_ci io_write(sd, 0x17, 0x5a); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci /* disable embedded syncs for auto graphics mode */ 9458c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0x81, 0x10, 0x00); 9468c2ecf20Sopenharmony_ci cp_write(sd, 0x8f, 0x00); 9478c2ecf20Sopenharmony_ci cp_write(sd, 0x90, 0x00); 9488c2ecf20Sopenharmony_ci cp_write(sd, 0xa2, 0x00); 9498c2ecf20Sopenharmony_ci cp_write(sd, 0xa3, 0x00); 9508c2ecf20Sopenharmony_ci cp_write(sd, 0xa4, 0x00); 9518c2ecf20Sopenharmony_ci cp_write(sd, 0xa5, 0x00); 9528c2ecf20Sopenharmony_ci cp_write(sd, 0xa6, 0x00); 9538c2ecf20Sopenharmony_ci cp_write(sd, 0xa7, 0x00); 9548c2ecf20Sopenharmony_ci cp_write(sd, 0xab, 0x00); 9558c2ecf20Sopenharmony_ci cp_write(sd, 0xac, 0x00); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (is_analog_input(sd)) { 9588c2ecf20Sopenharmony_ci err = find_and_set_predefined_video_timings(sd, 9598c2ecf20Sopenharmony_ci 0x01, adv7604_prim_mode_comp, timings); 9608c2ecf20Sopenharmony_ci if (err) 9618c2ecf20Sopenharmony_ci err = find_and_set_predefined_video_timings(sd, 9628c2ecf20Sopenharmony_ci 0x02, adv7604_prim_mode_gr, timings); 9638c2ecf20Sopenharmony_ci } else if (is_digital_input(sd)) { 9648c2ecf20Sopenharmony_ci err = find_and_set_predefined_video_timings(sd, 9658c2ecf20Sopenharmony_ci 0x05, adv76xx_prim_mode_hdmi_comp, timings); 9668c2ecf20Sopenharmony_ci if (err) 9678c2ecf20Sopenharmony_ci err = find_and_set_predefined_video_timings(sd, 9688c2ecf20Sopenharmony_ci 0x06, adv76xx_prim_mode_hdmi_gr, timings); 9698c2ecf20Sopenharmony_ci } else { 9708c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", 9718c2ecf20Sopenharmony_ci __func__, state->selected_input); 9728c2ecf20Sopenharmony_ci err = -1; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return err; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic void configure_custom_video_timings(struct v4l2_subdev *sd, 9808c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 9838c2ecf20Sopenharmony_ci u32 width = htotal(bt); 9848c2ecf20Sopenharmony_ci u32 height = vtotal(bt); 9858c2ecf20Sopenharmony_ci u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; 9868c2ecf20Sopenharmony_ci u16 cp_start_eav = width - bt->hfrontporch; 9878c2ecf20Sopenharmony_ci u16 cp_start_vbi = height - bt->vfrontporch; 9888c2ecf20Sopenharmony_ci u16 cp_end_vbi = bt->vsync + bt->vbackporch; 9898c2ecf20Sopenharmony_ci u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ? 9908c2ecf20Sopenharmony_ci ((width * (ADV76XX_FSC / 100)) / ((u32)bt->pixelclock / 100)) : 0; 9918c2ecf20Sopenharmony_ci const u8 pll[2] = { 9928c2ecf20Sopenharmony_ci 0xc0 | ((width >> 8) & 0x1f), 9938c2ecf20Sopenharmony_ci width & 0xff 9948c2ecf20Sopenharmony_ci }; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s\n", __func__); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (is_analog_input(sd)) { 9998c2ecf20Sopenharmony_ci /* auto graphics */ 10008c2ecf20Sopenharmony_ci io_write(sd, 0x00, 0x07); /* video std */ 10018c2ecf20Sopenharmony_ci io_write(sd, 0x01, 0x02); /* prim mode */ 10028c2ecf20Sopenharmony_ci /* enable embedded syncs for auto graphics mode */ 10038c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0x81, 0x10, 0x10); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ 10068c2ecf20Sopenharmony_ci /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ 10078c2ecf20Sopenharmony_ci /* IO-map reg. 0x16 and 0x17 should be written in sequence */ 10088c2ecf20Sopenharmony_ci if (regmap_raw_write(state->regmap[ADV76XX_PAGE_IO], 10098c2ecf20Sopenharmony_ci 0x16, pll, 2)) 10108c2ecf20Sopenharmony_ci v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* active video - horizontal timing */ 10138c2ecf20Sopenharmony_ci cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff); 10148c2ecf20Sopenharmony_ci cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | 10158c2ecf20Sopenharmony_ci ((cp_start_eav >> 8) & 0x0f)); 10168c2ecf20Sopenharmony_ci cp_write(sd, 0xa4, cp_start_eav & 0xff); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* active video - vertical timing */ 10198c2ecf20Sopenharmony_ci cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff); 10208c2ecf20Sopenharmony_ci cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | 10218c2ecf20Sopenharmony_ci ((cp_end_vbi >> 8) & 0xf)); 10228c2ecf20Sopenharmony_ci cp_write(sd, 0xa7, cp_end_vbi & 0xff); 10238c2ecf20Sopenharmony_ci } else if (is_digital_input(sd)) { 10248c2ecf20Sopenharmony_ci /* set default prim_mode/vid_std for HDMI 10258c2ecf20Sopenharmony_ci according to [REF_03, c. 4.2] */ 10268c2ecf20Sopenharmony_ci io_write(sd, 0x00, 0x02); /* video std */ 10278c2ecf20Sopenharmony_ci io_write(sd, 0x01, 0x06); /* prim mode */ 10288c2ecf20Sopenharmony_ci } else { 10298c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", 10308c2ecf20Sopenharmony_ci __func__, state->selected_input); 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); 10348c2ecf20Sopenharmony_ci cp_write(sd, 0x90, ch1_fr_ll & 0xff); 10358c2ecf20Sopenharmony_ci cp_write(sd, 0xab, (height >> 4) & 0xff); 10368c2ecf20Sopenharmony_ci cp_write(sd, 0xac, (height & 0x0f) << 4); 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 10428c2ecf20Sopenharmony_ci u8 offset_buf[4]; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (auto_offset) { 10458c2ecf20Sopenharmony_ci offset_a = 0x3ff; 10468c2ecf20Sopenharmony_ci offset_b = 0x3ff; 10478c2ecf20Sopenharmony_ci offset_c = 0x3ff; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n", 10518c2ecf20Sopenharmony_ci __func__, auto_offset ? "Auto" : "Manual", 10528c2ecf20Sopenharmony_ci offset_a, offset_b, offset_c); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci offset_buf[0] = (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4); 10558c2ecf20Sopenharmony_ci offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6); 10568c2ecf20Sopenharmony_ci offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8); 10578c2ecf20Sopenharmony_ci offset_buf[3] = offset_c & 0x0ff; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci /* Registers must be written in this order with no i2c access in between */ 10608c2ecf20Sopenharmony_ci if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP], 10618c2ecf20Sopenharmony_ci 0x77, offset_buf, 4)) 10628c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 10688c2ecf20Sopenharmony_ci u8 gain_buf[4]; 10698c2ecf20Sopenharmony_ci u8 gain_man = 1; 10708c2ecf20Sopenharmony_ci u8 agc_mode_man = 1; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (auto_gain) { 10738c2ecf20Sopenharmony_ci gain_man = 0; 10748c2ecf20Sopenharmony_ci agc_mode_man = 0; 10758c2ecf20Sopenharmony_ci gain_a = 0x100; 10768c2ecf20Sopenharmony_ci gain_b = 0x100; 10778c2ecf20Sopenharmony_ci gain_c = 0x100; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n", 10818c2ecf20Sopenharmony_ci __func__, auto_gain ? "Auto" : "Manual", 10828c2ecf20Sopenharmony_ci gain_a, gain_b, gain_c); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4)); 10858c2ecf20Sopenharmony_ci gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6)); 10868c2ecf20Sopenharmony_ci gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8)); 10878c2ecf20Sopenharmony_ci gain_buf[3] = ((gain_c & 0x0ff)); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Registers must be written in this order with no i2c access in between */ 10908c2ecf20Sopenharmony_ci if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP], 10918c2ecf20Sopenharmony_ci 0x73, gain_buf, 4)) 10928c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic void set_rgb_quantization_range(struct v4l2_subdev *sd) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 10988c2ecf20Sopenharmony_ci bool rgb_output = io_read(sd, 0x02) & 0x02; 10998c2ecf20Sopenharmony_ci bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; 11008c2ecf20Sopenharmony_ci u8 y = HDMI_COLORSPACE_RGB; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (hdmi_signal && (io_read(sd, 0x60) & 1)) 11038c2ecf20Sopenharmony_ci y = infoframe_read(sd, 0x01) >> 5; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", 11068c2ecf20Sopenharmony_ci __func__, state->rgb_quantization_range, 11078c2ecf20Sopenharmony_ci rgb_output, hdmi_signal); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0); 11108c2ecf20Sopenharmony_ci adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0); 11118c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci switch (state->rgb_quantization_range) { 11148c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_AUTO: 11158c2ecf20Sopenharmony_ci if (state->selected_input == ADV7604_PAD_VGA_RGB) { 11168c2ecf20Sopenharmony_ci /* Receiving analog RGB signal 11178c2ecf20Sopenharmony_ci * Set RGB full range (0-255) */ 11188c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x10); 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (state->selected_input == ADV7604_PAD_VGA_COMP) { 11238c2ecf20Sopenharmony_ci /* Receiving analog YPbPr signal 11248c2ecf20Sopenharmony_ci * Set automode */ 11258c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0xf0); 11268c2ecf20Sopenharmony_ci break; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (hdmi_signal) { 11308c2ecf20Sopenharmony_ci /* Receiving HDMI signal 11318c2ecf20Sopenharmony_ci * Set automode */ 11328c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0xf0); 11338c2ecf20Sopenharmony_ci break; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci /* Receiving DVI-D signal 11378c2ecf20Sopenharmony_ci * ADV7604 selects RGB limited range regardless of 11388c2ecf20Sopenharmony_ci * input format (CE/IT) in automatic mode */ 11398c2ecf20Sopenharmony_ci if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { 11408c2ecf20Sopenharmony_ci /* RGB limited range (16-235) */ 11418c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x00); 11428c2ecf20Sopenharmony_ci } else { 11438c2ecf20Sopenharmony_ci /* RGB full range (0-255) */ 11448c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x10); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (is_digital_input(sd) && rgb_output) { 11478c2ecf20Sopenharmony_ci adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40); 11488c2ecf20Sopenharmony_ci } else { 11498c2ecf20Sopenharmony_ci adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0); 11508c2ecf20Sopenharmony_ci adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci break; 11548c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_LIMITED: 11558c2ecf20Sopenharmony_ci if (state->selected_input == ADV7604_PAD_VGA_COMP) { 11568c2ecf20Sopenharmony_ci /* YCrCb limited range (16-235) */ 11578c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x20); 11588c2ecf20Sopenharmony_ci break; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (y != HDMI_COLORSPACE_RGB) 11628c2ecf20Sopenharmony_ci break; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* RGB limited range (16-235) */ 11658c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x00); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci break; 11688c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_FULL: 11698c2ecf20Sopenharmony_ci if (state->selected_input == ADV7604_PAD_VGA_COMP) { 11708c2ecf20Sopenharmony_ci /* YCrCb full range (0-255) */ 11718c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x60); 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (y != HDMI_COLORSPACE_RGB) 11768c2ecf20Sopenharmony_ci break; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* RGB full range (0-255) */ 11798c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0xf0, 0x10); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (is_analog_input(sd) || hdmi_signal) 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci /* Adjust gain/offset for DVI-D signals only */ 11858c2ecf20Sopenharmony_ci if (rgb_output) { 11868c2ecf20Sopenharmony_ci adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40); 11878c2ecf20Sopenharmony_ci } else { 11888c2ecf20Sopenharmony_ci adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0); 11898c2ecf20Sopenharmony_ci adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70); 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci break; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int adv76xx_s_ctrl(struct v4l2_ctrl *ctrl) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = 11988c2ecf20Sopenharmony_ci &container_of(ctrl->handler, struct adv76xx_state, hdl)->sd; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci switch (ctrl->id) { 12038c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 12048c2ecf20Sopenharmony_ci cp_write(sd, 0x3c, ctrl->val); 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 12078c2ecf20Sopenharmony_ci cp_write(sd, 0x3a, ctrl->val); 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 12108c2ecf20Sopenharmony_ci cp_write(sd, 0x3b, ctrl->val); 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci case V4L2_CID_HUE: 12138c2ecf20Sopenharmony_ci cp_write(sd, 0x3d, ctrl->val); 12148c2ecf20Sopenharmony_ci return 0; 12158c2ecf20Sopenharmony_ci case V4L2_CID_DV_RX_RGB_RANGE: 12168c2ecf20Sopenharmony_ci state->rgb_quantization_range = ctrl->val; 12178c2ecf20Sopenharmony_ci set_rgb_quantization_range(sd); 12188c2ecf20Sopenharmony_ci return 0; 12198c2ecf20Sopenharmony_ci case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: 12208c2ecf20Sopenharmony_ci if (!adv76xx_has_afe(state)) 12218c2ecf20Sopenharmony_ci return -EINVAL; 12228c2ecf20Sopenharmony_ci /* Set the analog sampling phase. This is needed to find the 12238c2ecf20Sopenharmony_ci best sampling phase for analog video: an application or 12248c2ecf20Sopenharmony_ci driver has to try a number of phases and analyze the picture 12258c2ecf20Sopenharmony_ci quality before settling on the best performing phase. */ 12268c2ecf20Sopenharmony_ci afe_write(sd, 0xc8, ctrl->val); 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL: 12298c2ecf20Sopenharmony_ci /* Use the default blue color for free running mode, 12308c2ecf20Sopenharmony_ci or supply your own. */ 12318c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0xbf, 0x04, ctrl->val << 2); 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci case V4L2_CID_ADV_RX_FREE_RUN_COLOR: 12348c2ecf20Sopenharmony_ci cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16); 12358c2ecf20Sopenharmony_ci cp_write(sd, 0xc1, (ctrl->val & 0x00ff00) >> 8); 12368c2ecf20Sopenharmony_ci cp_write(sd, 0xc2, (u8)(ctrl->val & 0x0000ff)); 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci return -EINVAL; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic int adv76xx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = 12458c2ecf20Sopenharmony_ci &container_of(ctrl->handler, struct adv76xx_state, hdl)->sd; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) { 12488c2ecf20Sopenharmony_ci ctrl->val = V4L2_DV_IT_CONTENT_TYPE_NO_ITC; 12498c2ecf20Sopenharmony_ci if ((io_read(sd, 0x60) & 1) && (infoframe_read(sd, 0x03) & 0x80)) 12508c2ecf20Sopenharmony_ci ctrl->val = (infoframe_read(sd, 0x05) >> 4) & 3; 12518c2ecf20Sopenharmony_ci return 0; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci return -EINVAL; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic inline bool no_power(struct v4l2_subdev *sd) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci /* Entire chip or CP powered off */ 12618c2ecf20Sopenharmony_ci return io_read(sd, 0x0c) & 0x24; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic inline bool no_signal_tmds(struct v4l2_subdev *sd) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input)); 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic inline bool no_lock_tmds(struct v4l2_subdev *sd) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 12748c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask; 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic inline bool is_hdmi(struct v4l2_subdev *sd) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci return hdmi_read(sd, 0x05) & 0x80; 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic inline bool no_lock_sspd(struct v4l2_subdev *sd) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci /* 12898c2ecf20Sopenharmony_ci * Chips without a AFE don't expose registers for the SSPD, so just assume 12908c2ecf20Sopenharmony_ci * that we have a lock. 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_ci if (adv76xx_has_afe(state)) 12938c2ecf20Sopenharmony_ci return false; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* TODO channel 2 */ 12968c2ecf20Sopenharmony_ci return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0); 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic inline bool no_lock_stdi(struct v4l2_subdev *sd) 13008c2ecf20Sopenharmony_ci{ 13018c2ecf20Sopenharmony_ci /* TODO channel 2 */ 13028c2ecf20Sopenharmony_ci return !(cp_read(sd, 0xb1) & 0x80); 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic inline bool no_signal(struct v4l2_subdev *sd) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci bool ret; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci ret = no_power(sd); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci ret |= no_lock_stdi(sd); 13128c2ecf20Sopenharmony_ci ret |= no_lock_sspd(sd); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci if (is_digital_input(sd)) { 13158c2ecf20Sopenharmony_ci ret |= no_lock_tmds(sd); 13168c2ecf20Sopenharmony_ci ret |= no_signal_tmds(sd); 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci return ret; 13208c2ecf20Sopenharmony_ci} 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic inline bool no_lock_cp(struct v4l2_subdev *sd) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci if (!adv76xx_has_afe(state)) 13278c2ecf20Sopenharmony_ci return false; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* CP has detected a non standard number of lines on the incoming 13308c2ecf20Sopenharmony_ci video compared to what it is configured to receive by s_dv_timings */ 13318c2ecf20Sopenharmony_ci return io_read(sd, 0x12) & 0x01; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_cistatic inline bool in_free_run(struct v4l2_subdev *sd) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci return cp_read(sd, 0xff) & 0x10; 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic int adv76xx_g_input_status(struct v4l2_subdev *sd, u32 *status) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci *status = 0; 13428c2ecf20Sopenharmony_ci *status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0; 13438c2ecf20Sopenharmony_ci *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; 13448c2ecf20Sopenharmony_ci if (!in_free_run(sd) && no_lock_cp(sd)) 13458c2ecf20Sopenharmony_ci *status |= is_digital_input(sd) ? 13468c2ecf20Sopenharmony_ci V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci return 0; 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistruct stdi_readback { 13568c2ecf20Sopenharmony_ci u16 bl, lcf, lcvs; 13578c2ecf20Sopenharmony_ci u8 hs_pol, vs_pol; 13588c2ecf20Sopenharmony_ci bool interlaced; 13598c2ecf20Sopenharmony_ci}; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic int stdi2dv_timings(struct v4l2_subdev *sd, 13628c2ecf20Sopenharmony_ci struct stdi_readback *stdi, 13638c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 13668c2ecf20Sopenharmony_ci u32 hfreq = (ADV76XX_FSC * 8) / stdi->bl; 13678c2ecf20Sopenharmony_ci u32 pix_clk; 13688c2ecf20Sopenharmony_ci int i; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { 13718c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], 13748c2ecf20Sopenharmony_ci adv76xx_get_dv_timings_cap(sd, -1), 13758c2ecf20Sopenharmony_ci adv76xx_check_dv_timings, NULL)) 13768c2ecf20Sopenharmony_ci continue; 13778c2ecf20Sopenharmony_ci if (vtotal(bt) != stdi->lcf + 1) 13788c2ecf20Sopenharmony_ci continue; 13798c2ecf20Sopenharmony_ci if (bt->vsync != stdi->lcvs) 13808c2ecf20Sopenharmony_ci continue; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci pix_clk = hfreq * htotal(bt); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci if ((pix_clk < bt->pixelclock + 1000000) && 13858c2ecf20Sopenharmony_ci (pix_clk > bt->pixelclock - 1000000)) { 13868c2ecf20Sopenharmony_ci *timings = v4l2_dv_timings_presets[i]; 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0, 13928c2ecf20Sopenharmony_ci (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | 13938c2ecf20Sopenharmony_ci (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), 13948c2ecf20Sopenharmony_ci false, timings)) 13958c2ecf20Sopenharmony_ci return 0; 13968c2ecf20Sopenharmony_ci if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, 13978c2ecf20Sopenharmony_ci (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | 13988c2ecf20Sopenharmony_ci (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), 13998c2ecf20Sopenharmony_ci false, state->aspect_ratio, timings)) 14008c2ecf20Sopenharmony_ci return 0; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, 14038c2ecf20Sopenharmony_ci "%s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync\n", 14048c2ecf20Sopenharmony_ci __func__, stdi->lcvs, stdi->lcf, stdi->bl, 14058c2ecf20Sopenharmony_ci stdi->hs_pol, stdi->vs_pol); 14068c2ecf20Sopenharmony_ci return -1; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_cistatic int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 14138c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 14148c2ecf20Sopenharmony_ci u8 polarity; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (no_lock_stdi(sd) || no_lock_sspd(sd)) { 14178c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__); 14188c2ecf20Sopenharmony_ci return -1; 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci /* read STDI */ 14228c2ecf20Sopenharmony_ci stdi->bl = cp_read16(sd, 0xb1, 0x3fff); 14238c2ecf20Sopenharmony_ci stdi->lcf = cp_read16(sd, info->lcf_reg, 0x7ff); 14248c2ecf20Sopenharmony_ci stdi->lcvs = cp_read(sd, 0xb3) >> 3; 14258c2ecf20Sopenharmony_ci stdi->interlaced = io_read(sd, 0x12) & 0x10; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (adv76xx_has_afe(state)) { 14288c2ecf20Sopenharmony_ci /* read SSPD */ 14298c2ecf20Sopenharmony_ci polarity = cp_read(sd, 0xb5); 14308c2ecf20Sopenharmony_ci if ((polarity & 0x03) == 0x01) { 14318c2ecf20Sopenharmony_ci stdi->hs_pol = polarity & 0x10 14328c2ecf20Sopenharmony_ci ? (polarity & 0x08 ? '+' : '-') : 'x'; 14338c2ecf20Sopenharmony_ci stdi->vs_pol = polarity & 0x40 14348c2ecf20Sopenharmony_ci ? (polarity & 0x20 ? '+' : '-') : 'x'; 14358c2ecf20Sopenharmony_ci } else { 14368c2ecf20Sopenharmony_ci stdi->hs_pol = 'x'; 14378c2ecf20Sopenharmony_ci stdi->vs_pol = 'x'; 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci } else { 14408c2ecf20Sopenharmony_ci polarity = hdmi_read(sd, 0x05); 14418c2ecf20Sopenharmony_ci stdi->hs_pol = polarity & 0x20 ? '+' : '-'; 14428c2ecf20Sopenharmony_ci stdi->vs_pol = polarity & 0x10 ? '+' : '-'; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (no_lock_stdi(sd) || no_lock_sspd(sd)) { 14468c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, 14478c2ecf20Sopenharmony_ci "%s: signal lost during readout of STDI/SSPD\n", __func__); 14488c2ecf20Sopenharmony_ci return -1; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci if (stdi->lcf < 239 || stdi->bl < 8 || stdi->bl == 0x3fff) { 14528c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: invalid signal\n", __func__); 14538c2ecf20Sopenharmony_ci memset(stdi, 0, sizeof(struct stdi_readback)); 14548c2ecf20Sopenharmony_ci return -1; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, 14588c2ecf20Sopenharmony_ci "%s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s\n", 14598c2ecf20Sopenharmony_ci __func__, stdi->lcf, stdi->bl, stdi->lcvs, 14608c2ecf20Sopenharmony_ci stdi->hs_pol, stdi->vs_pol, 14618c2ecf20Sopenharmony_ci stdi->interlaced ? "interlaced" : "progressive"); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci return 0; 14648c2ecf20Sopenharmony_ci} 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_cistatic int adv76xx_enum_dv_timings(struct v4l2_subdev *sd, 14678c2ecf20Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (timings->pad >= state->source_pad) 14728c2ecf20Sopenharmony_ci return -EINVAL; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci return v4l2_enum_dv_timings_cap(timings, 14758c2ecf20Sopenharmony_ci adv76xx_get_dv_timings_cap(sd, timings->pad), 14768c2ecf20Sopenharmony_ci adv76xx_check_dv_timings, NULL); 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_cistatic int adv76xx_dv_timings_cap(struct v4l2_subdev *sd, 14808c2ecf20Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 14838c2ecf20Sopenharmony_ci unsigned int pad = cap->pad; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci if (cap->pad >= state->source_pad) 14868c2ecf20Sopenharmony_ci return -EINVAL; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci *cap = *adv76xx_get_dv_timings_cap(sd, pad); 14898c2ecf20Sopenharmony_ci cap->pad = pad; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci return 0; 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings 14958c2ecf20Sopenharmony_ci if the format is listed in adv76xx_timings[] */ 14968c2ecf20Sopenharmony_cistatic void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, 14978c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd, -1), 15008c2ecf20Sopenharmony_ci is_digital_input(sd) ? 250000 : 1000000, 15018c2ecf20Sopenharmony_ci adv76xx_check_dv_timings, NULL); 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci int a, b; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci a = hdmi_read(sd, 0x06); 15098c2ecf20Sopenharmony_ci b = hdmi_read(sd, 0x3b); 15108c2ecf20Sopenharmony_ci if (a < 0 || b < 0) 15118c2ecf20Sopenharmony_ci return 0; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci return a * 1000000 + ((b & 0x30) >> 4) * 250000; 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci int a, b; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci a = hdmi_read(sd, 0x51); 15218c2ecf20Sopenharmony_ci b = hdmi_read(sd, 0x52); 15228c2ecf20Sopenharmony_ci if (a < 0 || b < 0) 15238c2ecf20Sopenharmony_ci return 0; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_cistatic unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 15318c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 15328c2ecf20Sopenharmony_ci unsigned int freq, bits_per_channel, pixelrepetition; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci freq = info->read_hdmi_pixelclock(sd); 15358c2ecf20Sopenharmony_ci if (is_hdmi(sd)) { 15368c2ecf20Sopenharmony_ci /* adjust for deep color mode and pixel repetition */ 15378c2ecf20Sopenharmony_ci bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; 15388c2ecf20Sopenharmony_ci pixelrepetition = (hdmi_read(sd, 0x05) & 0x0f) + 1; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci freq = freq * 8 / bits_per_channel / pixelrepetition; 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci return freq; 15448c2ecf20Sopenharmony_ci} 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_cistatic int adv76xx_query_dv_timings(struct v4l2_subdev *sd, 15478c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 15508c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 15518c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 15528c2ecf20Sopenharmony_ci struct stdi_readback stdi; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci if (!timings) 15558c2ecf20Sopenharmony_ci return -EINVAL; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci memset(timings, 0, sizeof(struct v4l2_dv_timings)); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (no_signal(sd)) { 15608c2ecf20Sopenharmony_ci state->restart_stdi_once = true; 15618c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); 15628c2ecf20Sopenharmony_ci return -ENOLINK; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci /* read STDI */ 15668c2ecf20Sopenharmony_ci if (read_stdi(sd, &stdi)) { 15678c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: STDI/SSPD not locked\n", __func__); 15688c2ecf20Sopenharmony_ci return -ENOLINK; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci bt->interlaced = stdi.interlaced ? 15718c2ecf20Sopenharmony_ci V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci if (is_digital_input(sd)) { 15748c2ecf20Sopenharmony_ci bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; 15758c2ecf20Sopenharmony_ci u8 vic = 0; 15768c2ecf20Sopenharmony_ci u32 w, h; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci w = hdmi_read16(sd, 0x07, info->linewidth_mask); 15798c2ecf20Sopenharmony_ci h = hdmi_read16(sd, 0x09, info->field0_height_mask); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (hdmi_signal && (io_read(sd, 0x60) & 1)) 15828c2ecf20Sopenharmony_ci vic = infoframe_read(sd, 0x04); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (vic && v4l2_find_dv_timings_cea861_vic(timings, vic) && 15858c2ecf20Sopenharmony_ci bt->width == w && bt->height == h) 15868c2ecf20Sopenharmony_ci goto found; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci timings->type = V4L2_DV_BT_656_1120; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci bt->width = w; 15918c2ecf20Sopenharmony_ci bt->height = h; 15928c2ecf20Sopenharmony_ci bt->pixelclock = adv76xx_read_hdmi_pixelclock(sd); 15938c2ecf20Sopenharmony_ci bt->hfrontporch = hdmi_read16(sd, 0x20, info->hfrontporch_mask); 15948c2ecf20Sopenharmony_ci bt->hsync = hdmi_read16(sd, 0x22, info->hsync_mask); 15958c2ecf20Sopenharmony_ci bt->hbackporch = hdmi_read16(sd, 0x24, info->hbackporch_mask); 15968c2ecf20Sopenharmony_ci bt->vfrontporch = hdmi_read16(sd, 0x2a, 15978c2ecf20Sopenharmony_ci info->field0_vfrontporch_mask) / 2; 15988c2ecf20Sopenharmony_ci bt->vsync = hdmi_read16(sd, 0x2e, info->field0_vsync_mask) / 2; 15998c2ecf20Sopenharmony_ci bt->vbackporch = hdmi_read16(sd, 0x32, 16008c2ecf20Sopenharmony_ci info->field0_vbackporch_mask) / 2; 16018c2ecf20Sopenharmony_ci bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | 16028c2ecf20Sopenharmony_ci ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); 16038c2ecf20Sopenharmony_ci if (bt->interlaced == V4L2_DV_INTERLACED) { 16048c2ecf20Sopenharmony_ci bt->height += hdmi_read16(sd, 0x0b, 16058c2ecf20Sopenharmony_ci info->field1_height_mask); 16068c2ecf20Sopenharmony_ci bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 16078c2ecf20Sopenharmony_ci info->field1_vfrontporch_mask) / 2; 16088c2ecf20Sopenharmony_ci bt->il_vsync = hdmi_read16(sd, 0x30, 16098c2ecf20Sopenharmony_ci info->field1_vsync_mask) / 2; 16108c2ecf20Sopenharmony_ci bt->il_vbackporch = hdmi_read16(sd, 0x34, 16118c2ecf20Sopenharmony_ci info->field1_vbackporch_mask) / 2; 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci adv76xx_fill_optional_dv_timings_fields(sd, timings); 16148c2ecf20Sopenharmony_ci } else { 16158c2ecf20Sopenharmony_ci /* find format 16168c2ecf20Sopenharmony_ci * Since LCVS values are inaccurate [REF_03, p. 275-276], 16178c2ecf20Sopenharmony_ci * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails. 16188c2ecf20Sopenharmony_ci */ 16198c2ecf20Sopenharmony_ci if (!stdi2dv_timings(sd, &stdi, timings)) 16208c2ecf20Sopenharmony_ci goto found; 16218c2ecf20Sopenharmony_ci stdi.lcvs += 1; 16228c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs); 16238c2ecf20Sopenharmony_ci if (!stdi2dv_timings(sd, &stdi, timings)) 16248c2ecf20Sopenharmony_ci goto found; 16258c2ecf20Sopenharmony_ci stdi.lcvs -= 2; 16268c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs); 16278c2ecf20Sopenharmony_ci if (stdi2dv_timings(sd, &stdi, timings)) { 16288c2ecf20Sopenharmony_ci /* 16298c2ecf20Sopenharmony_ci * The STDI block may measure wrong values, especially 16308c2ecf20Sopenharmony_ci * for lcvs and lcf. If the driver can not find any 16318c2ecf20Sopenharmony_ci * valid timing, the STDI block is restarted to measure 16328c2ecf20Sopenharmony_ci * the video timings again. The function will return an 16338c2ecf20Sopenharmony_ci * error, but the restart of STDI will generate a new 16348c2ecf20Sopenharmony_ci * STDI interrupt and the format detection process will 16358c2ecf20Sopenharmony_ci * restart. 16368c2ecf20Sopenharmony_ci */ 16378c2ecf20Sopenharmony_ci if (state->restart_stdi_once) { 16388c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); 16398c2ecf20Sopenharmony_ci /* TODO restart STDI for Sync Channel 2 */ 16408c2ecf20Sopenharmony_ci /* enter one-shot mode */ 16418c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0x86, 0x06, 0x00); 16428c2ecf20Sopenharmony_ci /* trigger STDI restart */ 16438c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0x86, 0x06, 0x04); 16448c2ecf20Sopenharmony_ci /* reset to continuous mode */ 16458c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0x86, 0x06, 0x02); 16468c2ecf20Sopenharmony_ci state->restart_stdi_once = false; 16478c2ecf20Sopenharmony_ci return -ENOLINK; 16488c2ecf20Sopenharmony_ci } 16498c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); 16508c2ecf20Sopenharmony_ci return -ERANGE; 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci state->restart_stdi_once = true; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_cifound: 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci if (no_signal(sd)) { 16578c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: signal lost during readout\n", __func__); 16588c2ecf20Sopenharmony_ci memset(timings, 0, sizeof(struct v4l2_dv_timings)); 16598c2ecf20Sopenharmony_ci return -ENOLINK; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if ((is_analog_input(sd) && bt->pixelclock > 170000000) || 16638c2ecf20Sopenharmony_ci (is_digital_input(sd) && bt->pixelclock > 225000000)) { 16648c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n", 16658c2ecf20Sopenharmony_ci __func__, (u32)bt->pixelclock); 16668c2ecf20Sopenharmony_ci return -ERANGE; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci if (debug > 1) 16708c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "adv76xx_query_dv_timings: ", 16718c2ecf20Sopenharmony_ci timings, true); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci return 0; 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_cistatic int adv76xx_s_dv_timings(struct v4l2_subdev *sd, 16778c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 16808c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt; 16818c2ecf20Sopenharmony_ci int err; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci if (!timings) 16848c2ecf20Sopenharmony_ci return -EINVAL; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) { 16878c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); 16888c2ecf20Sopenharmony_ci return 0; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci bt = &timings->bt; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd, -1), 16948c2ecf20Sopenharmony_ci adv76xx_check_dv_timings, NULL)) 16958c2ecf20Sopenharmony_ci return -ERANGE; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci adv76xx_fill_optional_dv_timings_fields(sd, timings); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci state->timings = *timings; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci cp_write_clr_set(sd, 0x91, 0x40, bt->interlaced ? 0x40 : 0x00); 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci /* Use prim_mode and vid_std when available */ 17048c2ecf20Sopenharmony_ci err = configure_predefined_video_timings(sd, timings); 17058c2ecf20Sopenharmony_ci if (err) { 17068c2ecf20Sopenharmony_ci /* custom settings when the video format 17078c2ecf20Sopenharmony_ci does not have prim_mode/vid_std */ 17088c2ecf20Sopenharmony_ci configure_custom_video_timings(sd, bt); 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci set_rgb_quantization_range(sd); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (debug > 1) 17148c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "adv76xx_s_dv_timings: ", 17158c2ecf20Sopenharmony_ci timings, true); 17168c2ecf20Sopenharmony_ci return 0; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_cistatic int adv76xx_g_dv_timings(struct v4l2_subdev *sd, 17208c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci *timings = state->timings; 17258c2ecf20Sopenharmony_ci return 0; 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_cistatic void adv7604_set_termination(struct v4l2_subdev *sd, bool enable) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci hdmi_write(sd, 0x01, enable ? 0x00 : 0x78); 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic void adv7611_set_termination(struct v4l2_subdev *sd, bool enable) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci hdmi_write(sd, 0x83, enable ? 0xfe : 0xff); 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_cistatic void enable_input(struct v4l2_subdev *sd) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if (is_analog_input(sd)) { 17438c2ecf20Sopenharmony_ci io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ 17448c2ecf20Sopenharmony_ci } else if (is_digital_input(sd)) { 17458c2ecf20Sopenharmony_ci hdmi_write_clr_set(sd, 0x00, 0x03, state->selected_input); 17468c2ecf20Sopenharmony_ci state->info->set_termination(sd, true); 17478c2ecf20Sopenharmony_ci io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ 17488c2ecf20Sopenharmony_ci hdmi_write_clr_set(sd, 0x1a, 0x10, 0x00); /* Unmute audio */ 17498c2ecf20Sopenharmony_ci } else { 17508c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", 17518c2ecf20Sopenharmony_ci __func__, state->selected_input); 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_cistatic void disable_input(struct v4l2_subdev *sd) 17568c2ecf20Sopenharmony_ci{ 17578c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */ 17608c2ecf20Sopenharmony_ci msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */ 17618c2ecf20Sopenharmony_ci io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ 17628c2ecf20Sopenharmony_ci state->info->set_termination(sd, false); 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_cistatic void select_input(struct v4l2_subdev *sd) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 17688c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci if (is_analog_input(sd)) { 17718c2ecf20Sopenharmony_ci adv76xx_write_reg_seq(sd, info->recommended_settings[0]); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci afe_write(sd, 0x00, 0x08); /* power up ADC */ 17748c2ecf20Sopenharmony_ci afe_write(sd, 0x01, 0x06); /* power up Analog Front End */ 17758c2ecf20Sopenharmony_ci afe_write(sd, 0xc8, 0x00); /* phase control */ 17768c2ecf20Sopenharmony_ci } else if (is_digital_input(sd)) { 17778c2ecf20Sopenharmony_ci hdmi_write(sd, 0x00, state->selected_input & 0x03); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci adv76xx_write_reg_seq(sd, info->recommended_settings[1]); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci if (adv76xx_has_afe(state)) { 17828c2ecf20Sopenharmony_ci afe_write(sd, 0x00, 0xff); /* power down ADC */ 17838c2ecf20Sopenharmony_ci afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ 17848c2ecf20Sopenharmony_ci afe_write(sd, 0xc8, 0x40); /* phase control */ 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */ 17888c2ecf20Sopenharmony_ci cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ 17898c2ecf20Sopenharmony_ci cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */ 17908c2ecf20Sopenharmony_ci } else { 17918c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", 17928c2ecf20Sopenharmony_ci __func__, state->selected_input); 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci} 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_cistatic int adv76xx_s_routing(struct v4l2_subdev *sd, 17978c2ecf20Sopenharmony_ci u32 input, u32 output, u32 config) 17988c2ecf20Sopenharmony_ci{ 17998c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d", 18028c2ecf20Sopenharmony_ci __func__, input, state->selected_input); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci if (input == state->selected_input) 18058c2ecf20Sopenharmony_ci return 0; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci if (input > state->info->max_port) 18088c2ecf20Sopenharmony_ci return -EINVAL; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci state->selected_input = input; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci disable_input(sd); 18138c2ecf20Sopenharmony_ci select_input(sd); 18148c2ecf20Sopenharmony_ci enable_input(sd); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci return 0; 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_cistatic int adv76xx_enum_mbus_code(struct v4l2_subdev *sd, 18228c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 18238c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (code->index >= state->info->nformats) 18288c2ecf20Sopenharmony_ci return -EINVAL; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci code->code = state->info->formats[code->index].code; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci return 0; 18338c2ecf20Sopenharmony_ci} 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_cistatic void adv76xx_fill_format(struct adv76xx_state *state, 18368c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci memset(format, 0, sizeof(*format)); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci format->width = state->timings.bt.width; 18418c2ecf20Sopenharmony_ci format->height = state->timings.bt.height; 18428c2ecf20Sopenharmony_ci format->field = V4L2_FIELD_NONE; 18438c2ecf20Sopenharmony_ci format->colorspace = V4L2_COLORSPACE_SRGB; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) 18468c2ecf20Sopenharmony_ci format->colorspace = (state->timings.bt.height <= 576) ? 18478c2ecf20Sopenharmony_ci V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci/* 18518c2ecf20Sopenharmony_ci * Compute the op_ch_sel value required to obtain on the bus the component order 18528c2ecf20Sopenharmony_ci * corresponding to the selected format taking into account bus reordering 18538c2ecf20Sopenharmony_ci * applied by the board at the output of the device. 18548c2ecf20Sopenharmony_ci * 18558c2ecf20Sopenharmony_ci * The following table gives the op_ch_value from the format component order 18568c2ecf20Sopenharmony_ci * (expressed as op_ch_sel value in column) and the bus reordering (expressed as 18578c2ecf20Sopenharmony_ci * adv76xx_bus_order value in row). 18588c2ecf20Sopenharmony_ci * 18598c2ecf20Sopenharmony_ci * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5) 18608c2ecf20Sopenharmony_ci * ----------+------------------------------------------------- 18618c2ecf20Sopenharmony_ci * RGB (NOP) | GBR GRB BGR RGB BRG RBG 18628c2ecf20Sopenharmony_ci * GRB (1-2) | BGR RGB GBR GRB RBG BRG 18638c2ecf20Sopenharmony_ci * RBG (2-3) | GRB GBR BRG RBG BGR RGB 18648c2ecf20Sopenharmony_ci * BGR (1-3) | RBG BRG RGB BGR GRB GBR 18658c2ecf20Sopenharmony_ci * BRG (ROR) | BRG RBG GRB GBR RGB BGR 18668c2ecf20Sopenharmony_ci * GBR (ROL) | RGB BGR RBG BRG GBR GRB 18678c2ecf20Sopenharmony_ci */ 18688c2ecf20Sopenharmony_cistatic unsigned int adv76xx_op_ch_sel(struct adv76xx_state *state) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci#define _SEL(a,b,c,d,e,f) { \ 18718c2ecf20Sopenharmony_ci ADV76XX_OP_CH_SEL_##a, ADV76XX_OP_CH_SEL_##b, ADV76XX_OP_CH_SEL_##c, \ 18728c2ecf20Sopenharmony_ci ADV76XX_OP_CH_SEL_##d, ADV76XX_OP_CH_SEL_##e, ADV76XX_OP_CH_SEL_##f } 18738c2ecf20Sopenharmony_ci#define _BUS(x) [ADV7604_BUS_ORDER_##x] 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci static const unsigned int op_ch_sel[6][6] = { 18768c2ecf20Sopenharmony_ci _BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG), 18778c2ecf20Sopenharmony_ci _BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG), 18788c2ecf20Sopenharmony_ci _BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB), 18798c2ecf20Sopenharmony_ci _BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR), 18808c2ecf20Sopenharmony_ci _BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR), 18818c2ecf20Sopenharmony_ci _BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB), 18828c2ecf20Sopenharmony_ci }; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5]; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic void adv76xx_setup_format(struct adv76xx_state *state) 18888c2ecf20Sopenharmony_ci{ 18898c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0x02, 18928c2ecf20Sopenharmony_ci state->format->rgb_out ? ADV76XX_RGB_OUT : 0); 18938c2ecf20Sopenharmony_ci io_write(sd, 0x03, state->format->op_format_sel | 18948c2ecf20Sopenharmony_ci state->pdata.op_format_mode_sel); 18958c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state)); 18968c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x05, 0x01, 18978c2ecf20Sopenharmony_ci state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0); 18988c2ecf20Sopenharmony_ci set_rgb_quantization_range(sd); 18998c2ecf20Sopenharmony_ci} 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_cistatic int adv76xx_get_format(struct v4l2_subdev *sd, 19028c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 19038c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 19048c2ecf20Sopenharmony_ci{ 19058c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci if (format->pad != state->source_pad) 19088c2ecf20Sopenharmony_ci return -EINVAL; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci adv76xx_fill_format(state, &format->format); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 19138c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 19168c2ecf20Sopenharmony_ci format->format.code = fmt->code; 19178c2ecf20Sopenharmony_ci } else { 19188c2ecf20Sopenharmony_ci format->format.code = state->format->code; 19198c2ecf20Sopenharmony_ci } 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci return 0; 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic int adv76xx_get_selection(struct v4l2_subdev *sd, 19258c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 19268c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 19318c2ecf20Sopenharmony_ci return -EINVAL; 19328c2ecf20Sopenharmony_ci /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */ 19338c2ecf20Sopenharmony_ci if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS) 19348c2ecf20Sopenharmony_ci return -EINVAL; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci sel->r.left = 0; 19378c2ecf20Sopenharmony_ci sel->r.top = 0; 19388c2ecf20Sopenharmony_ci sel->r.width = state->timings.bt.width; 19398c2ecf20Sopenharmony_ci sel->r.height = state->timings.bt.height; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci return 0; 19428c2ecf20Sopenharmony_ci} 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic int adv76xx_set_format(struct v4l2_subdev *sd, 19458c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 19468c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 19498c2ecf20Sopenharmony_ci const struct adv76xx_format_info *info; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci if (format->pad != state->source_pad) 19528c2ecf20Sopenharmony_ci return -EINVAL; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci info = adv76xx_format_info(state, format->format.code); 19558c2ecf20Sopenharmony_ci if (!info) 19568c2ecf20Sopenharmony_ci info = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci adv76xx_fill_format(state, &format->format); 19598c2ecf20Sopenharmony_ci format->format.code = info->code; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 19628c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 19658c2ecf20Sopenharmony_ci fmt->code = format->format.code; 19668c2ecf20Sopenharmony_ci } else { 19678c2ecf20Sopenharmony_ci state->format = info; 19688c2ecf20Sopenharmony_ci adv76xx_setup_format(state); 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci return 0; 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) 19758c2ecf20Sopenharmony_cistatic void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if ((cec_read(sd, 0x11) & 0x01) == 0) { 19808c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); 19818c2ecf20Sopenharmony_ci return; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci if (tx_raw_status & 0x02) { 19858c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", 19868c2ecf20Sopenharmony_ci __func__); 19878c2ecf20Sopenharmony_ci cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, 19888c2ecf20Sopenharmony_ci 1, 0, 0, 0); 19898c2ecf20Sopenharmony_ci return; 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci if (tx_raw_status & 0x04) { 19928c2ecf20Sopenharmony_ci u8 status; 19938c2ecf20Sopenharmony_ci u8 nack_cnt; 19948c2ecf20Sopenharmony_ci u8 low_drive_cnt; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); 19978c2ecf20Sopenharmony_ci /* 19988c2ecf20Sopenharmony_ci * We set this status bit since this hardware performs 19998c2ecf20Sopenharmony_ci * retransmissions. 20008c2ecf20Sopenharmony_ci */ 20018c2ecf20Sopenharmony_ci status = CEC_TX_STATUS_MAX_RETRIES; 20028c2ecf20Sopenharmony_ci nack_cnt = cec_read(sd, 0x14) & 0xf; 20038c2ecf20Sopenharmony_ci if (nack_cnt) 20048c2ecf20Sopenharmony_ci status |= CEC_TX_STATUS_NACK; 20058c2ecf20Sopenharmony_ci low_drive_cnt = cec_read(sd, 0x14) >> 4; 20068c2ecf20Sopenharmony_ci if (low_drive_cnt) 20078c2ecf20Sopenharmony_ci status |= CEC_TX_STATUS_LOW_DRIVE; 20088c2ecf20Sopenharmony_ci cec_transmit_done(state->cec_adap, status, 20098c2ecf20Sopenharmony_ci 0, nack_cnt, low_drive_cnt, 0); 20108c2ecf20Sopenharmony_ci return; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci if (tx_raw_status & 0x01) { 20138c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); 20148c2ecf20Sopenharmony_ci cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); 20158c2ecf20Sopenharmony_ci return; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistatic void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) 20208c2ecf20Sopenharmony_ci{ 20218c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 20228c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 20238c2ecf20Sopenharmony_ci u8 cec_irq; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci /* cec controller */ 20268c2ecf20Sopenharmony_ci cec_irq = io_read(sd, info->cec_irq_status) & 0x0f; 20278c2ecf20Sopenharmony_ci if (!cec_irq) 20288c2ecf20Sopenharmony_ci return; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq); 20318c2ecf20Sopenharmony_ci adv76xx_cec_tx_raw_status(sd, cec_irq); 20328c2ecf20Sopenharmony_ci if (cec_irq & 0x08) { 20338c2ecf20Sopenharmony_ci struct cec_msg msg; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci msg.len = cec_read(sd, 0x25) & 0x1f; 20368c2ecf20Sopenharmony_ci if (msg.len > 16) 20378c2ecf20Sopenharmony_ci msg.len = 16; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci if (msg.len) { 20408c2ecf20Sopenharmony_ci u8 i; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci for (i = 0; i < msg.len; i++) 20438c2ecf20Sopenharmony_ci msg.msg[i] = cec_read(sd, i + 0x15); 20448c2ecf20Sopenharmony_ci cec_write(sd, info->cec_rx_enable, 20458c2ecf20Sopenharmony_ci info->cec_rx_enable_mask); /* re-enable rx */ 20468c2ecf20Sopenharmony_ci cec_received_msg(state->cec_adap, &msg); 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci if (info->cec_irq_swap) { 20518c2ecf20Sopenharmony_ci /* 20528c2ecf20Sopenharmony_ci * Note: the bit order is swapped between 0x4d and 0x4e 20538c2ecf20Sopenharmony_ci * on adv7604 20548c2ecf20Sopenharmony_ci */ 20558c2ecf20Sopenharmony_ci cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | 20568c2ecf20Sopenharmony_ci ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci io_write(sd, info->cec_irq_status + 1, cec_irq); 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (handled) 20618c2ecf20Sopenharmony_ci *handled = true; 20628c2ecf20Sopenharmony_ci} 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_cistatic int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) 20658c2ecf20Sopenharmony_ci{ 20668c2ecf20Sopenharmony_ci struct adv76xx_state *state = cec_get_drvdata(adap); 20678c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 20688c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci if (!state->cec_enabled_adap && enable) { 20718c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */ 20728c2ecf20Sopenharmony_ci cec_write(sd, 0x2c, 0x01); /* cec soft reset */ 20738c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */ 20748c2ecf20Sopenharmony_ci /* enabled irqs: */ 20758c2ecf20Sopenharmony_ci /* tx: ready */ 20768c2ecf20Sopenharmony_ci /* tx: arbitration lost */ 20778c2ecf20Sopenharmony_ci /* tx: retry timeout */ 20788c2ecf20Sopenharmony_ci /* rx: ready */ 20798c2ecf20Sopenharmony_ci io_write_clr_set(sd, info->cec_irq_status + 3, 0x0f, 0x0f); 20808c2ecf20Sopenharmony_ci cec_write(sd, info->cec_rx_enable, info->cec_rx_enable_mask); 20818c2ecf20Sopenharmony_ci } else if (state->cec_enabled_adap && !enable) { 20828c2ecf20Sopenharmony_ci /* disable cec interrupts */ 20838c2ecf20Sopenharmony_ci io_write_clr_set(sd, info->cec_irq_status + 3, 0x0f, 0x00); 20848c2ecf20Sopenharmony_ci /* disable address mask 1-3 */ 20858c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x27, 0x70, 0x00); 20868c2ecf20Sopenharmony_ci /* power down cec section */ 20878c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x2a, 0x01, 0x00); 20888c2ecf20Sopenharmony_ci state->cec_valid_addrs = 0; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci state->cec_enabled_adap = enable; 20918c2ecf20Sopenharmony_ci adv76xx_s_detect_tx_5v_ctrl(sd); 20928c2ecf20Sopenharmony_ci return 0; 20938c2ecf20Sopenharmony_ci} 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_cistatic int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) 20968c2ecf20Sopenharmony_ci{ 20978c2ecf20Sopenharmony_ci struct adv76xx_state *state = cec_get_drvdata(adap); 20988c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 20998c2ecf20Sopenharmony_ci unsigned int i, free_idx = ADV76XX_MAX_ADDRS; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci if (!state->cec_enabled_adap) 21028c2ecf20Sopenharmony_ci return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (addr == CEC_LOG_ADDR_INVALID) { 21058c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x27, 0x70, 0); 21068c2ecf20Sopenharmony_ci state->cec_valid_addrs = 0; 21078c2ecf20Sopenharmony_ci return 0; 21088c2ecf20Sopenharmony_ci } 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci for (i = 0; i < ADV76XX_MAX_ADDRS; i++) { 21118c2ecf20Sopenharmony_ci bool is_valid = state->cec_valid_addrs & (1 << i); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci if (free_idx == ADV76XX_MAX_ADDRS && !is_valid) 21148c2ecf20Sopenharmony_ci free_idx = i; 21158c2ecf20Sopenharmony_ci if (is_valid && state->cec_addr[i] == addr) 21168c2ecf20Sopenharmony_ci return 0; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci if (i == ADV76XX_MAX_ADDRS) { 21198c2ecf20Sopenharmony_ci i = free_idx; 21208c2ecf20Sopenharmony_ci if (i == ADV76XX_MAX_ADDRS) 21218c2ecf20Sopenharmony_ci return -ENXIO; 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci state->cec_addr[i] = addr; 21248c2ecf20Sopenharmony_ci state->cec_valid_addrs |= 1 << i; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci switch (i) { 21278c2ecf20Sopenharmony_ci case 0: 21288c2ecf20Sopenharmony_ci /* enable address mask 0 */ 21298c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x27, 0x10, 0x10); 21308c2ecf20Sopenharmony_ci /* set address for mask 0 */ 21318c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x28, 0x0f, addr); 21328c2ecf20Sopenharmony_ci break; 21338c2ecf20Sopenharmony_ci case 1: 21348c2ecf20Sopenharmony_ci /* enable address mask 1 */ 21358c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x27, 0x20, 0x20); 21368c2ecf20Sopenharmony_ci /* set address for mask 1 */ 21378c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x28, 0xf0, addr << 4); 21388c2ecf20Sopenharmony_ci break; 21398c2ecf20Sopenharmony_ci case 2: 21408c2ecf20Sopenharmony_ci /* enable address mask 2 */ 21418c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x27, 0x40, 0x40); 21428c2ecf20Sopenharmony_ci /* set address for mask 1 */ 21438c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x29, 0x0f, addr); 21448c2ecf20Sopenharmony_ci break; 21458c2ecf20Sopenharmony_ci } 21468c2ecf20Sopenharmony_ci return 0; 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_cistatic int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 21508c2ecf20Sopenharmony_ci u32 signal_free_time, struct cec_msg *msg) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci struct adv76xx_state *state = cec_get_drvdata(adap); 21538c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 21548c2ecf20Sopenharmony_ci u8 len = msg->len; 21558c2ecf20Sopenharmony_ci unsigned int i; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci /* 21588c2ecf20Sopenharmony_ci * The number of retries is the number of attempts - 1, but retry 21598c2ecf20Sopenharmony_ci * at least once. It's not clear if a value of 0 is allowed, so 21608c2ecf20Sopenharmony_ci * let's do at least one retry. 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_ci cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci if (len > 16) { 21658c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); 21668c2ecf20Sopenharmony_ci return -EINVAL; 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci /* write data */ 21708c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 21718c2ecf20Sopenharmony_ci cec_write(sd, i, msg->msg[i]); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* set length (data + header) */ 21748c2ecf20Sopenharmony_ci cec_write(sd, 0x10, len); 21758c2ecf20Sopenharmony_ci /* start transmit, enable tx */ 21768c2ecf20Sopenharmony_ci cec_write(sd, 0x11, 0x01); 21778c2ecf20Sopenharmony_ci return 0; 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_cistatic const struct cec_adap_ops adv76xx_cec_adap_ops = { 21818c2ecf20Sopenharmony_ci .adap_enable = adv76xx_cec_adap_enable, 21828c2ecf20Sopenharmony_ci .adap_log_addr = adv76xx_cec_adap_log_addr, 21838c2ecf20Sopenharmony_ci .adap_transmit = adv76xx_cec_adap_transmit, 21848c2ecf20Sopenharmony_ci}; 21858c2ecf20Sopenharmony_ci#endif 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_cistatic int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) 21888c2ecf20Sopenharmony_ci{ 21898c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 21908c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 21918c2ecf20Sopenharmony_ci const u8 irq_reg_0x43 = io_read(sd, 0x43); 21928c2ecf20Sopenharmony_ci const u8 irq_reg_0x6b = io_read(sd, 0x6b); 21938c2ecf20Sopenharmony_ci const u8 irq_reg_0x70 = io_read(sd, 0x70); 21948c2ecf20Sopenharmony_ci u8 fmt_change_digital; 21958c2ecf20Sopenharmony_ci u8 fmt_change; 21968c2ecf20Sopenharmony_ci u8 tx_5v; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci if (irq_reg_0x43) 21998c2ecf20Sopenharmony_ci io_write(sd, 0x44, irq_reg_0x43); 22008c2ecf20Sopenharmony_ci if (irq_reg_0x70) 22018c2ecf20Sopenharmony_ci io_write(sd, 0x71, irq_reg_0x70); 22028c2ecf20Sopenharmony_ci if (irq_reg_0x6b) 22038c2ecf20Sopenharmony_ci io_write(sd, 0x6c, irq_reg_0x6b); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: ", __func__); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci /* format change */ 22088c2ecf20Sopenharmony_ci fmt_change = irq_reg_0x43 & 0x98; 22098c2ecf20Sopenharmony_ci fmt_change_digital = is_digital_input(sd) 22108c2ecf20Sopenharmony_ci ? irq_reg_0x6b & info->fmt_change_digital_mask 22118c2ecf20Sopenharmony_ci : 0; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (fmt_change || fmt_change_digital) { 22148c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 22158c2ecf20Sopenharmony_ci "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n", 22168c2ecf20Sopenharmony_ci __func__, fmt_change, fmt_change_digital); 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt); 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci if (handled) 22218c2ecf20Sopenharmony_ci *handled = true; 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci /* HDMI/DVI mode */ 22248c2ecf20Sopenharmony_ci if (irq_reg_0x6b & 0x01) { 22258c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, 22268c2ecf20Sopenharmony_ci (io_read(sd, 0x6a) & 0x01) ? "HDMI" : "DVI"); 22278c2ecf20Sopenharmony_ci set_rgb_quantization_range(sd); 22288c2ecf20Sopenharmony_ci if (handled) 22298c2ecf20Sopenharmony_ci *handled = true; 22308c2ecf20Sopenharmony_ci } 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) 22338c2ecf20Sopenharmony_ci /* cec */ 22348c2ecf20Sopenharmony_ci adv76xx_cec_isr(sd, handled); 22358c2ecf20Sopenharmony_ci#endif 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci /* tx 5v detect */ 22388c2ecf20Sopenharmony_ci tx_5v = irq_reg_0x70 & info->cable_det_mask; 22398c2ecf20Sopenharmony_ci if (tx_5v) { 22408c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); 22418c2ecf20Sopenharmony_ci adv76xx_s_detect_tx_5v_ctrl(sd); 22428c2ecf20Sopenharmony_ci if (handled) 22438c2ecf20Sopenharmony_ci *handled = true; 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci return 0; 22468c2ecf20Sopenharmony_ci} 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_cistatic irqreturn_t adv76xx_irq_handler(int irq, void *dev_id) 22498c2ecf20Sopenharmony_ci{ 22508c2ecf20Sopenharmony_ci struct adv76xx_state *state = dev_id; 22518c2ecf20Sopenharmony_ci bool handled = false; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci adv76xx_isr(&state->sd, 0, &handled); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci return handled ? IRQ_HANDLED : IRQ_NONE; 22568c2ecf20Sopenharmony_ci} 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_cistatic int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) 22598c2ecf20Sopenharmony_ci{ 22608c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 22618c2ecf20Sopenharmony_ci u8 *data = NULL; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci switch (edid->pad) { 22668c2ecf20Sopenharmony_ci case ADV76XX_PAD_HDMI_PORT_A: 22678c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_B: 22688c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_C: 22698c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_D: 22708c2ecf20Sopenharmony_ci if (state->edid.present & (1 << edid->pad)) 22718c2ecf20Sopenharmony_ci data = state->edid.edid; 22728c2ecf20Sopenharmony_ci break; 22738c2ecf20Sopenharmony_ci default: 22748c2ecf20Sopenharmony_ci return -EINVAL; 22758c2ecf20Sopenharmony_ci } 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (edid->start_block == 0 && edid->blocks == 0) { 22788c2ecf20Sopenharmony_ci edid->blocks = data ? state->edid.blocks : 0; 22798c2ecf20Sopenharmony_ci return 0; 22808c2ecf20Sopenharmony_ci } 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci if (!data) 22838c2ecf20Sopenharmony_ci return -ENODATA; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci if (edid->start_block >= state->edid.blocks) 22868c2ecf20Sopenharmony_ci return -EINVAL; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci if (edid->start_block + edid->blocks > state->edid.blocks) 22898c2ecf20Sopenharmony_ci edid->blocks = state->edid.blocks - edid->start_block; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci memcpy(edid->edid, data + edid->start_block * 128, edid->blocks * 128); 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci return 0; 22948c2ecf20Sopenharmony_ci} 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_cistatic int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) 22978c2ecf20Sopenharmony_ci{ 22988c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 22998c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 23008c2ecf20Sopenharmony_ci unsigned int spa_loc; 23018c2ecf20Sopenharmony_ci u16 pa; 23028c2ecf20Sopenharmony_ci int err; 23038c2ecf20Sopenharmony_ci int i; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci if (edid->pad > ADV7604_PAD_HDMI_PORT_D) 23088c2ecf20Sopenharmony_ci return -EINVAL; 23098c2ecf20Sopenharmony_ci if (edid->start_block != 0) 23108c2ecf20Sopenharmony_ci return -EINVAL; 23118c2ecf20Sopenharmony_ci if (edid->blocks == 0) { 23128c2ecf20Sopenharmony_ci /* Disable hotplug and I2C access to EDID RAM from DDC port */ 23138c2ecf20Sopenharmony_ci state->edid.present &= ~(1 << edid->pad); 23148c2ecf20Sopenharmony_ci adv76xx_set_hpd(state, state->edid.present); 23158c2ecf20Sopenharmony_ci rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci /* Fall back to a 16:9 aspect ratio */ 23188c2ecf20Sopenharmony_ci state->aspect_ratio.numerator = 16; 23198c2ecf20Sopenharmony_ci state->aspect_ratio.denominator = 9; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci if (!state->edid.present) { 23228c2ecf20Sopenharmony_ci state->edid.blocks = 0; 23238c2ecf20Sopenharmony_ci cec_phys_addr_invalidate(state->cec_adap); 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n", 23278c2ecf20Sopenharmony_ci __func__, edid->pad, state->edid.present); 23288c2ecf20Sopenharmony_ci return 0; 23298c2ecf20Sopenharmony_ci } 23308c2ecf20Sopenharmony_ci if (edid->blocks > 2) { 23318c2ecf20Sopenharmony_ci edid->blocks = 2; 23328c2ecf20Sopenharmony_ci return -E2BIG; 23338c2ecf20Sopenharmony_ci } 23348c2ecf20Sopenharmony_ci pa = v4l2_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); 23358c2ecf20Sopenharmony_ci err = v4l2_phys_addr_validate(pa, &pa, NULL); 23368c2ecf20Sopenharmony_ci if (err) 23378c2ecf20Sopenharmony_ci return err; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", 23408c2ecf20Sopenharmony_ci __func__, edid->pad, state->edid.present); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci /* Disable hotplug and I2C access to EDID RAM from DDC port */ 23438c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); 23448c2ecf20Sopenharmony_ci adv76xx_set_hpd(state, 0); 23458c2ecf20Sopenharmony_ci rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci /* 23488c2ecf20Sopenharmony_ci * Return an error if no location of the source physical address 23498c2ecf20Sopenharmony_ci * was found. 23508c2ecf20Sopenharmony_ci */ 23518c2ecf20Sopenharmony_ci if (spa_loc == 0) 23528c2ecf20Sopenharmony_ci return -EINVAL; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci switch (edid->pad) { 23558c2ecf20Sopenharmony_ci case ADV76XX_PAD_HDMI_PORT_A: 23568c2ecf20Sopenharmony_ci state->spa_port_a[0] = edid->edid[spa_loc]; 23578c2ecf20Sopenharmony_ci state->spa_port_a[1] = edid->edid[spa_loc + 1]; 23588c2ecf20Sopenharmony_ci break; 23598c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_B: 23608c2ecf20Sopenharmony_ci rep_write(sd, 0x70, edid->edid[spa_loc]); 23618c2ecf20Sopenharmony_ci rep_write(sd, 0x71, edid->edid[spa_loc + 1]); 23628c2ecf20Sopenharmony_ci break; 23638c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_C: 23648c2ecf20Sopenharmony_ci rep_write(sd, 0x72, edid->edid[spa_loc]); 23658c2ecf20Sopenharmony_ci rep_write(sd, 0x73, edid->edid[spa_loc + 1]); 23668c2ecf20Sopenharmony_ci break; 23678c2ecf20Sopenharmony_ci case ADV7604_PAD_HDMI_PORT_D: 23688c2ecf20Sopenharmony_ci rep_write(sd, 0x74, edid->edid[spa_loc]); 23698c2ecf20Sopenharmony_ci rep_write(sd, 0x75, edid->edid[spa_loc + 1]); 23708c2ecf20Sopenharmony_ci break; 23718c2ecf20Sopenharmony_ci default: 23728c2ecf20Sopenharmony_ci return -EINVAL; 23738c2ecf20Sopenharmony_ci } 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci if (info->type == ADV7604) { 23768c2ecf20Sopenharmony_ci rep_write(sd, 0x76, spa_loc & 0xff); 23778c2ecf20Sopenharmony_ci rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2); 23788c2ecf20Sopenharmony_ci } else { 23798c2ecf20Sopenharmony_ci /* ADV7612 Software Manual Rev. A, p. 15 */ 23808c2ecf20Sopenharmony_ci rep_write(sd, 0x70, spa_loc & 0xff); 23818c2ecf20Sopenharmony_ci rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8); 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci edid->edid[spa_loc] = state->spa_port_a[0]; 23858c2ecf20Sopenharmony_ci edid->edid[spa_loc + 1] = state->spa_port_a[1]; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci memcpy(state->edid.edid, edid->edid, 128 * edid->blocks); 23888c2ecf20Sopenharmony_ci state->edid.blocks = edid->blocks; 23898c2ecf20Sopenharmony_ci state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15], 23908c2ecf20Sopenharmony_ci edid->edid[0x16]); 23918c2ecf20Sopenharmony_ci state->edid.present |= 1 << edid->pad; 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid); 23948c2ecf20Sopenharmony_ci if (err < 0) { 23958c2ecf20Sopenharmony_ci v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad); 23968c2ecf20Sopenharmony_ci return err; 23978c2ecf20Sopenharmony_ci } 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci /* adv76xx calculates the checksums and enables I2C access to internal 24008c2ecf20Sopenharmony_ci EDID RAM from DDC port. */ 24018c2ecf20Sopenharmony_ci rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 24048c2ecf20Sopenharmony_ci if (rep_read(sd, info->edid_status_reg) & state->edid.present) 24058c2ecf20Sopenharmony_ci break; 24068c2ecf20Sopenharmony_ci mdelay(1); 24078c2ecf20Sopenharmony_ci } 24088c2ecf20Sopenharmony_ci if (i == 1000) { 24098c2ecf20Sopenharmony_ci v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); 24108c2ecf20Sopenharmony_ci return -EIO; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci cec_s_phys_addr(state->cec_adap, pa, false); 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci /* enable hotplug after 100 ms */ 24158c2ecf20Sopenharmony_ci schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); 24168c2ecf20Sopenharmony_ci return 0; 24178c2ecf20Sopenharmony_ci} 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci/*********** avi info frame CEA-861-E **************/ 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_cistatic const struct adv76xx_cfg_read_infoframe adv76xx_cri[] = { 24228c2ecf20Sopenharmony_ci { "AVI", 0x01, 0xe0, 0x00 }, 24238c2ecf20Sopenharmony_ci { "Audio", 0x02, 0xe3, 0x1c }, 24248c2ecf20Sopenharmony_ci { "SDP", 0x04, 0xe6, 0x2a }, 24258c2ecf20Sopenharmony_ci { "Vendor", 0x10, 0xec, 0x54 } 24268c2ecf20Sopenharmony_ci}; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_cistatic int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, 24298c2ecf20Sopenharmony_ci union hdmi_infoframe *frame) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci uint8_t buffer[32]; 24328c2ecf20Sopenharmony_ci u8 len; 24338c2ecf20Sopenharmony_ci int i; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci if (!(io_read(sd, 0x60) & adv76xx_cri[index].present_mask)) { 24368c2ecf20Sopenharmony_ci v4l2_info(sd, "%s infoframe not received\n", 24378c2ecf20Sopenharmony_ci adv76xx_cri[index].desc); 24388c2ecf20Sopenharmony_ci return -ENOENT; 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 24428c2ecf20Sopenharmony_ci buffer[i] = infoframe_read(sd, 24438c2ecf20Sopenharmony_ci adv76xx_cri[index].head_addr + i); 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci len = buffer[2] + 1; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci if (len + 3 > sizeof(buffer)) { 24488c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, 24498c2ecf20Sopenharmony_ci adv76xx_cri[index].desc, len); 24508c2ecf20Sopenharmony_ci return -ENOENT; 24518c2ecf20Sopenharmony_ci } 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 24548c2ecf20Sopenharmony_ci buffer[i + 3] = infoframe_read(sd, 24558c2ecf20Sopenharmony_ci adv76xx_cri[index].payload_addr + i); 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci if (hdmi_infoframe_unpack(frame, buffer, len + 3) < 0) { 24588c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, 24598c2ecf20Sopenharmony_ci adv76xx_cri[index].desc); 24608c2ecf20Sopenharmony_ci return -ENOENT; 24618c2ecf20Sopenharmony_ci } 24628c2ecf20Sopenharmony_ci return 0; 24638c2ecf20Sopenharmony_ci} 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_cistatic void adv76xx_log_infoframes(struct v4l2_subdev *sd) 24668c2ecf20Sopenharmony_ci{ 24678c2ecf20Sopenharmony_ci int i; 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci if (!is_hdmi(sd)) { 24708c2ecf20Sopenharmony_ci v4l2_info(sd, "receive DVI-D signal, no infoframes\n"); 24718c2ecf20Sopenharmony_ci return; 24728c2ecf20Sopenharmony_ci } 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(adv76xx_cri); i++) { 24758c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 24768c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci if (adv76xx_read_infoframe(sd, i, &frame)) 24798c2ecf20Sopenharmony_ci return; 24808c2ecf20Sopenharmony_ci hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci} 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_cistatic int adv76xx_log_status(struct v4l2_subdev *sd) 24858c2ecf20Sopenharmony_ci{ 24868c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 24878c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 24888c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 24898c2ecf20Sopenharmony_ci struct stdi_readback stdi; 24908c2ecf20Sopenharmony_ci u8 reg_io_0x02 = io_read(sd, 0x02); 24918c2ecf20Sopenharmony_ci u8 edid_enabled; 24928c2ecf20Sopenharmony_ci u8 cable_det; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci static const char * const csc_coeff_sel_rb[16] = { 24958c2ecf20Sopenharmony_ci "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB", 24968c2ecf20Sopenharmony_ci "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709", 24978c2ecf20Sopenharmony_ci "reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709", 24988c2ecf20Sopenharmony_ci "reserved", "reserved", "reserved", "reserved", "manual" 24998c2ecf20Sopenharmony_ci }; 25008c2ecf20Sopenharmony_ci static const char * const input_color_space_txt[16] = { 25018c2ecf20Sopenharmony_ci "RGB limited range (16-235)", "RGB full range (0-255)", 25028c2ecf20Sopenharmony_ci "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", 25038c2ecf20Sopenharmony_ci "xvYCC Bt.601", "xvYCC Bt.709", 25048c2ecf20Sopenharmony_ci "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", 25058c2ecf20Sopenharmony_ci "invalid", "invalid", "invalid", "invalid", "invalid", 25068c2ecf20Sopenharmony_ci "invalid", "invalid", "automatic" 25078c2ecf20Sopenharmony_ci }; 25088c2ecf20Sopenharmony_ci static const char * const hdmi_color_space_txt[16] = { 25098c2ecf20Sopenharmony_ci "RGB limited range (16-235)", "RGB full range (0-255)", 25108c2ecf20Sopenharmony_ci "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", 25118c2ecf20Sopenharmony_ci "xvYCC Bt.601", "xvYCC Bt.709", 25128c2ecf20Sopenharmony_ci "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", 25138c2ecf20Sopenharmony_ci "sYCC", "opYCC 601", "opRGB", "invalid", "invalid", 25148c2ecf20Sopenharmony_ci "invalid", "invalid", "invalid" 25158c2ecf20Sopenharmony_ci }; 25168c2ecf20Sopenharmony_ci static const char * const rgb_quantization_range_txt[] = { 25178c2ecf20Sopenharmony_ci "Automatic", 25188c2ecf20Sopenharmony_ci "RGB limited range (16-235)", 25198c2ecf20Sopenharmony_ci "RGB full range (0-255)", 25208c2ecf20Sopenharmony_ci }; 25218c2ecf20Sopenharmony_ci static const char * const deep_color_mode_txt[4] = { 25228c2ecf20Sopenharmony_ci "8-bits per channel", 25238c2ecf20Sopenharmony_ci "10-bits per channel", 25248c2ecf20Sopenharmony_ci "12-bits per channel", 25258c2ecf20Sopenharmony_ci "16-bits per channel (not supported)" 25268c2ecf20Sopenharmony_ci }; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Chip status-----\n"); 25298c2ecf20Sopenharmony_ci v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); 25308c2ecf20Sopenharmony_ci edid_enabled = rep_read(sd, info->edid_status_reg); 25318c2ecf20Sopenharmony_ci v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n", 25328c2ecf20Sopenharmony_ci ((edid_enabled & 0x01) ? "Yes" : "No"), 25338c2ecf20Sopenharmony_ci ((edid_enabled & 0x02) ? "Yes" : "No"), 25348c2ecf20Sopenharmony_ci ((edid_enabled & 0x04) ? "Yes" : "No"), 25358c2ecf20Sopenharmony_ci ((edid_enabled & 0x08) ? "Yes" : "No")); 25368c2ecf20Sopenharmony_ci v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? 25378c2ecf20Sopenharmony_ci "enabled" : "disabled"); 25388c2ecf20Sopenharmony_ci if (state->cec_enabled_adap) { 25398c2ecf20Sopenharmony_ci int i; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci for (i = 0; i < ADV76XX_MAX_ADDRS; i++) { 25428c2ecf20Sopenharmony_ci bool is_valid = state->cec_valid_addrs & (1 << i); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (is_valid) 25458c2ecf20Sopenharmony_ci v4l2_info(sd, "CEC Logical Address: 0x%x\n", 25468c2ecf20Sopenharmony_ci state->cec_addr[i]); 25478c2ecf20Sopenharmony_ci } 25488c2ecf20Sopenharmony_ci } 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Signal status-----\n"); 25518c2ecf20Sopenharmony_ci cable_det = info->read_cable_det(sd); 25528c2ecf20Sopenharmony_ci v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n", 25538c2ecf20Sopenharmony_ci ((cable_det & 0x01) ? "Yes" : "No"), 25548c2ecf20Sopenharmony_ci ((cable_det & 0x02) ? "Yes" : "No"), 25558c2ecf20Sopenharmony_ci ((cable_det & 0x04) ? "Yes" : "No"), 25568c2ecf20Sopenharmony_ci ((cable_det & 0x08) ? "Yes" : "No")); 25578c2ecf20Sopenharmony_ci v4l2_info(sd, "TMDS signal detected: %s\n", 25588c2ecf20Sopenharmony_ci no_signal_tmds(sd) ? "false" : "true"); 25598c2ecf20Sopenharmony_ci v4l2_info(sd, "TMDS signal locked: %s\n", 25608c2ecf20Sopenharmony_ci no_lock_tmds(sd) ? "false" : "true"); 25618c2ecf20Sopenharmony_ci v4l2_info(sd, "SSPD locked: %s\n", no_lock_sspd(sd) ? "false" : "true"); 25628c2ecf20Sopenharmony_ci v4l2_info(sd, "STDI locked: %s\n", no_lock_stdi(sd) ? "false" : "true"); 25638c2ecf20Sopenharmony_ci v4l2_info(sd, "CP locked: %s\n", no_lock_cp(sd) ? "false" : "true"); 25648c2ecf20Sopenharmony_ci v4l2_info(sd, "CP free run: %s\n", 25658c2ecf20Sopenharmony_ci (in_free_run(sd)) ? "on" : "off"); 25668c2ecf20Sopenharmony_ci v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n", 25678c2ecf20Sopenharmony_ci io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f, 25688c2ecf20Sopenharmony_ci (io_read(sd, 0x01) & 0x70) >> 4); 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Video Timings-----\n"); 25718c2ecf20Sopenharmony_ci if (read_stdi(sd, &stdi)) 25728c2ecf20Sopenharmony_ci v4l2_info(sd, "STDI: not locked\n"); 25738c2ecf20Sopenharmony_ci else 25748c2ecf20Sopenharmony_ci v4l2_info(sd, "STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %s, %chsync, %cvsync\n", 25758c2ecf20Sopenharmony_ci stdi.lcf, stdi.bl, stdi.lcvs, 25768c2ecf20Sopenharmony_ci stdi.interlaced ? "interlaced" : "progressive", 25778c2ecf20Sopenharmony_ci stdi.hs_pol, stdi.vs_pol); 25788c2ecf20Sopenharmony_ci if (adv76xx_query_dv_timings(sd, &timings)) 25798c2ecf20Sopenharmony_ci v4l2_info(sd, "No video detected\n"); 25808c2ecf20Sopenharmony_ci else 25818c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "Detected format: ", 25828c2ecf20Sopenharmony_ci &timings, true); 25838c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "Configured format: ", 25848c2ecf20Sopenharmony_ci &state->timings, true); 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci if (no_signal(sd)) 25878c2ecf20Sopenharmony_ci return 0; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Color space-----\n"); 25908c2ecf20Sopenharmony_ci v4l2_info(sd, "RGB quantization range ctrl: %s\n", 25918c2ecf20Sopenharmony_ci rgb_quantization_range_txt[state->rgb_quantization_range]); 25928c2ecf20Sopenharmony_ci v4l2_info(sd, "Input color space: %s\n", 25938c2ecf20Sopenharmony_ci input_color_space_txt[reg_io_0x02 >> 4]); 25948c2ecf20Sopenharmony_ci v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n", 25958c2ecf20Sopenharmony_ci (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", 25968c2ecf20Sopenharmony_ci (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? 25978c2ecf20Sopenharmony_ci "(16-235)" : "(0-255)", 25988c2ecf20Sopenharmony_ci (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); 25998c2ecf20Sopenharmony_ci v4l2_info(sd, "Color space conversion: %s\n", 26008c2ecf20Sopenharmony_ci csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]); 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci if (!is_digital_input(sd)) 26038c2ecf20Sopenharmony_ci return 0; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); 26068c2ecf20Sopenharmony_ci v4l2_info(sd, "Digital video port selected: %c\n", 26078c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x00) & 0x03) + 'A'); 26088c2ecf20Sopenharmony_ci v4l2_info(sd, "HDCP encrypted content: %s\n", 26098c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); 26108c2ecf20Sopenharmony_ci v4l2_info(sd, "HDCP keys read: %s%s\n", 26118c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no", 26128c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : ""); 26138c2ecf20Sopenharmony_ci if (is_hdmi(sd)) { 26148c2ecf20Sopenharmony_ci bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01; 26158c2ecf20Sopenharmony_ci bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01; 26168c2ecf20Sopenharmony_ci bool audio_mute = io_read(sd, 0x65) & 0x40; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci v4l2_info(sd, "Audio: pll %s, samples %s, %s\n", 26198c2ecf20Sopenharmony_ci audio_pll_locked ? "locked" : "not locked", 26208c2ecf20Sopenharmony_ci audio_sample_packet_detect ? "detected" : "not detected", 26218c2ecf20Sopenharmony_ci audio_mute ? "muted" : "enabled"); 26228c2ecf20Sopenharmony_ci if (audio_pll_locked && audio_sample_packet_detect) { 26238c2ecf20Sopenharmony_ci v4l2_info(sd, "Audio format: %s\n", 26248c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x07) & 0x20) ? "multi-channel" : "stereo"); 26258c2ecf20Sopenharmony_ci } 26268c2ecf20Sopenharmony_ci v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) + 26278c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x5c) << 8) + 26288c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x5d) & 0xf0)); 26298c2ecf20Sopenharmony_ci v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) + 26308c2ecf20Sopenharmony_ci (hdmi_read(sd, 0x5e) << 8) + 26318c2ecf20Sopenharmony_ci hdmi_read(sd, 0x5f)); 26328c2ecf20Sopenharmony_ci v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off"); 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[(hdmi_read(sd, 0x0b) & 0x60) >> 5]); 26358c2ecf20Sopenharmony_ci v4l2_info(sd, "HDMI colorspace: %s\n", hdmi_color_space_txt[hdmi_read(sd, 0x53) & 0xf]); 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci adv76xx_log_infoframes(sd); 26388c2ecf20Sopenharmony_ci } 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci return 0; 26418c2ecf20Sopenharmony_ci} 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_cistatic int adv76xx_subscribe_event(struct v4l2_subdev *sd, 26448c2ecf20Sopenharmony_ci struct v4l2_fh *fh, 26458c2ecf20Sopenharmony_ci struct v4l2_event_subscription *sub) 26468c2ecf20Sopenharmony_ci{ 26478c2ecf20Sopenharmony_ci switch (sub->type) { 26488c2ecf20Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 26498c2ecf20Sopenharmony_ci return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); 26508c2ecf20Sopenharmony_ci case V4L2_EVENT_CTRL: 26518c2ecf20Sopenharmony_ci return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); 26528c2ecf20Sopenharmony_ci default: 26538c2ecf20Sopenharmony_ci return -EINVAL; 26548c2ecf20Sopenharmony_ci } 26558c2ecf20Sopenharmony_ci} 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_cistatic int adv76xx_registered(struct v4l2_subdev *sd) 26588c2ecf20Sopenharmony_ci{ 26598c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 26608c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 26618c2ecf20Sopenharmony_ci int err; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci err = cec_register_adapter(state->cec_adap, &client->dev); 26648c2ecf20Sopenharmony_ci if (err) 26658c2ecf20Sopenharmony_ci cec_delete_adapter(state->cec_adap); 26668c2ecf20Sopenharmony_ci return err; 26678c2ecf20Sopenharmony_ci} 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_cistatic void adv76xx_unregistered(struct v4l2_subdev *sd) 26708c2ecf20Sopenharmony_ci{ 26718c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci cec_unregister_adapter(state->cec_adap); 26748c2ecf20Sopenharmony_ci} 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { 26798c2ecf20Sopenharmony_ci .s_ctrl = adv76xx_s_ctrl, 26808c2ecf20Sopenharmony_ci .g_volatile_ctrl = adv76xx_g_volatile_ctrl, 26818c2ecf20Sopenharmony_ci}; 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops adv76xx_core_ops = { 26848c2ecf20Sopenharmony_ci .log_status = adv76xx_log_status, 26858c2ecf20Sopenharmony_ci .interrupt_service_routine = adv76xx_isr, 26868c2ecf20Sopenharmony_ci .subscribe_event = adv76xx_subscribe_event, 26878c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 26888c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 26898c2ecf20Sopenharmony_ci .g_register = adv76xx_g_register, 26908c2ecf20Sopenharmony_ci .s_register = adv76xx_s_register, 26918c2ecf20Sopenharmony_ci#endif 26928c2ecf20Sopenharmony_ci}; 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops adv76xx_video_ops = { 26958c2ecf20Sopenharmony_ci .s_routing = adv76xx_s_routing, 26968c2ecf20Sopenharmony_ci .g_input_status = adv76xx_g_input_status, 26978c2ecf20Sopenharmony_ci .s_dv_timings = adv76xx_s_dv_timings, 26988c2ecf20Sopenharmony_ci .g_dv_timings = adv76xx_g_dv_timings, 26998c2ecf20Sopenharmony_ci .query_dv_timings = adv76xx_query_dv_timings, 27008c2ecf20Sopenharmony_ci}; 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { 27038c2ecf20Sopenharmony_ci .enum_mbus_code = adv76xx_enum_mbus_code, 27048c2ecf20Sopenharmony_ci .get_selection = adv76xx_get_selection, 27058c2ecf20Sopenharmony_ci .get_fmt = adv76xx_get_format, 27068c2ecf20Sopenharmony_ci .set_fmt = adv76xx_set_format, 27078c2ecf20Sopenharmony_ci .get_edid = adv76xx_get_edid, 27088c2ecf20Sopenharmony_ci .set_edid = adv76xx_set_edid, 27098c2ecf20Sopenharmony_ci .dv_timings_cap = adv76xx_dv_timings_cap, 27108c2ecf20Sopenharmony_ci .enum_dv_timings = adv76xx_enum_dv_timings, 27118c2ecf20Sopenharmony_ci}; 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops adv76xx_ops = { 27148c2ecf20Sopenharmony_ci .core = &adv76xx_core_ops, 27158c2ecf20Sopenharmony_ci .video = &adv76xx_video_ops, 27168c2ecf20Sopenharmony_ci .pad = &adv76xx_pad_ops, 27178c2ecf20Sopenharmony_ci}; 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops adv76xx_int_ops = { 27208c2ecf20Sopenharmony_ci .registered = adv76xx_registered, 27218c2ecf20Sopenharmony_ci .unregistered = adv76xx_unregistered, 27228c2ecf20Sopenharmony_ci}; 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci/* -------------------------- custom ctrls ---------------------------------- */ 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { 27278c2ecf20Sopenharmony_ci .ops = &adv76xx_ctrl_ops, 27288c2ecf20Sopenharmony_ci .id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE, 27298c2ecf20Sopenharmony_ci .name = "Analog Sampling Phase", 27308c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 27318c2ecf20Sopenharmony_ci .min = 0, 27328c2ecf20Sopenharmony_ci .max = 0x1f, 27338c2ecf20Sopenharmony_ci .step = 1, 27348c2ecf20Sopenharmony_ci .def = 0, 27358c2ecf20Sopenharmony_ci}; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color_manual = { 27388c2ecf20Sopenharmony_ci .ops = &adv76xx_ctrl_ops, 27398c2ecf20Sopenharmony_ci .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL, 27408c2ecf20Sopenharmony_ci .name = "Free Running Color, Manual", 27418c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 27428c2ecf20Sopenharmony_ci .min = false, 27438c2ecf20Sopenharmony_ci .max = true, 27448c2ecf20Sopenharmony_ci .step = 1, 27458c2ecf20Sopenharmony_ci .def = false, 27468c2ecf20Sopenharmony_ci}; 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = { 27498c2ecf20Sopenharmony_ci .ops = &adv76xx_ctrl_ops, 27508c2ecf20Sopenharmony_ci .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR, 27518c2ecf20Sopenharmony_ci .name = "Free Running Color", 27528c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 27538c2ecf20Sopenharmony_ci .min = 0x0, 27548c2ecf20Sopenharmony_ci .max = 0xffffff, 27558c2ecf20Sopenharmony_ci .step = 0x1, 27568c2ecf20Sopenharmony_ci .def = 0x0, 27578c2ecf20Sopenharmony_ci}; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_cistruct adv76xx_register_map { 27628c2ecf20Sopenharmony_ci const char *name; 27638c2ecf20Sopenharmony_ci u8 default_addr; 27648c2ecf20Sopenharmony_ci}; 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_cistatic const struct adv76xx_register_map adv76xx_default_addresses[] = { 27678c2ecf20Sopenharmony_ci [ADV76XX_PAGE_IO] = { "main", 0x4c }, 27688c2ecf20Sopenharmony_ci [ADV7604_PAGE_AVLINK] = { "avlink", 0x42 }, 27698c2ecf20Sopenharmony_ci [ADV76XX_PAGE_CEC] = { "cec", 0x40 }, 27708c2ecf20Sopenharmony_ci [ADV76XX_PAGE_INFOFRAME] = { "infoframe", 0x3e }, 27718c2ecf20Sopenharmony_ci [ADV7604_PAGE_ESDP] = { "esdp", 0x38 }, 27728c2ecf20Sopenharmony_ci [ADV7604_PAGE_DPP] = { "dpp", 0x3c }, 27738c2ecf20Sopenharmony_ci [ADV76XX_PAGE_AFE] = { "afe", 0x26 }, 27748c2ecf20Sopenharmony_ci [ADV76XX_PAGE_REP] = { "rep", 0x32 }, 27758c2ecf20Sopenharmony_ci [ADV76XX_PAGE_EDID] = { "edid", 0x36 }, 27768c2ecf20Sopenharmony_ci [ADV76XX_PAGE_HDMI] = { "hdmi", 0x34 }, 27778c2ecf20Sopenharmony_ci [ADV76XX_PAGE_TEST] = { "test", 0x30 }, 27788c2ecf20Sopenharmony_ci [ADV76XX_PAGE_CP] = { "cp", 0x22 }, 27798c2ecf20Sopenharmony_ci [ADV7604_PAGE_VDP] = { "vdp", 0x24 }, 27808c2ecf20Sopenharmony_ci}; 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_cistatic int adv76xx_core_init(struct v4l2_subdev *sd) 27838c2ecf20Sopenharmony_ci{ 27848c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 27858c2ecf20Sopenharmony_ci const struct adv76xx_chip_info *info = state->info; 27868c2ecf20Sopenharmony_ci struct adv76xx_platform_data *pdata = &state->pdata; 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci hdmi_write(sd, 0x48, 27898c2ecf20Sopenharmony_ci (pdata->disable_pwrdnb ? 0x80 : 0) | 27908c2ecf20Sopenharmony_ci (pdata->disable_cable_det_rst ? 0x40 : 0)); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci disable_input(sd); 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci if (pdata->default_input >= 0 && 27958c2ecf20Sopenharmony_ci pdata->default_input < state->source_pad) { 27968c2ecf20Sopenharmony_ci state->selected_input = pdata->default_input; 27978c2ecf20Sopenharmony_ci select_input(sd); 27988c2ecf20Sopenharmony_ci enable_input(sd); 27998c2ecf20Sopenharmony_ci } 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci /* power */ 28028c2ecf20Sopenharmony_ci io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ 28038c2ecf20Sopenharmony_ci io_write(sd, 0x0b, 0x44); /* Power down ESDP block */ 28048c2ecf20Sopenharmony_ci cp_write(sd, 0xcf, 0x01); /* Power down macrovision */ 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci /* video format */ 28078c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3); 28088c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 | 28098c2ecf20Sopenharmony_ci pdata->insert_av_codes << 2 | 28108c2ecf20Sopenharmony_ci pdata->replicate_av_codes << 1); 28118c2ecf20Sopenharmony_ci adv76xx_setup_format(state); 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_ci /* VS, HS polarities */ 28168c2ecf20Sopenharmony_ci io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | 28178c2ecf20Sopenharmony_ci pdata->inv_hs_pol << 1 | pdata->inv_llc_pol); 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci /* Adjust drive strength */ 28208c2ecf20Sopenharmony_ci io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 | 28218c2ecf20Sopenharmony_ci pdata->dr_str_clk << 2 | 28228c2ecf20Sopenharmony_ci pdata->dr_str_sync); 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */ 28258c2ecf20Sopenharmony_ci cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */ 28268c2ecf20Sopenharmony_ci cp_write(sd, 0xf9, 0x23); /* STDI ch. 1 - LCVS change threshold - 28278c2ecf20Sopenharmony_ci ADI recommended setting [REF_01, c. 2.3.3] */ 28288c2ecf20Sopenharmony_ci cp_write(sd, 0x45, 0x23); /* STDI ch. 2 - LCVS change threshold - 28298c2ecf20Sopenharmony_ci ADI recommended setting [REF_01, c. 2.3.3] */ 28308c2ecf20Sopenharmony_ci cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution 28318c2ecf20Sopenharmony_ci for digital formats */ 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci /* HDMI audio */ 28348c2ecf20Sopenharmony_ci hdmi_write_clr_set(sd, 0x15, 0x03, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */ 28358c2ecf20Sopenharmony_ci hdmi_write_clr_set(sd, 0x1a, 0x0e, 0x08); /* Wait 1 s before unmute */ 28368c2ecf20Sopenharmony_ci hdmi_write_clr_set(sd, 0x68, 0x06, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */ 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci /* TODO from platform data */ 28398c2ecf20Sopenharmony_ci afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci if (adv76xx_has_afe(state)) { 28428c2ecf20Sopenharmony_ci afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ 28438c2ecf20Sopenharmony_ci io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4); 28448c2ecf20Sopenharmony_ci } 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci /* interrupts */ 28478c2ecf20Sopenharmony_ci io_write(sd, 0x40, 0xc0 | pdata->int1_config); /* Configure INT1 */ 28488c2ecf20Sopenharmony_ci io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */ 28498c2ecf20Sopenharmony_ci io_write(sd, 0x6e, info->fmt_change_digital_mask); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ 28508c2ecf20Sopenharmony_ci io_write(sd, 0x73, info->cable_det_mask); /* Enable cable detection (+5v) interrupts */ 28518c2ecf20Sopenharmony_ci info->setup_irqs(sd); 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci return v4l2_ctrl_handler_setup(sd->ctrl_handler); 28548c2ecf20Sopenharmony_ci} 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_cistatic void adv7604_setup_irqs(struct v4l2_subdev *sd) 28578c2ecf20Sopenharmony_ci{ 28588c2ecf20Sopenharmony_ci io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ 28598c2ecf20Sopenharmony_ci} 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_cistatic void adv7611_setup_irqs(struct v4l2_subdev *sd) 28628c2ecf20Sopenharmony_ci{ 28638c2ecf20Sopenharmony_ci io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ 28648c2ecf20Sopenharmony_ci} 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_cistatic void adv7612_setup_irqs(struct v4l2_subdev *sd) 28678c2ecf20Sopenharmony_ci{ 28688c2ecf20Sopenharmony_ci io_write(sd, 0x41, 0xd0); /* disable INT2 */ 28698c2ecf20Sopenharmony_ci} 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_cistatic void adv76xx_unregister_clients(struct adv76xx_state *state) 28728c2ecf20Sopenharmony_ci{ 28738c2ecf20Sopenharmony_ci unsigned int i; 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) 28768c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_clients[i]); 28778c2ecf20Sopenharmony_ci} 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_cistatic struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd, 28808c2ecf20Sopenharmony_ci unsigned int page) 28818c2ecf20Sopenharmony_ci{ 28828c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 28838c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 28848c2ecf20Sopenharmony_ci struct adv76xx_platform_data *pdata = &state->pdata; 28858c2ecf20Sopenharmony_ci unsigned int io_reg = 0xf2 + page; 28868c2ecf20Sopenharmony_ci struct i2c_client *new_client; 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci if (pdata && pdata->i2c_addresses[page]) 28898c2ecf20Sopenharmony_ci new_client = i2c_new_dummy_device(client->adapter, 28908c2ecf20Sopenharmony_ci pdata->i2c_addresses[page]); 28918c2ecf20Sopenharmony_ci else 28928c2ecf20Sopenharmony_ci new_client = i2c_new_ancillary_device(client, 28938c2ecf20Sopenharmony_ci adv76xx_default_addresses[page].name, 28948c2ecf20Sopenharmony_ci adv76xx_default_addresses[page].default_addr); 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci if (!IS_ERR(new_client)) 28978c2ecf20Sopenharmony_ci io_write(sd, io_reg, new_client->addr << 1); 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci return new_client; 29008c2ecf20Sopenharmony_ci} 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_cistatic const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = { 29038c2ecf20Sopenharmony_ci /* reset ADI recommended settings for HDMI: */ 29048c2ecf20Sopenharmony_ci /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ 29058c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ 29068c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ 29078c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */ 29088c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */ 29098c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ 29108c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */ 29118c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */ 29128c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ 29138c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ 29148c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */ 29158c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */ 29168c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */ 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci /* set ADI recommended settings for digitizer */ 29198c2ecf20Sopenharmony_ci /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ 29208c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */ 29218c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */ 29228c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */ 29238c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */ 29248c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */ 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci { ADV76XX_REG_SEQ_TERM, 0 }, 29278c2ecf20Sopenharmony_ci}; 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_cistatic const struct adv76xx_reg_seq adv7604_recommended_settings_hdmi[] = { 29308c2ecf20Sopenharmony_ci /* set ADI recommended settings for HDMI: */ 29318c2ecf20Sopenharmony_ci /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ 29328c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */ 29338c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */ 29348c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */ 29358c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ 29368c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */ 29378c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */ 29388c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ 29398c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ 29408c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */ 29418c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */ 29428c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */ 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci /* reset ADI recommended settings for digitizer */ 29458c2ecf20Sopenharmony_ci /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ 29468c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */ 29478c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */ 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci { ADV76XX_REG_SEQ_TERM, 0 }, 29508c2ecf20Sopenharmony_ci}; 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_cistatic const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = { 29538c2ecf20Sopenharmony_ci /* ADV7611 Register Settings Recommendations Rev 1.5, May 2014 */ 29548c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 }, 29558c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 }, 29568c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 }, 29578c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f }, 29588c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 }, 29598c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda }, 29608c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 }, 29618c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 }, 29628c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 }, 29638c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x04 }, 29648c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x1e }, 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci { ADV76XX_REG_SEQ_TERM, 0 }, 29678c2ecf20Sopenharmony_ci}; 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_cistatic const struct adv76xx_reg_seq adv7612_recommended_settings_hdmi[] = { 29708c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 }, 29718c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 }, 29728c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 }, 29738c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f }, 29748c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 }, 29758c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda }, 29768c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 }, 29778c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 }, 29788c2ecf20Sopenharmony_ci { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 }, 29798c2ecf20Sopenharmony_ci { ADV76XX_REG_SEQ_TERM, 0 }, 29808c2ecf20Sopenharmony_ci}; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_cistatic const struct adv76xx_chip_info adv76xx_chip_info[] = { 29838c2ecf20Sopenharmony_ci [ADV7604] = { 29848c2ecf20Sopenharmony_ci .type = ADV7604, 29858c2ecf20Sopenharmony_ci .has_afe = true, 29868c2ecf20Sopenharmony_ci .max_port = ADV7604_PAD_VGA_COMP, 29878c2ecf20Sopenharmony_ci .num_dv_ports = 4, 29888c2ecf20Sopenharmony_ci .edid_enable_reg = 0x77, 29898c2ecf20Sopenharmony_ci .edid_status_reg = 0x7d, 29908c2ecf20Sopenharmony_ci .lcf_reg = 0xb3, 29918c2ecf20Sopenharmony_ci .tdms_lock_mask = 0xe0, 29928c2ecf20Sopenharmony_ci .cable_det_mask = 0x1e, 29938c2ecf20Sopenharmony_ci .fmt_change_digital_mask = 0xc1, 29948c2ecf20Sopenharmony_ci .cp_csc = 0xfc, 29958c2ecf20Sopenharmony_ci .cec_irq_status = 0x4d, 29968c2ecf20Sopenharmony_ci .cec_rx_enable = 0x26, 29978c2ecf20Sopenharmony_ci .cec_rx_enable_mask = 0x01, 29988c2ecf20Sopenharmony_ci .cec_irq_swap = true, 29998c2ecf20Sopenharmony_ci .formats = adv7604_formats, 30008c2ecf20Sopenharmony_ci .nformats = ARRAY_SIZE(adv7604_formats), 30018c2ecf20Sopenharmony_ci .set_termination = adv7604_set_termination, 30028c2ecf20Sopenharmony_ci .setup_irqs = adv7604_setup_irqs, 30038c2ecf20Sopenharmony_ci .read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock, 30048c2ecf20Sopenharmony_ci .read_cable_det = adv7604_read_cable_det, 30058c2ecf20Sopenharmony_ci .recommended_settings = { 30068c2ecf20Sopenharmony_ci [0] = adv7604_recommended_settings_afe, 30078c2ecf20Sopenharmony_ci [1] = adv7604_recommended_settings_hdmi, 30088c2ecf20Sopenharmony_ci }, 30098c2ecf20Sopenharmony_ci .num_recommended_settings = { 30108c2ecf20Sopenharmony_ci [0] = ARRAY_SIZE(adv7604_recommended_settings_afe), 30118c2ecf20Sopenharmony_ci [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi), 30128c2ecf20Sopenharmony_ci }, 30138c2ecf20Sopenharmony_ci .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) | 30148c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_CEC) | BIT(ADV76XX_PAGE_INFOFRAME) | 30158c2ecf20Sopenharmony_ci BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) | 30168c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_AFE) | BIT(ADV76XX_PAGE_REP) | 30178c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_EDID) | BIT(ADV76XX_PAGE_HDMI) | 30188c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_TEST) | BIT(ADV76XX_PAGE_CP) | 30198c2ecf20Sopenharmony_ci BIT(ADV7604_PAGE_VDP), 30208c2ecf20Sopenharmony_ci .linewidth_mask = 0xfff, 30218c2ecf20Sopenharmony_ci .field0_height_mask = 0xfff, 30228c2ecf20Sopenharmony_ci .field1_height_mask = 0xfff, 30238c2ecf20Sopenharmony_ci .hfrontporch_mask = 0x3ff, 30248c2ecf20Sopenharmony_ci .hsync_mask = 0x3ff, 30258c2ecf20Sopenharmony_ci .hbackporch_mask = 0x3ff, 30268c2ecf20Sopenharmony_ci .field0_vfrontporch_mask = 0x1fff, 30278c2ecf20Sopenharmony_ci .field0_vsync_mask = 0x1fff, 30288c2ecf20Sopenharmony_ci .field0_vbackporch_mask = 0x1fff, 30298c2ecf20Sopenharmony_ci .field1_vfrontporch_mask = 0x1fff, 30308c2ecf20Sopenharmony_ci .field1_vsync_mask = 0x1fff, 30318c2ecf20Sopenharmony_ci .field1_vbackporch_mask = 0x1fff, 30328c2ecf20Sopenharmony_ci }, 30338c2ecf20Sopenharmony_ci [ADV7611] = { 30348c2ecf20Sopenharmony_ci .type = ADV7611, 30358c2ecf20Sopenharmony_ci .has_afe = false, 30368c2ecf20Sopenharmony_ci .max_port = ADV76XX_PAD_HDMI_PORT_A, 30378c2ecf20Sopenharmony_ci .num_dv_ports = 1, 30388c2ecf20Sopenharmony_ci .edid_enable_reg = 0x74, 30398c2ecf20Sopenharmony_ci .edid_status_reg = 0x76, 30408c2ecf20Sopenharmony_ci .lcf_reg = 0xa3, 30418c2ecf20Sopenharmony_ci .tdms_lock_mask = 0x43, 30428c2ecf20Sopenharmony_ci .cable_det_mask = 0x01, 30438c2ecf20Sopenharmony_ci .fmt_change_digital_mask = 0x03, 30448c2ecf20Sopenharmony_ci .cp_csc = 0xf4, 30458c2ecf20Sopenharmony_ci .cec_irq_status = 0x93, 30468c2ecf20Sopenharmony_ci .cec_rx_enable = 0x2c, 30478c2ecf20Sopenharmony_ci .cec_rx_enable_mask = 0x02, 30488c2ecf20Sopenharmony_ci .formats = adv7611_formats, 30498c2ecf20Sopenharmony_ci .nformats = ARRAY_SIZE(adv7611_formats), 30508c2ecf20Sopenharmony_ci .set_termination = adv7611_set_termination, 30518c2ecf20Sopenharmony_ci .setup_irqs = adv7611_setup_irqs, 30528c2ecf20Sopenharmony_ci .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, 30538c2ecf20Sopenharmony_ci .read_cable_det = adv7611_read_cable_det, 30548c2ecf20Sopenharmony_ci .recommended_settings = { 30558c2ecf20Sopenharmony_ci [1] = adv7611_recommended_settings_hdmi, 30568c2ecf20Sopenharmony_ci }, 30578c2ecf20Sopenharmony_ci .num_recommended_settings = { 30588c2ecf20Sopenharmony_ci [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi), 30598c2ecf20Sopenharmony_ci }, 30608c2ecf20Sopenharmony_ci .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) | 30618c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) | 30628c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) | 30638c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP), 30648c2ecf20Sopenharmony_ci .linewidth_mask = 0x1fff, 30658c2ecf20Sopenharmony_ci .field0_height_mask = 0x1fff, 30668c2ecf20Sopenharmony_ci .field1_height_mask = 0x1fff, 30678c2ecf20Sopenharmony_ci .hfrontporch_mask = 0x1fff, 30688c2ecf20Sopenharmony_ci .hsync_mask = 0x1fff, 30698c2ecf20Sopenharmony_ci .hbackporch_mask = 0x1fff, 30708c2ecf20Sopenharmony_ci .field0_vfrontporch_mask = 0x3fff, 30718c2ecf20Sopenharmony_ci .field0_vsync_mask = 0x3fff, 30728c2ecf20Sopenharmony_ci .field0_vbackporch_mask = 0x3fff, 30738c2ecf20Sopenharmony_ci .field1_vfrontporch_mask = 0x3fff, 30748c2ecf20Sopenharmony_ci .field1_vsync_mask = 0x3fff, 30758c2ecf20Sopenharmony_ci .field1_vbackporch_mask = 0x3fff, 30768c2ecf20Sopenharmony_ci }, 30778c2ecf20Sopenharmony_ci [ADV7612] = { 30788c2ecf20Sopenharmony_ci .type = ADV7612, 30798c2ecf20Sopenharmony_ci .has_afe = false, 30808c2ecf20Sopenharmony_ci .max_port = ADV76XX_PAD_HDMI_PORT_A, /* B not supported */ 30818c2ecf20Sopenharmony_ci .num_dv_ports = 1, /* normally 2 */ 30828c2ecf20Sopenharmony_ci .edid_enable_reg = 0x74, 30838c2ecf20Sopenharmony_ci .edid_status_reg = 0x76, 30848c2ecf20Sopenharmony_ci .lcf_reg = 0xa3, 30858c2ecf20Sopenharmony_ci .tdms_lock_mask = 0x43, 30868c2ecf20Sopenharmony_ci .cable_det_mask = 0x01, 30878c2ecf20Sopenharmony_ci .fmt_change_digital_mask = 0x03, 30888c2ecf20Sopenharmony_ci .cp_csc = 0xf4, 30898c2ecf20Sopenharmony_ci .cec_irq_status = 0x93, 30908c2ecf20Sopenharmony_ci .cec_rx_enable = 0x2c, 30918c2ecf20Sopenharmony_ci .cec_rx_enable_mask = 0x02, 30928c2ecf20Sopenharmony_ci .formats = adv7612_formats, 30938c2ecf20Sopenharmony_ci .nformats = ARRAY_SIZE(adv7612_formats), 30948c2ecf20Sopenharmony_ci .set_termination = adv7611_set_termination, 30958c2ecf20Sopenharmony_ci .setup_irqs = adv7612_setup_irqs, 30968c2ecf20Sopenharmony_ci .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, 30978c2ecf20Sopenharmony_ci .read_cable_det = adv7612_read_cable_det, 30988c2ecf20Sopenharmony_ci .recommended_settings = { 30998c2ecf20Sopenharmony_ci [1] = adv7612_recommended_settings_hdmi, 31008c2ecf20Sopenharmony_ci }, 31018c2ecf20Sopenharmony_ci .num_recommended_settings = { 31028c2ecf20Sopenharmony_ci [1] = ARRAY_SIZE(adv7612_recommended_settings_hdmi), 31038c2ecf20Sopenharmony_ci }, 31048c2ecf20Sopenharmony_ci .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) | 31058c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) | 31068c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) | 31078c2ecf20Sopenharmony_ci BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP), 31088c2ecf20Sopenharmony_ci .linewidth_mask = 0x1fff, 31098c2ecf20Sopenharmony_ci .field0_height_mask = 0x1fff, 31108c2ecf20Sopenharmony_ci .field1_height_mask = 0x1fff, 31118c2ecf20Sopenharmony_ci .hfrontporch_mask = 0x1fff, 31128c2ecf20Sopenharmony_ci .hsync_mask = 0x1fff, 31138c2ecf20Sopenharmony_ci .hbackporch_mask = 0x1fff, 31148c2ecf20Sopenharmony_ci .field0_vfrontporch_mask = 0x3fff, 31158c2ecf20Sopenharmony_ci .field0_vsync_mask = 0x3fff, 31168c2ecf20Sopenharmony_ci .field0_vbackporch_mask = 0x3fff, 31178c2ecf20Sopenharmony_ci .field1_vfrontporch_mask = 0x3fff, 31188c2ecf20Sopenharmony_ci .field1_vsync_mask = 0x3fff, 31198c2ecf20Sopenharmony_ci .field1_vbackporch_mask = 0x3fff, 31208c2ecf20Sopenharmony_ci }, 31218c2ecf20Sopenharmony_ci}; 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_cistatic const struct i2c_device_id adv76xx_i2c_id[] = { 31248c2ecf20Sopenharmony_ci { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, 31258c2ecf20Sopenharmony_ci { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, 31268c2ecf20Sopenharmony_ci { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, 31278c2ecf20Sopenharmony_ci { } 31288c2ecf20Sopenharmony_ci}; 31298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_cistatic const struct of_device_id adv76xx_of_id[] __maybe_unused = { 31328c2ecf20Sopenharmony_ci { .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] }, 31338c2ecf20Sopenharmony_ci { .compatible = "adi,adv7612", .data = &adv76xx_chip_info[ADV7612] }, 31348c2ecf20Sopenharmony_ci { } 31358c2ecf20Sopenharmony_ci}; 31368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, adv76xx_of_id); 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_cistatic int adv76xx_parse_dt(struct adv76xx_state *state) 31398c2ecf20Sopenharmony_ci{ 31408c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; 31418c2ecf20Sopenharmony_ci struct device_node *endpoint; 31428c2ecf20Sopenharmony_ci struct device_node *np; 31438c2ecf20Sopenharmony_ci unsigned int flags; 31448c2ecf20Sopenharmony_ci int ret; 31458c2ecf20Sopenharmony_ci u32 v; 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node; 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci /* Parse the endpoint. */ 31508c2ecf20Sopenharmony_ci endpoint = of_graph_get_next_endpoint(np, NULL); 31518c2ecf20Sopenharmony_ci if (!endpoint) 31528c2ecf20Sopenharmony_ci return -EINVAL; 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg); 31558c2ecf20Sopenharmony_ci of_node_put(endpoint); 31568c2ecf20Sopenharmony_ci if (ret) 31578c2ecf20Sopenharmony_ci return ret; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "default-input", &v)) 31608c2ecf20Sopenharmony_ci state->pdata.default_input = v; 31618c2ecf20Sopenharmony_ci else 31628c2ecf20Sopenharmony_ci state->pdata.default_input = -1; 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci flags = bus_cfg.bus.parallel.flags; 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) 31678c2ecf20Sopenharmony_ci state->pdata.inv_hs_pol = 1; 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) 31708c2ecf20Sopenharmony_ci state->pdata.inv_vs_pol = 1; 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) 31738c2ecf20Sopenharmony_ci state->pdata.inv_llc_pol = 1; 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci if (bus_cfg.bus_type == V4L2_MBUS_BT656) 31768c2ecf20Sopenharmony_ci state->pdata.insert_av_codes = 1; 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci /* Disable the interrupt for now as no DT-based board uses it. */ 31798c2ecf20Sopenharmony_ci state->pdata.int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH; 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_ci /* Hardcode the remaining platform data fields. */ 31828c2ecf20Sopenharmony_ci state->pdata.disable_pwrdnb = 0; 31838c2ecf20Sopenharmony_ci state->pdata.disable_cable_det_rst = 0; 31848c2ecf20Sopenharmony_ci state->pdata.blank_data = 1; 31858c2ecf20Sopenharmony_ci state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0; 31868c2ecf20Sopenharmony_ci state->pdata.bus_order = ADV7604_BUS_ORDER_RGB; 31878c2ecf20Sopenharmony_ci state->pdata.dr_str_data = ADV76XX_DR_STR_MEDIUM_HIGH; 31888c2ecf20Sopenharmony_ci state->pdata.dr_str_clk = ADV76XX_DR_STR_MEDIUM_HIGH; 31898c2ecf20Sopenharmony_ci state->pdata.dr_str_sync = ADV76XX_DR_STR_MEDIUM_HIGH; 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci return 0; 31928c2ecf20Sopenharmony_ci} 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_cistatic const struct regmap_config adv76xx_regmap_cnf[] = { 31958c2ecf20Sopenharmony_ci { 31968c2ecf20Sopenharmony_ci .name = "io", 31978c2ecf20Sopenharmony_ci .reg_bits = 8, 31988c2ecf20Sopenharmony_ci .val_bits = 8, 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci .max_register = 0xff, 32018c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32028c2ecf20Sopenharmony_ci }, 32038c2ecf20Sopenharmony_ci { 32048c2ecf20Sopenharmony_ci .name = "avlink", 32058c2ecf20Sopenharmony_ci .reg_bits = 8, 32068c2ecf20Sopenharmony_ci .val_bits = 8, 32078c2ecf20Sopenharmony_ci 32088c2ecf20Sopenharmony_ci .max_register = 0xff, 32098c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32108c2ecf20Sopenharmony_ci }, 32118c2ecf20Sopenharmony_ci { 32128c2ecf20Sopenharmony_ci .name = "cec", 32138c2ecf20Sopenharmony_ci .reg_bits = 8, 32148c2ecf20Sopenharmony_ci .val_bits = 8, 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci .max_register = 0xff, 32178c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32188c2ecf20Sopenharmony_ci }, 32198c2ecf20Sopenharmony_ci { 32208c2ecf20Sopenharmony_ci .name = "infoframe", 32218c2ecf20Sopenharmony_ci .reg_bits = 8, 32228c2ecf20Sopenharmony_ci .val_bits = 8, 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci .max_register = 0xff, 32258c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32268c2ecf20Sopenharmony_ci }, 32278c2ecf20Sopenharmony_ci { 32288c2ecf20Sopenharmony_ci .name = "esdp", 32298c2ecf20Sopenharmony_ci .reg_bits = 8, 32308c2ecf20Sopenharmony_ci .val_bits = 8, 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci .max_register = 0xff, 32338c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32348c2ecf20Sopenharmony_ci }, 32358c2ecf20Sopenharmony_ci { 32368c2ecf20Sopenharmony_ci .name = "epp", 32378c2ecf20Sopenharmony_ci .reg_bits = 8, 32388c2ecf20Sopenharmony_ci .val_bits = 8, 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci .max_register = 0xff, 32418c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32428c2ecf20Sopenharmony_ci }, 32438c2ecf20Sopenharmony_ci { 32448c2ecf20Sopenharmony_ci .name = "afe", 32458c2ecf20Sopenharmony_ci .reg_bits = 8, 32468c2ecf20Sopenharmony_ci .val_bits = 8, 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci .max_register = 0xff, 32498c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32508c2ecf20Sopenharmony_ci }, 32518c2ecf20Sopenharmony_ci { 32528c2ecf20Sopenharmony_ci .name = "rep", 32538c2ecf20Sopenharmony_ci .reg_bits = 8, 32548c2ecf20Sopenharmony_ci .val_bits = 8, 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci .max_register = 0xff, 32578c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32588c2ecf20Sopenharmony_ci }, 32598c2ecf20Sopenharmony_ci { 32608c2ecf20Sopenharmony_ci .name = "edid", 32618c2ecf20Sopenharmony_ci .reg_bits = 8, 32628c2ecf20Sopenharmony_ci .val_bits = 8, 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci .max_register = 0xff, 32658c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32668c2ecf20Sopenharmony_ci }, 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci { 32698c2ecf20Sopenharmony_ci .name = "hdmi", 32708c2ecf20Sopenharmony_ci .reg_bits = 8, 32718c2ecf20Sopenharmony_ci .val_bits = 8, 32728c2ecf20Sopenharmony_ci 32738c2ecf20Sopenharmony_ci .max_register = 0xff, 32748c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32758c2ecf20Sopenharmony_ci }, 32768c2ecf20Sopenharmony_ci { 32778c2ecf20Sopenharmony_ci .name = "test", 32788c2ecf20Sopenharmony_ci .reg_bits = 8, 32798c2ecf20Sopenharmony_ci .val_bits = 8, 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci .max_register = 0xff, 32828c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32838c2ecf20Sopenharmony_ci }, 32848c2ecf20Sopenharmony_ci { 32858c2ecf20Sopenharmony_ci .name = "cp", 32868c2ecf20Sopenharmony_ci .reg_bits = 8, 32878c2ecf20Sopenharmony_ci .val_bits = 8, 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci .max_register = 0xff, 32908c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32918c2ecf20Sopenharmony_ci }, 32928c2ecf20Sopenharmony_ci { 32938c2ecf20Sopenharmony_ci .name = "vdp", 32948c2ecf20Sopenharmony_ci .reg_bits = 8, 32958c2ecf20Sopenharmony_ci .val_bits = 8, 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci .max_register = 0xff, 32988c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 32998c2ecf20Sopenharmony_ci }, 33008c2ecf20Sopenharmony_ci}; 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_cistatic int configure_regmap(struct adv76xx_state *state, int region) 33038c2ecf20Sopenharmony_ci{ 33048c2ecf20Sopenharmony_ci int err; 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci if (!state->i2c_clients[region]) 33078c2ecf20Sopenharmony_ci return -ENODEV; 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci state->regmap[region] = 33108c2ecf20Sopenharmony_ci devm_regmap_init_i2c(state->i2c_clients[region], 33118c2ecf20Sopenharmony_ci &adv76xx_regmap_cnf[region]); 33128c2ecf20Sopenharmony_ci 33138c2ecf20Sopenharmony_ci if (IS_ERR(state->regmap[region])) { 33148c2ecf20Sopenharmony_ci err = PTR_ERR(state->regmap[region]); 33158c2ecf20Sopenharmony_ci v4l_err(state->i2c_clients[region], 33168c2ecf20Sopenharmony_ci "Error initializing regmap %d with error %d\n", 33178c2ecf20Sopenharmony_ci region, err); 33188c2ecf20Sopenharmony_ci return -EINVAL; 33198c2ecf20Sopenharmony_ci } 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci return 0; 33228c2ecf20Sopenharmony_ci} 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_cistatic int configure_regmaps(struct adv76xx_state *state) 33258c2ecf20Sopenharmony_ci{ 33268c2ecf20Sopenharmony_ci int i, err; 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci for (i = ADV7604_PAGE_AVLINK ; i < ADV76XX_PAGE_MAX; i++) { 33298c2ecf20Sopenharmony_ci err = configure_regmap(state, i); 33308c2ecf20Sopenharmony_ci if (err && (err != -ENODEV)) 33318c2ecf20Sopenharmony_ci return err; 33328c2ecf20Sopenharmony_ci } 33338c2ecf20Sopenharmony_ci return 0; 33348c2ecf20Sopenharmony_ci} 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_cistatic void adv76xx_reset(struct adv76xx_state *state) 33378c2ecf20Sopenharmony_ci{ 33388c2ecf20Sopenharmony_ci if (state->reset_gpio) { 33398c2ecf20Sopenharmony_ci /* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */ 33408c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(state->reset_gpio, 0); 33418c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 33428c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(state->reset_gpio, 1); 33438c2ecf20Sopenharmony_ci /* It is recommended to wait 5 ms after the low pulse before */ 33448c2ecf20Sopenharmony_ci /* an I2C write is performed to the ADV76XX. */ 33458c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 33468c2ecf20Sopenharmony_ci } 33478c2ecf20Sopenharmony_ci} 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_cistatic int adv76xx_probe(struct i2c_client *client, 33508c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 33518c2ecf20Sopenharmony_ci{ 33528c2ecf20Sopenharmony_ci static const struct v4l2_dv_timings cea640x480 = 33538c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_640X480P59_94; 33548c2ecf20Sopenharmony_ci struct adv76xx_state *state; 33558c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 33568c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl; 33578c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 33588c2ecf20Sopenharmony_ci unsigned int i; 33598c2ecf20Sopenharmony_ci unsigned int val, val2; 33608c2ecf20Sopenharmony_ci int err; 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci /* Check if the adapter supports the needed features */ 33638c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 33648c2ecf20Sopenharmony_ci return -EIO; 33658c2ecf20Sopenharmony_ci v4l_dbg(1, debug, client, "detecting adv76xx client on address 0x%x\n", 33668c2ecf20Sopenharmony_ci client->addr << 1); 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); 33698c2ecf20Sopenharmony_ci if (!state) 33708c2ecf20Sopenharmony_ci return -ENOMEM; 33718c2ecf20Sopenharmony_ci 33728c2ecf20Sopenharmony_ci state->i2c_clients[ADV76XX_PAGE_IO] = client; 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci /* initialize variables */ 33758c2ecf20Sopenharmony_ci state->restart_stdi_once = true; 33768c2ecf20Sopenharmony_ci state->selected_input = ~0; 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { 33798c2ecf20Sopenharmony_ci const struct of_device_id *oid; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci oid = of_match_node(adv76xx_of_id, client->dev.of_node); 33828c2ecf20Sopenharmony_ci state->info = oid->data; 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci err = adv76xx_parse_dt(state); 33858c2ecf20Sopenharmony_ci if (err < 0) { 33868c2ecf20Sopenharmony_ci v4l_err(client, "DT parsing error\n"); 33878c2ecf20Sopenharmony_ci return err; 33888c2ecf20Sopenharmony_ci } 33898c2ecf20Sopenharmony_ci } else if (client->dev.platform_data) { 33908c2ecf20Sopenharmony_ci struct adv76xx_platform_data *pdata = client->dev.platform_data; 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci state->info = (const struct adv76xx_chip_info *)id->driver_data; 33938c2ecf20Sopenharmony_ci state->pdata = *pdata; 33948c2ecf20Sopenharmony_ci } else { 33958c2ecf20Sopenharmony_ci v4l_err(client, "No platform data!\n"); 33968c2ecf20Sopenharmony_ci return -ENODEV; 33978c2ecf20Sopenharmony_ci } 33988c2ecf20Sopenharmony_ci 33998c2ecf20Sopenharmony_ci /* Request GPIOs. */ 34008c2ecf20Sopenharmony_ci for (i = 0; i < state->info->num_dv_ports; ++i) { 34018c2ecf20Sopenharmony_ci state->hpd_gpio[i] = 34028c2ecf20Sopenharmony_ci devm_gpiod_get_index_optional(&client->dev, "hpd", i, 34038c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 34048c2ecf20Sopenharmony_ci if (IS_ERR(state->hpd_gpio[i])) 34058c2ecf20Sopenharmony_ci return PTR_ERR(state->hpd_gpio[i]); 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci if (state->hpd_gpio[i]) 34088c2ecf20Sopenharmony_ci v4l_info(client, "Handling HPD %u GPIO\n", i); 34098c2ecf20Sopenharmony_ci } 34108c2ecf20Sopenharmony_ci state->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", 34118c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 34128c2ecf20Sopenharmony_ci if (IS_ERR(state->reset_gpio)) 34138c2ecf20Sopenharmony_ci return PTR_ERR(state->reset_gpio); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci adv76xx_reset(state); 34168c2ecf20Sopenharmony_ci 34178c2ecf20Sopenharmony_ci state->timings = cea640x480; 34188c2ecf20Sopenharmony_ci state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci sd = &state->sd; 34218c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &adv76xx_ops); 34228c2ecf20Sopenharmony_ci snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", 34238c2ecf20Sopenharmony_ci id->name, i2c_adapter_id(client->adapter), 34248c2ecf20Sopenharmony_ci client->addr); 34258c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 34268c2ecf20Sopenharmony_ci sd->internal_ops = &adv76xx_int_ops; 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci /* Configure IO Regmap region */ 34298c2ecf20Sopenharmony_ci err = configure_regmap(state, ADV76XX_PAGE_IO); 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_ci if (err) { 34328c2ecf20Sopenharmony_ci v4l2_err(sd, "Error configuring IO regmap region\n"); 34338c2ecf20Sopenharmony_ci return -ENODEV; 34348c2ecf20Sopenharmony_ci } 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci /* 34378c2ecf20Sopenharmony_ci * Verify that the chip is present. On ADV7604 the RD_INFO register only 34388c2ecf20Sopenharmony_ci * identifies the revision, while on ADV7611 it identifies the model as 34398c2ecf20Sopenharmony_ci * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. 34408c2ecf20Sopenharmony_ci */ 34418c2ecf20Sopenharmony_ci switch (state->info->type) { 34428c2ecf20Sopenharmony_ci case ADV7604: 34438c2ecf20Sopenharmony_ci err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xfb, &val); 34448c2ecf20Sopenharmony_ci if (err) { 34458c2ecf20Sopenharmony_ci v4l2_err(sd, "Error %d reading IO Regmap\n", err); 34468c2ecf20Sopenharmony_ci return -ENODEV; 34478c2ecf20Sopenharmony_ci } 34488c2ecf20Sopenharmony_ci if (val != 0x68) { 34498c2ecf20Sopenharmony_ci v4l2_err(sd, "not an adv7604 on address 0x%x\n", 34508c2ecf20Sopenharmony_ci client->addr << 1); 34518c2ecf20Sopenharmony_ci return -ENODEV; 34528c2ecf20Sopenharmony_ci } 34538c2ecf20Sopenharmony_ci break; 34548c2ecf20Sopenharmony_ci case ADV7611: 34558c2ecf20Sopenharmony_ci case ADV7612: 34568c2ecf20Sopenharmony_ci err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 34578c2ecf20Sopenharmony_ci 0xea, 34588c2ecf20Sopenharmony_ci &val); 34598c2ecf20Sopenharmony_ci if (err) { 34608c2ecf20Sopenharmony_ci v4l2_err(sd, "Error %d reading IO Regmap\n", err); 34618c2ecf20Sopenharmony_ci return -ENODEV; 34628c2ecf20Sopenharmony_ci } 34638c2ecf20Sopenharmony_ci val2 = val << 8; 34648c2ecf20Sopenharmony_ci err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 34658c2ecf20Sopenharmony_ci 0xeb, 34668c2ecf20Sopenharmony_ci &val); 34678c2ecf20Sopenharmony_ci if (err) { 34688c2ecf20Sopenharmony_ci v4l2_err(sd, "Error %d reading IO Regmap\n", err); 34698c2ecf20Sopenharmony_ci return -ENODEV; 34708c2ecf20Sopenharmony_ci } 34718c2ecf20Sopenharmony_ci val |= val2; 34728c2ecf20Sopenharmony_ci if ((state->info->type == ADV7611 && val != 0x2051) || 34738c2ecf20Sopenharmony_ci (state->info->type == ADV7612 && val != 0x2041)) { 34748c2ecf20Sopenharmony_ci v4l2_err(sd, "not an adv761x on address 0x%x\n", 34758c2ecf20Sopenharmony_ci client->addr << 1); 34768c2ecf20Sopenharmony_ci return -ENODEV; 34778c2ecf20Sopenharmony_ci } 34788c2ecf20Sopenharmony_ci break; 34798c2ecf20Sopenharmony_ci } 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci /* control handlers */ 34828c2ecf20Sopenharmony_ci hdl = &state->hdl; 34838c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, adv76xx_has_afe(state) ? 9 : 8); 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, 34868c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 34878c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, 34888c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 128); 34898c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, 34908c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 128); 34918c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, 34928c2ecf20Sopenharmony_ci V4L2_CID_HUE, 0, 128, 1, 0); 34938c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, 34948c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 34958c2ecf20Sopenharmony_ci 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); 34968c2ecf20Sopenharmony_ci if (ctrl) 34978c2ecf20Sopenharmony_ci ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 34988c2ecf20Sopenharmony_ci 34998c2ecf20Sopenharmony_ci state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, 35008c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_POWER_PRESENT, 0, 35018c2ecf20Sopenharmony_ci (1 << state->info->num_dv_ports) - 1, 0, 0); 35028c2ecf20Sopenharmony_ci state->rgb_quantization_range_ctrl = 35038c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, 35048c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 35058c2ecf20Sopenharmony_ci 0, V4L2_DV_RGB_RANGE_AUTO); 35068c2ecf20Sopenharmony_ci 35078c2ecf20Sopenharmony_ci /* custom controls */ 35088c2ecf20Sopenharmony_ci if (adv76xx_has_afe(state)) 35098c2ecf20Sopenharmony_ci state->analog_sampling_phase_ctrl = 35108c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); 35118c2ecf20Sopenharmony_ci state->free_run_color_manual_ctrl = 35128c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color_manual, NULL); 35138c2ecf20Sopenharmony_ci state->free_run_color_ctrl = 35148c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color, NULL); 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci sd->ctrl_handler = hdl; 35178c2ecf20Sopenharmony_ci if (hdl->error) { 35188c2ecf20Sopenharmony_ci err = hdl->error; 35198c2ecf20Sopenharmony_ci goto err_hdl; 35208c2ecf20Sopenharmony_ci } 35218c2ecf20Sopenharmony_ci if (adv76xx_s_detect_tx_5v_ctrl(sd)) { 35228c2ecf20Sopenharmony_ci err = -ENODEV; 35238c2ecf20Sopenharmony_ci goto err_hdl; 35248c2ecf20Sopenharmony_ci } 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci for (i = 1; i < ADV76XX_PAGE_MAX; ++i) { 35278c2ecf20Sopenharmony_ci struct i2c_client *dummy_client; 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci if (!(BIT(i) & state->info->page_mask)) 35308c2ecf20Sopenharmony_ci continue; 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci dummy_client = adv76xx_dummy_client(sd, i); 35338c2ecf20Sopenharmony_ci if (IS_ERR(dummy_client)) { 35348c2ecf20Sopenharmony_ci err = PTR_ERR(dummy_client); 35358c2ecf20Sopenharmony_ci v4l2_err(sd, "failed to create i2c client %u\n", i); 35368c2ecf20Sopenharmony_ci goto err_i2c; 35378c2ecf20Sopenharmony_ci } 35388c2ecf20Sopenharmony_ci 35398c2ecf20Sopenharmony_ci state->i2c_clients[i] = dummy_client; 35408c2ecf20Sopenharmony_ci } 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, 35438c2ecf20Sopenharmony_ci adv76xx_delayed_work_enable_hotplug); 35448c2ecf20Sopenharmony_ci 35458c2ecf20Sopenharmony_ci state->source_pad = state->info->num_dv_ports 35468c2ecf20Sopenharmony_ci + (state->info->has_afe ? 2 : 0); 35478c2ecf20Sopenharmony_ci for (i = 0; i < state->source_pad; ++i) 35488c2ecf20Sopenharmony_ci state->pads[i].flags = MEDIA_PAD_FL_SINK; 35498c2ecf20Sopenharmony_ci state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; 35508c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_DV_DECODER; 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_ci err = media_entity_pads_init(&sd->entity, state->source_pad + 1, 35538c2ecf20Sopenharmony_ci state->pads); 35548c2ecf20Sopenharmony_ci if (err) 35558c2ecf20Sopenharmony_ci goto err_work_queues; 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci /* Configure regmaps */ 35588c2ecf20Sopenharmony_ci err = configure_regmaps(state); 35598c2ecf20Sopenharmony_ci if (err) 35608c2ecf20Sopenharmony_ci goto err_entity; 35618c2ecf20Sopenharmony_ci 35628c2ecf20Sopenharmony_ci err = adv76xx_core_init(sd); 35638c2ecf20Sopenharmony_ci if (err) 35648c2ecf20Sopenharmony_ci goto err_entity; 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci if (client->irq) { 35678c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(&client->dev, 35688c2ecf20Sopenharmony_ci client->irq, 35698c2ecf20Sopenharmony_ci NULL, adv76xx_irq_handler, 35708c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 35718c2ecf20Sopenharmony_ci client->name, state); 35728c2ecf20Sopenharmony_ci if (err) 35738c2ecf20Sopenharmony_ci goto err_entity; 35748c2ecf20Sopenharmony_ci } 35758c2ecf20Sopenharmony_ci 35768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) 35778c2ecf20Sopenharmony_ci state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops, 35788c2ecf20Sopenharmony_ci state, dev_name(&client->dev), 35798c2ecf20Sopenharmony_ci CEC_CAP_DEFAULTS, ADV76XX_MAX_ADDRS); 35808c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(state->cec_adap); 35818c2ecf20Sopenharmony_ci if (err) 35828c2ecf20Sopenharmony_ci goto err_entity; 35838c2ecf20Sopenharmony_ci#endif 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, 35868c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci err = v4l2_async_register_subdev(sd); 35898c2ecf20Sopenharmony_ci if (err) 35908c2ecf20Sopenharmony_ci goto err_entity; 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ci return 0; 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_cierr_entity: 35958c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 35968c2ecf20Sopenharmony_cierr_work_queues: 35978c2ecf20Sopenharmony_ci cancel_delayed_work(&state->delayed_work_enable_hotplug); 35988c2ecf20Sopenharmony_cierr_i2c: 35998c2ecf20Sopenharmony_ci adv76xx_unregister_clients(state); 36008c2ecf20Sopenharmony_cierr_hdl: 36018c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 36028c2ecf20Sopenharmony_ci return err; 36038c2ecf20Sopenharmony_ci} 36048c2ecf20Sopenharmony_ci 36058c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_cistatic int adv76xx_remove(struct i2c_client *client) 36088c2ecf20Sopenharmony_ci{ 36098c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 36108c2ecf20Sopenharmony_ci struct adv76xx_state *state = to_state(sd); 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci /* disable interrupts */ 36138c2ecf20Sopenharmony_ci io_write(sd, 0x40, 0); 36148c2ecf20Sopenharmony_ci io_write(sd, 0x41, 0); 36158c2ecf20Sopenharmony_ci io_write(sd, 0x46, 0); 36168c2ecf20Sopenharmony_ci io_write(sd, 0x6e, 0); 36178c2ecf20Sopenharmony_ci io_write(sd, 0x73, 0); 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); 36208c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 36218c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 36228c2ecf20Sopenharmony_ci adv76xx_unregister_clients(to_state(sd)); 36238c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 36248c2ecf20Sopenharmony_ci return 0; 36258c2ecf20Sopenharmony_ci} 36268c2ecf20Sopenharmony_ci 36278c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_cistatic struct i2c_driver adv76xx_driver = { 36308c2ecf20Sopenharmony_ci .driver = { 36318c2ecf20Sopenharmony_ci .name = "adv7604", 36328c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(adv76xx_of_id), 36338c2ecf20Sopenharmony_ci }, 36348c2ecf20Sopenharmony_ci .probe = adv76xx_probe, 36358c2ecf20Sopenharmony_ci .remove = adv76xx_remove, 36368c2ecf20Sopenharmony_ci .id_table = adv76xx_i2c_id, 36378c2ecf20Sopenharmony_ci}; 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_cimodule_i2c_driver(adv76xx_driver); 3640