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