18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Analog Devices ADV7511 HDMI Transmitter Device 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 * This file is named adv7511-v4l2.c so it doesn't conflict with the Analog 108c2ecf20Sopenharmony_ci * Device ADV7511 (config fragment CONFIG_DRM_I2C_ADV7511). 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 238c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 288c2ecf20Sopenharmony_ci#include <media/i2c/adv7511.h> 298c2ecf20Sopenharmony_ci#include <media/cec.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int debug; 328c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-2)"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices ADV7511 HDMI Transmitter Device Driver"); 368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil"); 378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MASK_ADV7511_EDID_RDY_INT 0x04 408c2ecf20Sopenharmony_ci#define MASK_ADV7511_MSEN_INT 0x40 418c2ecf20Sopenharmony_ci#define MASK_ADV7511_HPD_INT 0x80 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define MASK_ADV7511_HPD_DETECT 0x40 448c2ecf20Sopenharmony_ci#define MASK_ADV7511_MSEN_DETECT 0x20 458c2ecf20Sopenharmony_ci#define MASK_ADV7511_EDID_RDY 0x10 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define EDID_MAX_RETRIES (8) 488c2ecf20Sopenharmony_ci#define EDID_DELAY 250 498c2ecf20Sopenharmony_ci#define EDID_MAX_SEGM 8 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define ADV7511_MAX_WIDTH 1920 528c2ecf20Sopenharmony_ci#define ADV7511_MAX_HEIGHT 1200 538c2ecf20Sopenharmony_ci#define ADV7511_MIN_PIXELCLOCK 20000000 548c2ecf20Sopenharmony_ci#define ADV7511_MAX_PIXELCLOCK 225000000 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define ADV7511_MAX_ADDRS (3) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci********************************************************************** 608c2ecf20Sopenharmony_ci* 618c2ecf20Sopenharmony_ci* Arrays with configuration parameters for the ADV7511 628c2ecf20Sopenharmony_ci* 638c2ecf20Sopenharmony_ci********************************************************************** 648c2ecf20Sopenharmony_ci*/ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct i2c_reg_value { 678c2ecf20Sopenharmony_ci unsigned char reg; 688c2ecf20Sopenharmony_ci unsigned char value; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct adv7511_state_edid { 728c2ecf20Sopenharmony_ci /* total number of blocks */ 738c2ecf20Sopenharmony_ci u32 blocks; 748c2ecf20Sopenharmony_ci /* Number of segments read */ 758c2ecf20Sopenharmony_ci u32 segments; 768c2ecf20Sopenharmony_ci u8 data[EDID_MAX_SEGM * 256]; 778c2ecf20Sopenharmony_ci /* Number of EDID read retries left */ 788c2ecf20Sopenharmony_ci unsigned read_retries; 798c2ecf20Sopenharmony_ci bool complete; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct adv7511_state { 838c2ecf20Sopenharmony_ci struct adv7511_platform_data pdata; 848c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 858c2ecf20Sopenharmony_ci struct media_pad pad; 868c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 878c2ecf20Sopenharmony_ci int chip_revision; 888c2ecf20Sopenharmony_ci u8 i2c_edid_addr; 898c2ecf20Sopenharmony_ci u8 i2c_pktmem_addr; 908c2ecf20Sopenharmony_ci u8 i2c_cec_addr; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci struct i2c_client *i2c_cec; 938c2ecf20Sopenharmony_ci struct cec_adapter *cec_adap; 948c2ecf20Sopenharmony_ci u8 cec_addr[ADV7511_MAX_ADDRS]; 958c2ecf20Sopenharmony_ci u8 cec_valid_addrs; 968c2ecf20Sopenharmony_ci bool cec_enabled_adap; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Is the adv7511 powered on? */ 998c2ecf20Sopenharmony_ci bool power_on; 1008c2ecf20Sopenharmony_ci /* Did we receive hotplug and rx-sense signals? */ 1018c2ecf20Sopenharmony_ci bool have_monitor; 1028c2ecf20Sopenharmony_ci bool enabled_irq; 1038c2ecf20Sopenharmony_ci /* timings from s_dv_timings */ 1048c2ecf20Sopenharmony_ci struct v4l2_dv_timings dv_timings; 1058c2ecf20Sopenharmony_ci u32 fmt_code; 1068c2ecf20Sopenharmony_ci u32 colorspace; 1078c2ecf20Sopenharmony_ci u32 ycbcr_enc; 1088c2ecf20Sopenharmony_ci u32 quantization; 1098c2ecf20Sopenharmony_ci u32 xfer_func; 1108c2ecf20Sopenharmony_ci u32 content_type; 1118c2ecf20Sopenharmony_ci /* controls */ 1128c2ecf20Sopenharmony_ci struct v4l2_ctrl *hdmi_mode_ctrl; 1138c2ecf20Sopenharmony_ci struct v4l2_ctrl *hotplug_ctrl; 1148c2ecf20Sopenharmony_ci struct v4l2_ctrl *rx_sense_ctrl; 1158c2ecf20Sopenharmony_ci struct v4l2_ctrl *have_edid0_ctrl; 1168c2ecf20Sopenharmony_ci struct v4l2_ctrl *rgb_quantization_range_ctrl; 1178c2ecf20Sopenharmony_ci struct v4l2_ctrl *content_type_ctrl; 1188c2ecf20Sopenharmony_ci struct i2c_client *i2c_edid; 1198c2ecf20Sopenharmony_ci struct i2c_client *i2c_pktmem; 1208c2ecf20Sopenharmony_ci struct adv7511_state_edid edid; 1218c2ecf20Sopenharmony_ci /* Running counter of the number of detected EDIDs (for debugging) */ 1228c2ecf20Sopenharmony_ci unsigned edid_detect_counter; 1238c2ecf20Sopenharmony_ci struct workqueue_struct *work_queue; 1248c2ecf20Sopenharmony_ci struct delayed_work edid_handler; /* work entry */ 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void adv7511_check_monitor_present_status(struct v4l2_subdev *sd); 1288c2ecf20Sopenharmony_cistatic bool adv7511_check_edid_status(struct v4l2_subdev *sd); 1298c2ecf20Sopenharmony_cistatic void adv7511_setup(struct v4l2_subdev *sd); 1308c2ecf20Sopenharmony_cistatic int adv7511_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq); 1318c2ecf20Sopenharmony_cistatic int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap adv7511_timings_cap = { 1358c2ecf20Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 1368c2ecf20Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 1378c2ecf20Sopenharmony_ci .reserved = { 0 }, 1388c2ecf20Sopenharmony_ci V4L2_INIT_BT_TIMINGS(640, ADV7511_MAX_WIDTH, 350, ADV7511_MAX_HEIGHT, 1398c2ecf20Sopenharmony_ci ADV7511_MIN_PIXELCLOCK, ADV7511_MAX_PIXELCLOCK, 1408c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 1418c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, 1428c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | 1438c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_CUSTOM) 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline struct adv7511_state *get_adv7511_state(struct v4l2_subdev *sd) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return container_of(sd, struct adv7511_state, sd); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct adv7511_state, hdl)->sd; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* ------------------------ I2C ----------------------------------------------- */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic s32 adv_smbus_read_byte_data_check(struct i2c_client *client, 1598c2ecf20Sopenharmony_ci u8 command, bool check) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci union i2c_smbus_data data; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, 1648c2ecf20Sopenharmony_ci I2C_SMBUS_READ, command, 1658c2ecf20Sopenharmony_ci I2C_SMBUS_BYTE_DATA, &data)) 1668c2ecf20Sopenharmony_ci return data.byte; 1678c2ecf20Sopenharmony_ci if (check) 1688c2ecf20Sopenharmony_ci v4l_err(client, "error reading %02x, %02x\n", 1698c2ecf20Sopenharmony_ci client->addr, command); 1708c2ecf20Sopenharmony_ci return -1; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int i; 1768c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 1778c2ecf20Sopenharmony_ci int ret = adv_smbus_read_byte_data_check(client, command, true); 1788c2ecf20Sopenharmony_ci if (ret >= 0) { 1798c2ecf20Sopenharmony_ci if (i) 1808c2ecf20Sopenharmony_ci v4l_err(client, "read ok after %d retries\n", i); 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci v4l_err(client, "read failed\n"); 1858c2ecf20Sopenharmony_ci return -1; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int adv7511_rd(struct v4l2_subdev *sd, u8 reg) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return adv_smbus_read_byte_data(client, reg); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int adv7511_wr(struct v4l2_subdev *sd, u8 reg, u8 val) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1988c2ecf20Sopenharmony_ci int ret; 1998c2ecf20Sopenharmony_ci int i; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 2028c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, reg, val); 2038c2ecf20Sopenharmony_ci if (ret == 0) 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: i2c write error\n", __func__); 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* To set specific bits in the register, a clear-mask is given (to be AND-ed), 2118c2ecf20Sopenharmony_ci and then the value-mask (to be OR-ed). */ 2128c2ecf20Sopenharmony_cistatic inline void adv7511_wr_and_or(struct v4l2_subdev *sd, u8 reg, u8 clr_mask, u8 val_mask) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci adv7511_wr(sd, reg, (adv7511_rd(sd, reg) & clr_mask) | val_mask); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int adv_smbus_read_i2c_block_data(struct i2c_client *client, 2188c2ecf20Sopenharmony_ci u8 command, unsigned length, u8 *values) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci union i2c_smbus_data data; 2218c2ecf20Sopenharmony_ci int ret; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (length > I2C_SMBUS_BLOCK_MAX) 2248c2ecf20Sopenharmony_ci length = I2C_SMBUS_BLOCK_MAX; 2258c2ecf20Sopenharmony_ci data.block[0] = length; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ret = i2c_smbus_xfer(client->adapter, client->addr, client->flags, 2288c2ecf20Sopenharmony_ci I2C_SMBUS_READ, command, 2298c2ecf20Sopenharmony_ci I2C_SMBUS_I2C_BLOCK_DATA, &data); 2308c2ecf20Sopenharmony_ci memcpy(values, data.block + 1, length); 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 2378c2ecf20Sopenharmony_ci int i; 2388c2ecf20Sopenharmony_ci int err = 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s:\n", __func__); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) 2438c2ecf20Sopenharmony_ci err = adv_smbus_read_i2c_block_data(state->i2c_edid, i, 2448c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX, buf + i); 2458c2ecf20Sopenharmony_ci if (err) 2468c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: i2c read error\n", __func__); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic inline int adv7511_cec_read(struct v4l2_subdev *sd, u8 reg) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return i2c_smbus_read_byte_data(state->i2c_cec, reg); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int adv7511_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 2598c2ecf20Sopenharmony_ci int ret; 2608c2ecf20Sopenharmony_ci int i; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 2638c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val); 2648c2ecf20Sopenharmony_ci if (ret == 0) 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: I2C Write Problem\n", __func__); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline int adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, 2728c2ecf20Sopenharmony_ci u8 val) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return adv_smbus_read_byte_data(state->i2c_pktmem, reg); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int adv7511_pktmem_wr(struct v4l2_subdev *sd, u8 reg, u8 val) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 2878c2ecf20Sopenharmony_ci int ret; 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 2918c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(state->i2c_pktmem, reg, val); 2928c2ecf20Sopenharmony_ci if (ret == 0) 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: i2c write error\n", __func__); 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* To set specific bits in the register, a clear-mask is given (to be AND-ed), 3008c2ecf20Sopenharmony_ci and then the value-mask (to be OR-ed). */ 3018c2ecf20Sopenharmony_cistatic inline void adv7511_pktmem_wr_and_or(struct v4l2_subdev *sd, u8 reg, u8 clr_mask, u8 val_mask) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci adv7511_pktmem_wr(sd, reg, (adv7511_pktmem_rd(sd, reg) & clr_mask) | val_mask); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic inline bool adv7511_have_hotplug(struct v4l2_subdev *sd) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci return adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic inline bool adv7511_have_rx_sense(struct v4l2_subdev *sd) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci return adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void adv7511_csc_conversion_mode(struct v4l2_subdev *sd, u8 mode) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void adv7511_csc_coeff(struct v4l2_subdev *sd, 3228c2ecf20Sopenharmony_ci u16 A1, u16 A2, u16 A3, u16 A4, 3238c2ecf20Sopenharmony_ci u16 B1, u16 B2, u16 B3, u16 B4, 3248c2ecf20Sopenharmony_ci u16 C1, u16 C2, u16 C3, u16 C4) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci /* A */ 3278c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x18, 0xe0, A1>>8); 3288c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x19, A1); 3298c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x1A, 0xe0, A2>>8); 3308c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x1B, A2); 3318c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x1c, 0xe0, A3>>8); 3328c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x1d, A3); 3338c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x1e, 0xe0, A4>>8); 3348c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x1f, A4); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* B */ 3378c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x20, 0xe0, B1>>8); 3388c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x21, B1); 3398c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x22, 0xe0, B2>>8); 3408c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x23, B2); 3418c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x24, 0xe0, B3>>8); 3428c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x25, B3); 3438c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x26, 0xe0, B4>>8); 3448c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x27, B4); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* C */ 3478c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x28, 0xe0, C1>>8); 3488c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x29, C1); 3498c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x2A, 0xe0, C2>>8); 3508c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x2B, C2); 3518c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x2C, 0xe0, C3>>8); 3528c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x2D, C3); 3538c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x2E, 0xe0, C4>>8); 3548c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x2F, C4); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci if (enable) { 3608c2ecf20Sopenharmony_ci u8 csc_mode = 0; 3618c2ecf20Sopenharmony_ci adv7511_csc_conversion_mode(sd, csc_mode); 3628c2ecf20Sopenharmony_ci adv7511_csc_coeff(sd, 3638c2ecf20Sopenharmony_ci 4096-564, 0, 0, 256, 3648c2ecf20Sopenharmony_ci 0, 4096-564, 0, 256, 3658c2ecf20Sopenharmony_ci 0, 0, 4096-564, 256); 3668c2ecf20Sopenharmony_ci /* enable CSC */ 3678c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x18, 0x7f, 0x80); 3688c2ecf20Sopenharmony_ci /* AVI infoframe: Limited range RGB (16-235) */ 3698c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x57, 0xf3, 0x04); 3708c2ecf20Sopenharmony_ci } else { 3718c2ecf20Sopenharmony_ci /* disable CSC */ 3728c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0); 3738c2ecf20Sopenharmony_ci /* AVI infoframe: Full range RGB (0-255) */ 3748c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x57, 0xf3, 0x08); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic void adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Only makes sense for RGB formats */ 3838c2ecf20Sopenharmony_ci if (state->fmt_code != MEDIA_BUS_FMT_RGB888_1X24) { 3848c2ecf20Sopenharmony_ci /* so just keep quantization */ 3858c2ecf20Sopenharmony_ci adv7511_csc_rgb_full2limit(sd, false); 3868c2ecf20Sopenharmony_ci return; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci switch (ctrl->val) { 3908c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_AUTO: 3918c2ecf20Sopenharmony_ci /* automatic */ 3928c2ecf20Sopenharmony_ci if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { 3938c2ecf20Sopenharmony_ci /* CE format, RGB limited range (16-235) */ 3948c2ecf20Sopenharmony_ci adv7511_csc_rgb_full2limit(sd, true); 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci /* not CE format, RGB full range (0-255) */ 3978c2ecf20Sopenharmony_ci adv7511_csc_rgb_full2limit(sd, false); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_LIMITED: 4018c2ecf20Sopenharmony_ci /* RGB limited range (16-235) */ 4028c2ecf20Sopenharmony_ci adv7511_csc_rgb_full2limit(sd, true); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_FULL: 4058c2ecf20Sopenharmony_ci /* RGB full range (0-255) */ 4068c2ecf20Sopenharmony_ci adv7511_csc_rgb_full2limit(sd, false); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* ------------------------------ CTRL OPS ------------------------------ */ 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int adv7511_s_ctrl(struct v4l2_ctrl *ctrl) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 4168c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (state->hdmi_mode_ctrl == ctrl) { 4218c2ecf20Sopenharmony_ci /* Set HDMI or DVI-D */ 4228c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xaf, 0xfd, ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci if (state->rgb_quantization_range_ctrl == ctrl) { 4268c2ecf20Sopenharmony_ci adv7511_set_rgb_quantization_mode(sd, ctrl); 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci if (state->content_type_ctrl == ctrl) { 4308c2ecf20Sopenharmony_ci u8 itc, cn; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci state->content_type = ctrl->val; 4338c2ecf20Sopenharmony_ci itc = state->content_type != V4L2_DV_IT_CONTENT_TYPE_NO_ITC; 4348c2ecf20Sopenharmony_ci cn = itc ? state->content_type : V4L2_DV_IT_CONTENT_TYPE_GRAPHICS; 4358c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x57, 0x7f, itc << 7); 4368c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x59, 0xcf, cn << 4); 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return -EINVAL; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops adv7511_ctrl_ops = { 4448c2ecf20Sopenharmony_ci .s_ctrl = adv7511_s_ctrl, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* ---------------------------- CORE OPS ------------------------------------------- */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 4508c2ecf20Sopenharmony_cistatic void adv7511_inv_register(struct v4l2_subdev *sd) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci v4l2_info(sd, "0x000-0x0ff: Main Map\n"); 4558c2ecf20Sopenharmony_ci if (state->i2c_cec) 4568c2ecf20Sopenharmony_ci v4l2_info(sd, "0x100-0x1ff: CEC Map\n"); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci reg->size = 1; 4648c2ecf20Sopenharmony_ci switch (reg->reg >> 8) { 4658c2ecf20Sopenharmony_ci case 0: 4668c2ecf20Sopenharmony_ci reg->val = adv7511_rd(sd, reg->reg & 0xff); 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case 1: 4698c2ecf20Sopenharmony_ci if (state->i2c_cec) { 4708c2ecf20Sopenharmony_ci reg->val = adv7511_cec_read(sd, reg->reg & 0xff); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci fallthrough; 4748c2ecf20Sopenharmony_ci default: 4758c2ecf20Sopenharmony_ci v4l2_info(sd, "Register %03llx not supported\n", reg->reg); 4768c2ecf20Sopenharmony_ci adv7511_inv_register(sd); 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci switch (reg->reg >> 8) { 4878c2ecf20Sopenharmony_ci case 0: 4888c2ecf20Sopenharmony_ci adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci case 1: 4918c2ecf20Sopenharmony_ci if (state->i2c_cec) { 4928c2ecf20Sopenharmony_ci adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff); 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci fallthrough; 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci v4l2_info(sd, "Register %03llx not supported\n", reg->reg); 4988c2ecf20Sopenharmony_ci adv7511_inv_register(sd); 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci#endif 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistruct adv7511_cfg_read_infoframe { 5068c2ecf20Sopenharmony_ci const char *desc; 5078c2ecf20Sopenharmony_ci u8 present_reg; 5088c2ecf20Sopenharmony_ci u8 present_mask; 5098c2ecf20Sopenharmony_ci u8 header[3]; 5108c2ecf20Sopenharmony_ci u16 payload_addr; 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci u8 csum = 0; 5168c2ecf20Sopenharmony_ci size_t i; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* compute checksum */ 5198c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 5208c2ecf20Sopenharmony_ci csum += ptr[i]; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return 256 - csum; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_infoframe *cri) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5288c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 5298c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 5308c2ecf20Sopenharmony_ci u8 buffer[32]; 5318c2ecf20Sopenharmony_ci u8 len; 5328c2ecf20Sopenharmony_ci int i; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (!(adv7511_rd(sd, cri->present_reg) & cri->present_mask)) { 5358c2ecf20Sopenharmony_ci v4l2_info(sd, "%s infoframe not transmitted\n", cri->desc); 5368c2ecf20Sopenharmony_ci return; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci memcpy(buffer, cri->header, sizeof(cri->header)); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci len = buffer[2]; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (len + 4 > sizeof(buffer)) { 5448c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, cri->desc, len); 5458c2ecf20Sopenharmony_ci return; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (cri->payload_addr >= 0x100) { 5498c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 5508c2ecf20Sopenharmony_ci buffer[i + 4] = adv7511_pktmem_rd(sd, cri->payload_addr + i - 0x100); 5518c2ecf20Sopenharmony_ci } else { 5528c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 5538c2ecf20Sopenharmony_ci buffer[i + 4] = adv7511_rd(sd, cri->payload_addr + i); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci buffer[3] = 0; 5568c2ecf20Sopenharmony_ci buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (hdmi_infoframe_unpack(&frame, buffer, len + 4) < 0) { 5598c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci hdmi_infoframe_log(KERN_INFO, dev, &frame); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic void adv7511_log_infoframes(struct v4l2_subdev *sd) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci static const struct adv7511_cfg_read_infoframe cri[] = { 5698c2ecf20Sopenharmony_ci { "AVI", 0x44, 0x10, { 0x82, 2, 13 }, 0x55 }, 5708c2ecf20Sopenharmony_ci { "Audio", 0x44, 0x08, { 0x84, 1, 10 }, 0x73 }, 5718c2ecf20Sopenharmony_ci { "SDP", 0x40, 0x40, { 0x83, 1, 25 }, 0x103 }, 5728c2ecf20Sopenharmony_ci }; 5738c2ecf20Sopenharmony_ci int i; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cri); i++) 5768c2ecf20Sopenharmony_ci log_infoframe(sd, &cri[i]); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int adv7511_log_status(struct v4l2_subdev *sd) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 5828c2ecf20Sopenharmony_ci struct adv7511_state_edid *edid = &state->edid; 5838c2ecf20Sopenharmony_ci int i; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci static const char * const states[] = { 5868c2ecf20Sopenharmony_ci "in reset", 5878c2ecf20Sopenharmony_ci "reading EDID", 5888c2ecf20Sopenharmony_ci "idle", 5898c2ecf20Sopenharmony_ci "initializing HDCP", 5908c2ecf20Sopenharmony_ci "HDCP enabled", 5918c2ecf20Sopenharmony_ci "initializing HDCP repeater", 5928c2ecf20Sopenharmony_ci "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" 5938c2ecf20Sopenharmony_ci }; 5948c2ecf20Sopenharmony_ci static const char * const errors[] = { 5958c2ecf20Sopenharmony_ci "no error", 5968c2ecf20Sopenharmony_ci "bad receiver BKSV", 5978c2ecf20Sopenharmony_ci "Ri mismatch", 5988c2ecf20Sopenharmony_ci "Pj mismatch", 5998c2ecf20Sopenharmony_ci "i2c error", 6008c2ecf20Sopenharmony_ci "timed out", 6018c2ecf20Sopenharmony_ci "max repeater cascade exceeded", 6028c2ecf20Sopenharmony_ci "hash check failed", 6038c2ecf20Sopenharmony_ci "too many devices", 6048c2ecf20Sopenharmony_ci "9", "A", "B", "C", "D", "E", "F" 6058c2ecf20Sopenharmony_ci }; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); 6088c2ecf20Sopenharmony_ci v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", 6098c2ecf20Sopenharmony_ci (adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT) ? "detected" : "no", 6108c2ecf20Sopenharmony_ci (adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT) ? "detected" : "no", 6118c2ecf20Sopenharmony_ci edid->segments ? "found" : "no", 6128c2ecf20Sopenharmony_ci edid->blocks); 6138c2ecf20Sopenharmony_ci v4l2_info(sd, "%s output %s\n", 6148c2ecf20Sopenharmony_ci (adv7511_rd(sd, 0xaf) & 0x02) ? 6158c2ecf20Sopenharmony_ci "HDMI" : "DVI-D", 6168c2ecf20Sopenharmony_ci (adv7511_rd(sd, 0xa1) & 0x3c) ? 6178c2ecf20Sopenharmony_ci "disabled" : "enabled"); 6188c2ecf20Sopenharmony_ci v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", 6198c2ecf20Sopenharmony_ci states[adv7511_rd(sd, 0xc8) & 0xf], 6208c2ecf20Sopenharmony_ci errors[adv7511_rd(sd, 0xc8) >> 4], state->edid_detect_counter, 6218c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x94), adv7511_rd(sd, 0x96)); 6228c2ecf20Sopenharmony_ci v4l2_info(sd, "RGB quantization: %s range\n", adv7511_rd(sd, 0x18) & 0x80 ? "limited" : "full"); 6238c2ecf20Sopenharmony_ci if (adv7511_rd(sd, 0xaf) & 0x02) { 6248c2ecf20Sopenharmony_ci /* HDMI only */ 6258c2ecf20Sopenharmony_ci u8 manual_cts = adv7511_rd(sd, 0x0a) & 0x80; 6268c2ecf20Sopenharmony_ci u32 N = (adv7511_rd(sd, 0x01) & 0xf) << 16 | 6278c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x02) << 8 | 6288c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x03); 6298c2ecf20Sopenharmony_ci u8 vic_detect = adv7511_rd(sd, 0x3e) >> 2; 6308c2ecf20Sopenharmony_ci u8 vic_sent = adv7511_rd(sd, 0x3d) & 0x3f; 6318c2ecf20Sopenharmony_ci u32 CTS; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (manual_cts) 6348c2ecf20Sopenharmony_ci CTS = (adv7511_rd(sd, 0x07) & 0xf) << 16 | 6358c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x08) << 8 | 6368c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x09); 6378c2ecf20Sopenharmony_ci else 6388c2ecf20Sopenharmony_ci CTS = (adv7511_rd(sd, 0x04) & 0xf) << 16 | 6398c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x05) << 8 | 6408c2ecf20Sopenharmony_ci adv7511_rd(sd, 0x06); 6418c2ecf20Sopenharmony_ci v4l2_info(sd, "CTS %s mode: N %d, CTS %d\n", 6428c2ecf20Sopenharmony_ci manual_cts ? "manual" : "automatic", N, CTS); 6438c2ecf20Sopenharmony_ci v4l2_info(sd, "VIC: detected %d, sent %d\n", 6448c2ecf20Sopenharmony_ci vic_detect, vic_sent); 6458c2ecf20Sopenharmony_ci adv7511_log_infoframes(sd); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci if (state->dv_timings.type == V4L2_DV_BT_656_1120) 6488c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "timings: ", 6498c2ecf20Sopenharmony_ci &state->dv_timings, false); 6508c2ecf20Sopenharmony_ci else 6518c2ecf20Sopenharmony_ci v4l2_info(sd, "no timings set\n"); 6528c2ecf20Sopenharmony_ci v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (state->i2c_cec == NULL) 6558c2ecf20Sopenharmony_ci return 0; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? 6608c2ecf20Sopenharmony_ci "enabled" : "disabled"); 6618c2ecf20Sopenharmony_ci if (state->cec_enabled_adap) { 6628c2ecf20Sopenharmony_ci for (i = 0; i < ADV7511_MAX_ADDRS; i++) { 6638c2ecf20Sopenharmony_ci bool is_valid = state->cec_valid_addrs & (1 << i); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (is_valid) 6668c2ecf20Sopenharmony_ci v4l2_info(sd, "CEC Logical Address: 0x%x\n", 6678c2ecf20Sopenharmony_ci state->cec_addr[i]); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr); 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* Power up/down adv7511 */ 6758c2ecf20Sopenharmony_cistatic int adv7511_s_power(struct v4l2_subdev *sd, int on) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 6788c2ecf20Sopenharmony_ci const int retries = 20; 6798c2ecf20Sopenharmony_ci int i; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off"); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci state->power_on = on; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (!on) { 6868c2ecf20Sopenharmony_ci /* Power down */ 6878c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x41, 0xbf, 0x40); 6888c2ecf20Sopenharmony_ci return true; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* Power up */ 6928c2ecf20Sopenharmony_ci /* The adv7511 does not always come up immediately. 6938c2ecf20Sopenharmony_ci Retry multiple times. */ 6948c2ecf20Sopenharmony_ci for (i = 0; i < retries; i++) { 6958c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x41, 0xbf, 0x0); 6968c2ecf20Sopenharmony_ci if ((adv7511_rd(sd, 0x41) & 0x40) == 0) 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x41, 0xbf, 0x40); 6998c2ecf20Sopenharmony_ci msleep(10); 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci if (i == retries) { 7028c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: failed to powerup the adv7511!\n", __func__); 7038c2ecf20Sopenharmony_ci adv7511_s_power(sd, 0); 7048c2ecf20Sopenharmony_ci return false; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci if (i > 1) 7078c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: needed %d retries to powerup the adv7511\n", __func__, i); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* Reserved registers that must be set */ 7108c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x98, 0x03); 7118c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x9a, 0xfe, 0x70); 7128c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x9c, 0x30); 7138c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x9d, 0xfc, 0x01); 7148c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xa2, 0xa4); 7158c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xa3, 0xa4); 7168c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xe0, 0xd0); 7178c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xf9, 0x00); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x43, state->i2c_edid_addr); 7208c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x45, state->i2c_pktmem_addr); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Set number of attempts to read the EDID */ 7238c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xc9, 0xf); 7248c2ecf20Sopenharmony_ci return true; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) 7288c2ecf20Sopenharmony_cistatic int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct adv7511_state *state = cec_get_drvdata(adap); 7318c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (state->i2c_cec == NULL) 7348c2ecf20Sopenharmony_ci return -EIO; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (!state->cec_enabled_adap && enable) { 7378c2ecf20Sopenharmony_ci /* power up cec section */ 7388c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01); 7398c2ecf20Sopenharmony_ci /* legacy mode and clear all rx buffers */ 7408c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4a, 0x00); 7418c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4a, 0x07); 7428c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ 7438c2ecf20Sopenharmony_ci /* enabled irqs: */ 7448c2ecf20Sopenharmony_ci /* tx: ready */ 7458c2ecf20Sopenharmony_ci /* tx: arbitration lost */ 7468c2ecf20Sopenharmony_ci /* tx: retry timeout */ 7478c2ecf20Sopenharmony_ci /* rx: ready 1 */ 7488c2ecf20Sopenharmony_ci if (state->enabled_irq) 7498c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39); 7508c2ecf20Sopenharmony_ci } else if (state->cec_enabled_adap && !enable) { 7518c2ecf20Sopenharmony_ci if (state->enabled_irq) 7528c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00); 7538c2ecf20Sopenharmony_ci /* disable address mask 1-3 */ 7548c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00); 7558c2ecf20Sopenharmony_ci /* power down cec section */ 7568c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00); 7578c2ecf20Sopenharmony_ci state->cec_valid_addrs = 0; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci state->cec_enabled_adap = enable; 7608c2ecf20Sopenharmony_ci return 0; 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct adv7511_state *state = cec_get_drvdata(adap); 7668c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 7678c2ecf20Sopenharmony_ci unsigned int i, free_idx = ADV7511_MAX_ADDRS; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!state->cec_enabled_adap) 7708c2ecf20Sopenharmony_ci return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (addr == CEC_LOG_ADDR_INVALID) { 7738c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0); 7748c2ecf20Sopenharmony_ci state->cec_valid_addrs = 0; 7758c2ecf20Sopenharmony_ci return 0; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci for (i = 0; i < ADV7511_MAX_ADDRS; i++) { 7798c2ecf20Sopenharmony_ci bool is_valid = state->cec_valid_addrs & (1 << i); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (free_idx == ADV7511_MAX_ADDRS && !is_valid) 7828c2ecf20Sopenharmony_ci free_idx = i; 7838c2ecf20Sopenharmony_ci if (is_valid && state->cec_addr[i] == addr) 7848c2ecf20Sopenharmony_ci return 0; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci if (i == ADV7511_MAX_ADDRS) { 7878c2ecf20Sopenharmony_ci i = free_idx; 7888c2ecf20Sopenharmony_ci if (i == ADV7511_MAX_ADDRS) 7898c2ecf20Sopenharmony_ci return -ENXIO; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci state->cec_addr[i] = addr; 7928c2ecf20Sopenharmony_ci state->cec_valid_addrs |= 1 << i; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci switch (i) { 7958c2ecf20Sopenharmony_ci case 0: 7968c2ecf20Sopenharmony_ci /* enable address mask 0 */ 7978c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10); 7988c2ecf20Sopenharmony_ci /* set address for mask 0 */ 7998c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr); 8008c2ecf20Sopenharmony_ci break; 8018c2ecf20Sopenharmony_ci case 1: 8028c2ecf20Sopenharmony_ci /* enable address mask 1 */ 8038c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20); 8048c2ecf20Sopenharmony_ci /* set address for mask 1 */ 8058c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4); 8068c2ecf20Sopenharmony_ci break; 8078c2ecf20Sopenharmony_ci case 2: 8088c2ecf20Sopenharmony_ci /* enable address mask 2 */ 8098c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40); 8108c2ecf20Sopenharmony_ci /* set address for mask 1 */ 8118c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr); 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 8188c2ecf20Sopenharmony_ci u32 signal_free_time, struct cec_msg *msg) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct adv7511_state *state = cec_get_drvdata(adap); 8218c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 8228c2ecf20Sopenharmony_ci u8 len = msg->len; 8238c2ecf20Sopenharmony_ci unsigned int i; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (len > 16) { 8288c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); 8298c2ecf20Sopenharmony_ci return -EINVAL; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* 8338c2ecf20Sopenharmony_ci * The number of retries is the number of attempts - 1, but retry 8348c2ecf20Sopenharmony_ci * at least once. It's not clear if a value of 0 is allowed, so 8358c2ecf20Sopenharmony_ci * let's do at least one retry. 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* clear cec tx irq status */ 8408c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x97, 0x38); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* write data */ 8438c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 8448c2ecf20Sopenharmony_ci adv7511_cec_write(sd, i, msg->msg[i]); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* set length (data + header) */ 8478c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x10, len); 8488c2ecf20Sopenharmony_ci /* start transmit, enable tx */ 8498c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x11, 0x01); 8508c2ecf20Sopenharmony_ci return 0; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if ((adv7511_cec_read(sd, 0x11) & 0x01) == 0) { 8588c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); 8598c2ecf20Sopenharmony_ci return; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (tx_raw_status & 0x10) { 8638c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 8648c2ecf20Sopenharmony_ci "%s: tx raw: arbitration lost\n", __func__); 8658c2ecf20Sopenharmony_ci cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, 8668c2ecf20Sopenharmony_ci 1, 0, 0, 0); 8678c2ecf20Sopenharmony_ci return; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci if (tx_raw_status & 0x08) { 8708c2ecf20Sopenharmony_ci u8 status; 8718c2ecf20Sopenharmony_ci u8 nack_cnt; 8728c2ecf20Sopenharmony_ci u8 low_drive_cnt; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); 8758c2ecf20Sopenharmony_ci /* 8768c2ecf20Sopenharmony_ci * We set this status bit since this hardware performs 8778c2ecf20Sopenharmony_ci * retransmissions. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_ci status = CEC_TX_STATUS_MAX_RETRIES; 8808c2ecf20Sopenharmony_ci nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf; 8818c2ecf20Sopenharmony_ci if (nack_cnt) 8828c2ecf20Sopenharmony_ci status |= CEC_TX_STATUS_NACK; 8838c2ecf20Sopenharmony_ci low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4; 8848c2ecf20Sopenharmony_ci if (low_drive_cnt) 8858c2ecf20Sopenharmony_ci status |= CEC_TX_STATUS_LOW_DRIVE; 8868c2ecf20Sopenharmony_ci cec_transmit_done(state->cec_adap, status, 8878c2ecf20Sopenharmony_ci 0, nack_cnt, low_drive_cnt, 0); 8888c2ecf20Sopenharmony_ci return; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci if (tx_raw_status & 0x20) { 8918c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); 8928c2ecf20Sopenharmony_ci cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); 8938c2ecf20Sopenharmony_ci return; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic const struct cec_adap_ops adv7511_cec_adap_ops = { 8988c2ecf20Sopenharmony_ci .adap_enable = adv7511_cec_adap_enable, 8998c2ecf20Sopenharmony_ci .adap_log_addr = adv7511_cec_adap_log_addr, 9008c2ecf20Sopenharmony_ci .adap_transmit = adv7511_cec_adap_transmit, 9018c2ecf20Sopenharmony_ci}; 9028c2ecf20Sopenharmony_ci#endif 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci/* Enable interrupts */ 9058c2ecf20Sopenharmony_cistatic void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 9088c2ecf20Sopenharmony_ci u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; 9098c2ecf20Sopenharmony_ci u8 irqs_rd; 9108c2ecf20Sopenharmony_ci int retries = 100; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (state->enabled_irq == enable) 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci state->enabled_irq = enable; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* The datasheet says that the EDID ready interrupt should be 9198c2ecf20Sopenharmony_ci disabled if there is no hotplug. */ 9208c2ecf20Sopenharmony_ci if (!enable) 9218c2ecf20Sopenharmony_ci irqs = 0; 9228c2ecf20Sopenharmony_ci else if (adv7511_have_hotplug(sd)) 9238c2ecf20Sopenharmony_ci irqs |= MASK_ADV7511_EDID_RDY_INT; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* 9268c2ecf20Sopenharmony_ci * This i2c write can fail (approx. 1 in 1000 writes). But it 9278c2ecf20Sopenharmony_ci * is essential that this register is correct, so retry it 9288c2ecf20Sopenharmony_ci * multiple times. 9298c2ecf20Sopenharmony_ci * 9308c2ecf20Sopenharmony_ci * Note that the i2c write does not report an error, but the readback 9318c2ecf20Sopenharmony_ci * clearly shows the wrong value. 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_ci do { 9348c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x94, irqs); 9358c2ecf20Sopenharmony_ci irqs_rd = adv7511_rd(sd, 0x94); 9368c2ecf20Sopenharmony_ci } while (retries-- && irqs_rd != irqs); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (irqs_rd != irqs) 9398c2ecf20Sopenharmony_ci v4l2_err(sd, "Could not set interrupts: hw failure?\n"); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x95, 0xc0, 9428c2ecf20Sopenharmony_ci (state->cec_enabled_adap && enable) ? 0x39 : 0x00); 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci/* Interrupt handler */ 9468c2ecf20Sopenharmony_cistatic int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci u8 irq_status; 9498c2ecf20Sopenharmony_ci u8 cec_irq; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* disable interrupts to prevent a race condition */ 9528c2ecf20Sopenharmony_ci adv7511_set_isr(sd, false); 9538c2ecf20Sopenharmony_ci irq_status = adv7511_rd(sd, 0x96); 9548c2ecf20Sopenharmony_ci cec_irq = adv7511_rd(sd, 0x97); 9558c2ecf20Sopenharmony_ci /* clear detected interrupts */ 9568c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x96, irq_status); 9578c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x97, cec_irq); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__, 9608c2ecf20Sopenharmony_ci irq_status, cec_irq); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) 9638c2ecf20Sopenharmony_ci adv7511_check_monitor_present_status(sd); 9648c2ecf20Sopenharmony_ci if (irq_status & MASK_ADV7511_EDID_RDY_INT) 9658c2ecf20Sopenharmony_ci adv7511_check_edid_status(sd); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) 9688c2ecf20Sopenharmony_ci if (cec_irq & 0x38) 9698c2ecf20Sopenharmony_ci adv_cec_tx_raw_status(sd, cec_irq); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (cec_irq & 1) { 9728c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 9738c2ecf20Sopenharmony_ci struct cec_msg msg; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci msg.len = adv7511_cec_read(sd, 0x25) & 0x1f; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__, 9788c2ecf20Sopenharmony_ci msg.len); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (msg.len > 16) 9818c2ecf20Sopenharmony_ci msg.len = 16; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (msg.len) { 9848c2ecf20Sopenharmony_ci u8 i; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci for (i = 0; i < msg.len; i++) 9878c2ecf20Sopenharmony_ci msg.msg[i] = adv7511_cec_read(sd, i + 0x15); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4a, 0); /* toggle to re-enable rx 1 */ 9908c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4a, 1); 9918c2ecf20Sopenharmony_ci cec_received_msg(state->cec_adap, &msg); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci#endif 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* enable interrupts */ 9978c2ecf20Sopenharmony_ci adv7511_set_isr(sd, true); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (handled) 10008c2ecf20Sopenharmony_ci *handled = true; 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops adv7511_core_ops = { 10058c2ecf20Sopenharmony_ci .log_status = adv7511_log_status, 10068c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 10078c2ecf20Sopenharmony_ci .g_register = adv7511_g_register, 10088c2ecf20Sopenharmony_ci .s_register = adv7511_s_register, 10098c2ecf20Sopenharmony_ci#endif 10108c2ecf20Sopenharmony_ci .s_power = adv7511_s_power, 10118c2ecf20Sopenharmony_ci .interrupt_service_routine = adv7511_isr, 10128c2ecf20Sopenharmony_ci}; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/* ------------------------------ VIDEO OPS ------------------------------ */ 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/* Enable/disable adv7511 output */ 10178c2ecf20Sopenharmony_cistatic int adv7511_s_stream(struct v4l2_subdev *sd, int enable) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); 10228c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); 10238c2ecf20Sopenharmony_ci if (enable) { 10248c2ecf20Sopenharmony_ci adv7511_check_monitor_present_status(sd); 10258c2ecf20Sopenharmony_ci } else { 10268c2ecf20Sopenharmony_ci adv7511_s_power(sd, 0); 10278c2ecf20Sopenharmony_ci state->have_monitor = false; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int adv7511_s_dv_timings(struct v4l2_subdev *sd, 10338c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 10368c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 10378c2ecf20Sopenharmony_ci u32 fps; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s:\n", __func__); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* quick sanity check */ 10428c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL)) 10438c2ecf20Sopenharmony_ci return -EINVAL; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings 10468c2ecf20Sopenharmony_ci if the format is one of the CEA or DMT timings. */ 10478c2ecf20Sopenharmony_ci v4l2_find_dv_timings_cap(timings, &adv7511_timings_cap, 0, NULL, NULL); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* save timings */ 10508c2ecf20Sopenharmony_ci state->dv_timings = *timings; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* set h/vsync polarities */ 10538c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x17, 0x9f, 10548c2ecf20Sopenharmony_ci ((bt->polarities & V4L2_DV_VSYNC_POS_POL) ? 0 : 0x40) | 10558c2ecf20Sopenharmony_ci ((bt->polarities & V4L2_DV_HSYNC_POS_POL) ? 0 : 0x20)); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci fps = (u32)bt->pixelclock / (V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt)); 10588c2ecf20Sopenharmony_ci switch (fps) { 10598c2ecf20Sopenharmony_ci case 24: 10608c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xfb, 0xf9, 1 << 1); 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci case 25: 10638c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xfb, 0xf9, 2 << 1); 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci case 30: 10668c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xfb, 0xf9, 3 << 1); 10678c2ecf20Sopenharmony_ci break; 10688c2ecf20Sopenharmony_ci default: 10698c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xfb, 0xf9, 0); 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* update quantization range based on new dv_timings */ 10748c2ecf20Sopenharmony_ci adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci return 0; 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic int adv7511_g_dv_timings(struct v4l2_subdev *sd, 10808c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s:\n", __func__); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (!timings) 10878c2ecf20Sopenharmony_ci return -EINVAL; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci *timings = state->dv_timings; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic int adv7511_enum_dv_timings(struct v4l2_subdev *sd, 10958c2ecf20Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci if (timings->pad != 0) 10988c2ecf20Sopenharmony_ci return -EINVAL; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL); 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic int adv7511_dv_timings_cap(struct v4l2_subdev *sd, 11048c2ecf20Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci if (cap->pad != 0) 11078c2ecf20Sopenharmony_ci return -EINVAL; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci *cap = adv7511_timings_cap; 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops adv7511_video_ops = { 11148c2ecf20Sopenharmony_ci .s_stream = adv7511_s_stream, 11158c2ecf20Sopenharmony_ci .s_dv_timings = adv7511_s_dv_timings, 11168c2ecf20Sopenharmony_ci .g_dv_timings = adv7511_g_dv_timings, 11178c2ecf20Sopenharmony_ci}; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/* ------------------------------ AUDIO OPS ------------------------------ */ 11208c2ecf20Sopenharmony_cistatic int adv7511_s_audio_stream(struct v4l2_subdev *sd, int enable) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (enable) 11258c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x4b, 0x3f, 0x80); 11268c2ecf20Sopenharmony_ci else 11278c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x4b, 0x3f, 0x40); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return 0; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci u32 N; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci switch (freq) { 11378c2ecf20Sopenharmony_ci case 32000: N = 4096; break; 11388c2ecf20Sopenharmony_ci case 44100: N = 6272; break; 11398c2ecf20Sopenharmony_ci case 48000: N = 6144; break; 11408c2ecf20Sopenharmony_ci case 88200: N = 12544; break; 11418c2ecf20Sopenharmony_ci case 96000: N = 12288; break; 11428c2ecf20Sopenharmony_ci case 176400: N = 25088; break; 11438c2ecf20Sopenharmony_ci case 192000: N = 24576; break; 11448c2ecf20Sopenharmony_ci default: 11458c2ecf20Sopenharmony_ci return -EINVAL; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* Set N (used with CTS to regenerate the audio clock) */ 11498c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x01, (N >> 16) & 0xf); 11508c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x02, (N >> 8) & 0xff); 11518c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x03, N & 0xff); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci return 0; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int adv7511_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci u32 i2s_sf; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci switch (freq) { 11618c2ecf20Sopenharmony_ci case 32000: i2s_sf = 0x30; break; 11628c2ecf20Sopenharmony_ci case 44100: i2s_sf = 0x00; break; 11638c2ecf20Sopenharmony_ci case 48000: i2s_sf = 0x20; break; 11648c2ecf20Sopenharmony_ci case 88200: i2s_sf = 0x80; break; 11658c2ecf20Sopenharmony_ci case 96000: i2s_sf = 0xa0; break; 11668c2ecf20Sopenharmony_ci case 176400: i2s_sf = 0xc0; break; 11678c2ecf20Sopenharmony_ci case 192000: i2s_sf = 0xe0; break; 11688c2ecf20Sopenharmony_ci default: 11698c2ecf20Sopenharmony_ci return -EINVAL; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* Set sampling frequency for I2S audio to 48 kHz */ 11738c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x15, 0xf, i2s_sf); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci return 0; 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic int adv7511_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci /* Only 2 channels in use for application */ 11818c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x73, 0xf8, 0x1); 11828c2ecf20Sopenharmony_ci /* Speaker mapping */ 11838c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x76, 0x00); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* 16 bit audio word length */ 11868c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x14, 0xf0, 0x02); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return 0; 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_audio_ops adv7511_audio_ops = { 11928c2ecf20Sopenharmony_ci .s_stream = adv7511_s_audio_stream, 11938c2ecf20Sopenharmony_ci .s_clock_freq = adv7511_s_clock_freq, 11948c2ecf20Sopenharmony_ci .s_i2s_clock_freq = adv7511_s_i2s_clock_freq, 11958c2ecf20Sopenharmony_ci .s_routing = adv7511_s_routing, 11968c2ecf20Sopenharmony_ci}; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci/* ---------------------------- PAD OPS ------------------------------------- */ 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (edid->pad != 0) 12078c2ecf20Sopenharmony_ci return -EINVAL; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (edid->start_block == 0 && edid->blocks == 0) { 12108c2ecf20Sopenharmony_ci edid->blocks = state->edid.segments * 2; 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (state->edid.segments == 0) 12158c2ecf20Sopenharmony_ci return -ENODATA; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (edid->start_block >= state->edid.segments * 2) 12188c2ecf20Sopenharmony_ci return -EINVAL; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (edid->start_block + edid->blocks > state->edid.segments * 2) 12218c2ecf20Sopenharmony_ci edid->blocks = state->edid.segments * 2 - edid->start_block; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci memcpy(edid->edid, &state->edid.data[edid->start_block * 128], 12248c2ecf20Sopenharmony_ci 128 * edid->blocks); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci return 0; 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cistatic int adv7511_enum_mbus_code(struct v4l2_subdev *sd, 12308c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12318c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci if (code->pad != 0) 12348c2ecf20Sopenharmony_ci return -EINVAL; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci switch (code->index) { 12378c2ecf20Sopenharmony_ci case 0: 12388c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_RGB888_1X24; 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci case 1: 12418c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_YUYV8_1X16; 12428c2ecf20Sopenharmony_ci break; 12438c2ecf20Sopenharmony_ci case 2: 12448c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_UYVY8_1X16; 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci default: 12478c2ecf20Sopenharmony_ci return -EINVAL; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci return 0; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic void adv7511_fill_format(struct adv7511_state *state, 12538c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci format->width = state->dv_timings.bt.width; 12568c2ecf20Sopenharmony_ci format->height = state->dv_timings.bt.height; 12578c2ecf20Sopenharmony_ci format->field = V4L2_FIELD_NONE; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic int adv7511_get_fmt(struct v4l2_subdev *sd, 12618c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12628c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci if (format->pad != 0) 12678c2ecf20Sopenharmony_ci return -EINVAL; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci memset(&format->format, 0, sizeof(format->format)); 12708c2ecf20Sopenharmony_ci adv7511_fill_format(state, &format->format); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 12738c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 12768c2ecf20Sopenharmony_ci format->format.code = fmt->code; 12778c2ecf20Sopenharmony_ci format->format.colorspace = fmt->colorspace; 12788c2ecf20Sopenharmony_ci format->format.ycbcr_enc = fmt->ycbcr_enc; 12798c2ecf20Sopenharmony_ci format->format.quantization = fmt->quantization; 12808c2ecf20Sopenharmony_ci format->format.xfer_func = fmt->xfer_func; 12818c2ecf20Sopenharmony_ci } else { 12828c2ecf20Sopenharmony_ci format->format.code = state->fmt_code; 12838c2ecf20Sopenharmony_ci format->format.colorspace = state->colorspace; 12848c2ecf20Sopenharmony_ci format->format.ycbcr_enc = state->ycbcr_enc; 12858c2ecf20Sopenharmony_ci format->format.quantization = state->quantization; 12868c2ecf20Sopenharmony_ci format->format.xfer_func = state->xfer_func; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci return 0; 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic int adv7511_set_fmt(struct v4l2_subdev *sd, 12938c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12948c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 12978c2ecf20Sopenharmony_ci /* 12988c2ecf20Sopenharmony_ci * Bitfield namings come the CEA-861-F standard, table 8 "Auxiliary 12998c2ecf20Sopenharmony_ci * Video Information (AVI) InfoFrame Format" 13008c2ecf20Sopenharmony_ci * 13018c2ecf20Sopenharmony_ci * c = Colorimetry 13028c2ecf20Sopenharmony_ci * ec = Extended Colorimetry 13038c2ecf20Sopenharmony_ci * y = RGB or YCbCr 13048c2ecf20Sopenharmony_ci * q = RGB Quantization Range 13058c2ecf20Sopenharmony_ci * yq = YCC Quantization Range 13068c2ecf20Sopenharmony_ci */ 13078c2ecf20Sopenharmony_ci u8 c = HDMI_COLORIMETRY_NONE; 13088c2ecf20Sopenharmony_ci u8 ec = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 13098c2ecf20Sopenharmony_ci u8 y = HDMI_COLORSPACE_RGB; 13108c2ecf20Sopenharmony_ci u8 q = HDMI_QUANTIZATION_RANGE_DEFAULT; 13118c2ecf20Sopenharmony_ci u8 yq = HDMI_YCC_QUANTIZATION_RANGE_LIMITED; 13128c2ecf20Sopenharmony_ci u8 itc = state->content_type != V4L2_DV_IT_CONTENT_TYPE_NO_ITC; 13138c2ecf20Sopenharmony_ci u8 cn = itc ? state->content_type : V4L2_DV_IT_CONTENT_TYPE_GRAPHICS; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (format->pad != 0) 13168c2ecf20Sopenharmony_ci return -EINVAL; 13178c2ecf20Sopenharmony_ci switch (format->format.code) { 13188c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 13198c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1X16: 13208c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 13218c2ecf20Sopenharmony_ci break; 13228c2ecf20Sopenharmony_ci default: 13238c2ecf20Sopenharmony_ci return -EINVAL; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci adv7511_fill_format(state, &format->format); 13278c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 13288c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 13318c2ecf20Sopenharmony_ci fmt->code = format->format.code; 13328c2ecf20Sopenharmony_ci fmt->colorspace = format->format.colorspace; 13338c2ecf20Sopenharmony_ci fmt->ycbcr_enc = format->format.ycbcr_enc; 13348c2ecf20Sopenharmony_ci fmt->quantization = format->format.quantization; 13358c2ecf20Sopenharmony_ci fmt->xfer_func = format->format.xfer_func; 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci switch (format->format.code) { 13408c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 13418c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x15, 0xf0, 0x01); 13428c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x16, 0x03, 0xb8); 13438c2ecf20Sopenharmony_ci y = HDMI_COLORSPACE_YUV422; 13448c2ecf20Sopenharmony_ci break; 13458c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1X16: 13468c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x15, 0xf0, 0x01); 13478c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x16, 0x03, 0xbc); 13488c2ecf20Sopenharmony_ci y = HDMI_COLORSPACE_YUV422; 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 13518c2ecf20Sopenharmony_ci default: 13528c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x15, 0xf0, 0x00); 13538c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x16, 0x03, 0x00); 13548c2ecf20Sopenharmony_ci break; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci state->fmt_code = format->format.code; 13578c2ecf20Sopenharmony_ci state->colorspace = format->format.colorspace; 13588c2ecf20Sopenharmony_ci state->ycbcr_enc = format->format.ycbcr_enc; 13598c2ecf20Sopenharmony_ci state->quantization = format->format.quantization; 13608c2ecf20Sopenharmony_ci state->xfer_func = format->format.xfer_func; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci switch (format->format.colorspace) { 13638c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_OPRGB: 13648c2ecf20Sopenharmony_ci c = HDMI_COLORIMETRY_EXTENDED; 13658c2ecf20Sopenharmony_ci ec = y ? HDMI_EXTENDED_COLORIMETRY_OPYCC_601 : 13668c2ecf20Sopenharmony_ci HDMI_EXTENDED_COLORIMETRY_OPRGB; 13678c2ecf20Sopenharmony_ci break; 13688c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_SMPTE170M: 13698c2ecf20Sopenharmony_ci c = y ? HDMI_COLORIMETRY_ITU_601 : HDMI_COLORIMETRY_NONE; 13708c2ecf20Sopenharmony_ci if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_XV601) { 13718c2ecf20Sopenharmony_ci c = HDMI_COLORIMETRY_EXTENDED; 13728c2ecf20Sopenharmony_ci ec = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci break; 13758c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_REC709: 13768c2ecf20Sopenharmony_ci c = y ? HDMI_COLORIMETRY_ITU_709 : HDMI_COLORIMETRY_NONE; 13778c2ecf20Sopenharmony_ci if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_XV709) { 13788c2ecf20Sopenharmony_ci c = HDMI_COLORIMETRY_EXTENDED; 13798c2ecf20Sopenharmony_ci ec = HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci break; 13828c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_SRGB: 13838c2ecf20Sopenharmony_ci c = y ? HDMI_COLORIMETRY_EXTENDED : HDMI_COLORIMETRY_NONE; 13848c2ecf20Sopenharmony_ci ec = y ? HDMI_EXTENDED_COLORIMETRY_S_YCC_601 : 13858c2ecf20Sopenharmony_ci HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; 13868c2ecf20Sopenharmony_ci break; 13878c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_BT2020: 13888c2ecf20Sopenharmony_ci c = HDMI_COLORIMETRY_EXTENDED; 13898c2ecf20Sopenharmony_ci if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) 13908c2ecf20Sopenharmony_ci ec = 5; /* Not yet available in hdmi.h */ 13918c2ecf20Sopenharmony_ci else 13928c2ecf20Sopenharmony_ci ec = 6; /* Not yet available in hdmi.h */ 13938c2ecf20Sopenharmony_ci break; 13948c2ecf20Sopenharmony_ci default: 13958c2ecf20Sopenharmony_ci break; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* 13998c2ecf20Sopenharmony_ci * CEA-861-F says that for RGB formats the YCC range must match the 14008c2ecf20Sopenharmony_ci * RGB range, although sources should ignore the YCC range. 14018c2ecf20Sopenharmony_ci * 14028c2ecf20Sopenharmony_ci * The RGB quantization range shouldn't be non-zero if the EDID doesn't 14038c2ecf20Sopenharmony_ci * have the Q bit set in the Video Capabilities Data Block, however this 14048c2ecf20Sopenharmony_ci * isn't checked at the moment. The assumption is that the application 14058c2ecf20Sopenharmony_ci * knows the EDID and can detect this. 14068c2ecf20Sopenharmony_ci * 14078c2ecf20Sopenharmony_ci * The same is true for the YCC quantization range: non-standard YCC 14088c2ecf20Sopenharmony_ci * quantization ranges should only be sent if the EDID has the YQ bit 14098c2ecf20Sopenharmony_ci * set in the Video Capabilities Data Block. 14108c2ecf20Sopenharmony_ci */ 14118c2ecf20Sopenharmony_ci switch (format->format.quantization) { 14128c2ecf20Sopenharmony_ci case V4L2_QUANTIZATION_FULL_RANGE: 14138c2ecf20Sopenharmony_ci q = y ? HDMI_QUANTIZATION_RANGE_DEFAULT : 14148c2ecf20Sopenharmony_ci HDMI_QUANTIZATION_RANGE_FULL; 14158c2ecf20Sopenharmony_ci yq = q ? q - 1 : HDMI_YCC_QUANTIZATION_RANGE_FULL; 14168c2ecf20Sopenharmony_ci break; 14178c2ecf20Sopenharmony_ci case V4L2_QUANTIZATION_LIM_RANGE: 14188c2ecf20Sopenharmony_ci q = y ? HDMI_QUANTIZATION_RANGE_DEFAULT : 14198c2ecf20Sopenharmony_ci HDMI_QUANTIZATION_RANGE_LIMITED; 14208c2ecf20Sopenharmony_ci yq = q ? q - 1 : HDMI_YCC_QUANTIZATION_RANGE_LIMITED; 14218c2ecf20Sopenharmony_ci break; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x4a, 0xbf, 0); 14258c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x55, 0x9f, y << 5); 14268c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x56, 0x3f, c << 6); 14278c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2) | (itc << 7)); 14288c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x59, 0x0f, (yq << 6) | (cn << 4)); 14298c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x4a, 0xff, 1); 14308c2ecf20Sopenharmony_ci adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops adv7511_pad_ops = { 14368c2ecf20Sopenharmony_ci .get_edid = adv7511_get_edid, 14378c2ecf20Sopenharmony_ci .enum_mbus_code = adv7511_enum_mbus_code, 14388c2ecf20Sopenharmony_ci .get_fmt = adv7511_get_fmt, 14398c2ecf20Sopenharmony_ci .set_fmt = adv7511_set_fmt, 14408c2ecf20Sopenharmony_ci .enum_dv_timings = adv7511_enum_dv_timings, 14418c2ecf20Sopenharmony_ci .dv_timings_cap = adv7511_dv_timings_cap, 14428c2ecf20Sopenharmony_ci}; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci/* --------------------- SUBDEV OPS --------------------------------------- */ 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops adv7511_ops = { 14478c2ecf20Sopenharmony_ci .core = &adv7511_core_ops, 14488c2ecf20Sopenharmony_ci .pad = &adv7511_pad_ops, 14498c2ecf20Sopenharmony_ci .video = &adv7511_video_ops, 14508c2ecf20Sopenharmony_ci .audio = &adv7511_audio_ops, 14518c2ecf20Sopenharmony_ci}; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 14548c2ecf20Sopenharmony_cistatic void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, int segment, u8 *buf) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci if (debug >= lvl) { 14578c2ecf20Sopenharmony_ci int i, j; 14588c2ecf20Sopenharmony_ci v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment); 14598c2ecf20Sopenharmony_ci for (i = 0; i < 256; i += 16) { 14608c2ecf20Sopenharmony_ci u8 b[128]; 14618c2ecf20Sopenharmony_ci u8 *bp = b; 14628c2ecf20Sopenharmony_ci if (i == 128) 14638c2ecf20Sopenharmony_ci v4l2_dbg(lvl, debug, sd, "\n"); 14648c2ecf20Sopenharmony_ci for (j = i; j < i + 16; j++) { 14658c2ecf20Sopenharmony_ci sprintf(bp, "0x%02x, ", buf[j]); 14668c2ecf20Sopenharmony_ci bp += 6; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci bp[0] = '\0'; 14698c2ecf20Sopenharmony_ci v4l2_dbg(lvl, debug, sd, "%s\n", b); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic void adv7511_notify_no_edid(struct v4l2_subdev *sd) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 14778c2ecf20Sopenharmony_ci struct adv7511_edid_detect ed; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci /* We failed to read the EDID, so send an event for this. */ 14808c2ecf20Sopenharmony_ci ed.present = false; 14818c2ecf20Sopenharmony_ci ed.segment = adv7511_rd(sd, 0xc4); 14828c2ecf20Sopenharmony_ci ed.phys_addr = CEC_PHYS_ADDR_INVALID; 14838c2ecf20Sopenharmony_ci cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); 14848c2ecf20Sopenharmony_ci v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); 14858c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0); 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic void adv7511_edid_handler(struct work_struct *work) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 14918c2ecf20Sopenharmony_ci struct adv7511_state *state = container_of(dwork, struct adv7511_state, edid_handler); 14928c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s:\n", __func__); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci if (adv7511_check_edid_status(sd)) { 14978c2ecf20Sopenharmony_ci /* Return if we received the EDID. */ 14988c2ecf20Sopenharmony_ci return; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci if (adv7511_have_hotplug(sd)) { 15028c2ecf20Sopenharmony_ci /* We must retry reading the EDID several times, it is possible 15038c2ecf20Sopenharmony_ci * that initially the EDID couldn't be read due to i2c errors 15048c2ecf20Sopenharmony_ci * (DVI connectors are particularly prone to this problem). */ 15058c2ecf20Sopenharmony_ci if (state->edid.read_retries) { 15068c2ecf20Sopenharmony_ci state->edid.read_retries--; 15078c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); 15088c2ecf20Sopenharmony_ci state->have_monitor = false; 15098c2ecf20Sopenharmony_ci adv7511_s_power(sd, false); 15108c2ecf20Sopenharmony_ci adv7511_s_power(sd, true); 15118c2ecf20Sopenharmony_ci queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); 15128c2ecf20Sopenharmony_ci return; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* We failed to read the EDID, so send an event for this. */ 15178c2ecf20Sopenharmony_ci adv7511_notify_no_edid(sd); 15188c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic void adv7511_audio_setup(struct v4l2_subdev *sd) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s\n", __func__); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci adv7511_s_i2s_clock_freq(sd, 48000); 15268c2ecf20Sopenharmony_ci adv7511_s_clock_freq(sd, 48000); 15278c2ecf20Sopenharmony_ci adv7511_s_routing(sd, 0, 0, 0); 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci/* Configure hdmi transmitter. */ 15318c2ecf20Sopenharmony_cistatic void adv7511_setup(struct v4l2_subdev *sd) 15328c2ecf20Sopenharmony_ci{ 15338c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 15348c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s\n", __func__); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* Input format: RGB 4:4:4 */ 15378c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x15, 0xf0, 0x0); 15388c2ecf20Sopenharmony_ci /* Output format: RGB 4:4:4 */ 15398c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x16, 0x7f, 0x0); 15408c2ecf20Sopenharmony_ci /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, Aspect ratio: 16:9 */ 15418c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x17, 0xf9, 0x06); 15428c2ecf20Sopenharmony_ci /* Disable pixel repetition */ 15438c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x3b, 0x9f, 0x0); 15448c2ecf20Sopenharmony_ci /* Disable CSC */ 15458c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0); 15468c2ecf20Sopenharmony_ci /* Output format: RGB 4:4:4, Active Format Information is valid, 15478c2ecf20Sopenharmony_ci * underscanned */ 15488c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x55, 0x9c, 0x12); 15498c2ecf20Sopenharmony_ci /* AVI Info frame packet enable, Audio Info frame disable */ 15508c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0x44, 0xe7, 0x10); 15518c2ecf20Sopenharmony_ci /* Colorimetry, Active format aspect ratio: same as picure. */ 15528c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x56, 0xa8); 15538c2ecf20Sopenharmony_ci /* No encryption */ 15548c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xaf, 0xed, 0x0); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* Positive clk edge capture for input video clock */ 15578c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xba, 0x1f, 0x60); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci adv7511_audio_setup(sd); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&state->hdl); 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic void adv7511_notify_monitor_detect(struct v4l2_subdev *sd) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci struct adv7511_monitor_detect mdt; 15678c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci mdt.present = state->have_monitor; 15708c2ecf20Sopenharmony_ci v4l2_subdev_notify(sd, ADV7511_MONITOR_DETECT, (void *)&mdt); 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 15768c2ecf20Sopenharmony_ci /* read hotplug and rx-sense state */ 15778c2ecf20Sopenharmony_ci u8 status = adv7511_rd(sd, 0x42); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", 15808c2ecf20Sopenharmony_ci __func__, 15818c2ecf20Sopenharmony_ci status, 15828c2ecf20Sopenharmony_ci status & MASK_ADV7511_HPD_DETECT ? ", hotplug" : "", 15838c2ecf20Sopenharmony_ci status & MASK_ADV7511_MSEN_DETECT ? ", rx-sense" : ""); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* update read only ctrls */ 15868c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(state->hotplug_ctrl, adv7511_have_hotplug(sd) ? 0x1 : 0x0); 15878c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, adv7511_have_rx_sense(sd) ? 0x1 : 0x0); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci if ((status & MASK_ADV7511_HPD_DETECT) && ((status & MASK_ADV7511_MSEN_DETECT) || state->edid.segments)) { 15908c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: hotplug and (rx-sense or edid)\n", __func__); 15918c2ecf20Sopenharmony_ci if (!state->have_monitor) { 15928c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__); 15938c2ecf20Sopenharmony_ci state->have_monitor = true; 15948c2ecf20Sopenharmony_ci adv7511_set_isr(sd, true); 15958c2ecf20Sopenharmony_ci if (!adv7511_s_power(sd, true)) { 15968c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: monitor detected, powerup failed\n", __func__); 15978c2ecf20Sopenharmony_ci return; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci adv7511_setup(sd); 16008c2ecf20Sopenharmony_ci adv7511_notify_monitor_detect(sd); 16018c2ecf20Sopenharmony_ci state->edid.read_retries = EDID_MAX_RETRIES; 16028c2ecf20Sopenharmony_ci queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci } else if (status & MASK_ADV7511_HPD_DETECT) { 16058c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); 16068c2ecf20Sopenharmony_ci state->edid.read_retries = EDID_MAX_RETRIES; 16078c2ecf20Sopenharmony_ci queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); 16088c2ecf20Sopenharmony_ci } else if (!(status & MASK_ADV7511_HPD_DETECT)) { 16098c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); 16108c2ecf20Sopenharmony_ci if (state->have_monitor) { 16118c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__); 16128c2ecf20Sopenharmony_ci state->have_monitor = false; 16138c2ecf20Sopenharmony_ci adv7511_notify_monitor_detect(sd); 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci adv7511_s_power(sd, false); 16168c2ecf20Sopenharmony_ci memset(&state->edid, 0, sizeof(struct adv7511_state_edid)); 16178c2ecf20Sopenharmony_ci adv7511_notify_no_edid(sd); 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic bool edid_block_verify_crc(u8 *edid_block) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci u8 sum = 0; 16248c2ecf20Sopenharmony_ci int i; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci for (i = 0; i < 128; i++) 16278c2ecf20Sopenharmony_ci sum += edid_block[i]; 16288c2ecf20Sopenharmony_ci return sum == 0; 16298c2ecf20Sopenharmony_ci} 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_cistatic bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment) 16328c2ecf20Sopenharmony_ci{ 16338c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 16348c2ecf20Sopenharmony_ci u32 blocks = state->edid.blocks; 16358c2ecf20Sopenharmony_ci u8 *data = state->edid.data; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (!edid_block_verify_crc(&data[segment * 256])) 16388c2ecf20Sopenharmony_ci return false; 16398c2ecf20Sopenharmony_ci if ((segment + 1) * 2 <= blocks) 16408c2ecf20Sopenharmony_ci return edid_block_verify_crc(&data[segment * 256 + 128]); 16418c2ecf20Sopenharmony_ci return true; 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_cistatic bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci static const u8 hdmi_header[] = { 16478c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 16488c2ecf20Sopenharmony_ci }; 16498c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 16508c2ecf20Sopenharmony_ci u8 *data = state->edid.data; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (segment != 0) 16538c2ecf20Sopenharmony_ci return true; 16548c2ecf20Sopenharmony_ci return !memcmp(data, hdmi_header, sizeof(hdmi_header)); 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_cistatic bool adv7511_check_edid_status(struct v4l2_subdev *sd) 16588c2ecf20Sopenharmony_ci{ 16598c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 16608c2ecf20Sopenharmony_ci u8 edidRdy = adv7511_rd(sd, 0xc5); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", 16638c2ecf20Sopenharmony_ci __func__, EDID_MAX_RETRIES - state->edid.read_retries); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if (state->edid.complete) 16668c2ecf20Sopenharmony_ci return true; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (edidRdy & MASK_ADV7511_EDID_RDY) { 16698c2ecf20Sopenharmony_ci int segment = adv7511_rd(sd, 0xc4); 16708c2ecf20Sopenharmony_ci struct adv7511_edid_detect ed; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (segment >= EDID_MAX_SEGM) { 16738c2ecf20Sopenharmony_ci v4l2_err(sd, "edid segment number too big\n"); 16748c2ecf20Sopenharmony_ci return false; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); 16778c2ecf20Sopenharmony_ci adv7511_edid_rd(sd, 256, &state->edid.data[segment * 256]); 16788c2ecf20Sopenharmony_ci adv7511_dbg_dump_edid(2, debug, sd, segment, &state->edid.data[segment * 256]); 16798c2ecf20Sopenharmony_ci if (segment == 0) { 16808c2ecf20Sopenharmony_ci state->edid.blocks = state->edid.data[0x7e] + 1; 16818c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks); 16828c2ecf20Sopenharmony_ci } 16838c2ecf20Sopenharmony_ci if (!edid_verify_crc(sd, segment) || 16848c2ecf20Sopenharmony_ci !edid_verify_header(sd, segment)) { 16858c2ecf20Sopenharmony_ci /* edid crc error, force reread of edid segment */ 16868c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: edid crc or header error\n", __func__); 16878c2ecf20Sopenharmony_ci state->have_monitor = false; 16888c2ecf20Sopenharmony_ci adv7511_s_power(sd, false); 16898c2ecf20Sopenharmony_ci adv7511_s_power(sd, true); 16908c2ecf20Sopenharmony_ci return false; 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci /* one more segment read ok */ 16938c2ecf20Sopenharmony_ci state->edid.segments = segment + 1; 16948c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x1); 16958c2ecf20Sopenharmony_ci if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { 16968c2ecf20Sopenharmony_ci /* Request next EDID segment */ 16978c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments); 16988c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xc9, 0xf); 16998c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xc4, state->edid.segments); 17008c2ecf20Sopenharmony_ci state->edid.read_retries = EDID_MAX_RETRIES; 17018c2ecf20Sopenharmony_ci queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); 17028c2ecf20Sopenharmony_ci return false; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); 17068c2ecf20Sopenharmony_ci state->edid.complete = true; 17078c2ecf20Sopenharmony_ci ed.phys_addr = cec_get_edid_phys_addr(state->edid.data, 17088c2ecf20Sopenharmony_ci state->edid.segments * 256, 17098c2ecf20Sopenharmony_ci NULL); 17108c2ecf20Sopenharmony_ci /* report when we have all segments 17118c2ecf20Sopenharmony_ci but report only for segment 0 17128c2ecf20Sopenharmony_ci */ 17138c2ecf20Sopenharmony_ci ed.present = true; 17148c2ecf20Sopenharmony_ci ed.segment = 0; 17158c2ecf20Sopenharmony_ci state->edid_detect_counter++; 17168c2ecf20Sopenharmony_ci cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); 17178c2ecf20Sopenharmony_ci v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); 17188c2ecf20Sopenharmony_ci return ed.present; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci return false; 17228c2ecf20Sopenharmony_ci} 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_cistatic int adv7511_registered(struct v4l2_subdev *sd) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 17278c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 17288c2ecf20Sopenharmony_ci int err; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci err = cec_register_adapter(state->cec_adap, &client->dev); 17318c2ecf20Sopenharmony_ci if (err) 17328c2ecf20Sopenharmony_ci cec_delete_adapter(state->cec_adap); 17338c2ecf20Sopenharmony_ci return err; 17348c2ecf20Sopenharmony_ci} 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_cistatic void adv7511_unregistered(struct v4l2_subdev *sd) 17378c2ecf20Sopenharmony_ci{ 17388c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci cec_unregister_adapter(state->cec_adap); 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops adv7511_int_ops = { 17448c2ecf20Sopenharmony_ci .registered = adv7511_registered, 17458c2ecf20Sopenharmony_ci .unregistered = adv7511_unregistered, 17468c2ecf20Sopenharmony_ci}; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 17498c2ecf20Sopenharmony_ci/* Setup ADV7511 */ 17508c2ecf20Sopenharmony_cistatic void adv7511_init_setup(struct v4l2_subdev *sd) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 17538c2ecf20Sopenharmony_ci struct adv7511_state_edid *edid = &state->edid; 17548c2ecf20Sopenharmony_ci u32 cec_clk = state->pdata.cec_clk; 17558c2ecf20Sopenharmony_ci u8 ratio; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s\n", __func__); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci /* clear all interrupts */ 17608c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x96, 0xff); 17618c2ecf20Sopenharmony_ci adv7511_wr(sd, 0x97, 0xff); 17628c2ecf20Sopenharmony_ci /* 17638c2ecf20Sopenharmony_ci * Stop HPD from resetting a lot of registers. 17648c2ecf20Sopenharmony_ci * It might leave the chip in a partly un-initialized state, 17658c2ecf20Sopenharmony_ci * in particular with regards to hotplug bounces. 17668c2ecf20Sopenharmony_ci */ 17678c2ecf20Sopenharmony_ci adv7511_wr_and_or(sd, 0xd6, 0x3f, 0xc0); 17688c2ecf20Sopenharmony_ci memset(edid, 0, sizeof(struct adv7511_state_edid)); 17698c2ecf20Sopenharmony_ci state->have_monitor = false; 17708c2ecf20Sopenharmony_ci adv7511_set_isr(sd, false); 17718c2ecf20Sopenharmony_ci adv7511_s_stream(sd, false); 17728c2ecf20Sopenharmony_ci adv7511_s_audio_stream(sd, false); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (state->i2c_cec == NULL) 17758c2ecf20Sopenharmony_ci return; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci /* cec soft reset */ 17808c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x50, 0x01); 17818c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x50, 0x00); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci /* legacy mode */ 17848c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4a, 0x00); 17858c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4a, 0x07); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci if (cec_clk % 750000 != 0) 17888c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n", 17898c2ecf20Sopenharmony_ci __func__, cec_clk); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci ratio = (cec_clk / 750000) - 1; 17928c2ecf20Sopenharmony_ci adv7511_cec_write(sd, 0x4e, ratio << 2); 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) 17968c2ecf20Sopenharmony_ci{ 17978c2ecf20Sopenharmony_ci struct adv7511_state *state; 17988c2ecf20Sopenharmony_ci struct adv7511_platform_data *pdata = client->dev.platform_data; 17998c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 18008c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 18018c2ecf20Sopenharmony_ci u8 chip_id[2]; 18028c2ecf20Sopenharmony_ci int err = -EIO; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci /* Check if the adapter supports the needed features */ 18058c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 18068c2ecf20Sopenharmony_ci return -EIO; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci state = devm_kzalloc(&client->dev, sizeof(struct adv7511_state), GFP_KERNEL); 18098c2ecf20Sopenharmony_ci if (!state) 18108c2ecf20Sopenharmony_ci return -ENOMEM; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* Platform data */ 18138c2ecf20Sopenharmony_ci if (!pdata) { 18148c2ecf20Sopenharmony_ci v4l_err(client, "No platform data!\n"); 18158c2ecf20Sopenharmony_ci return -ENODEV; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci memcpy(&state->pdata, pdata, sizeof(state->pdata)); 18188c2ecf20Sopenharmony_ci state->fmt_code = MEDIA_BUS_FMT_RGB888_1X24; 18198c2ecf20Sopenharmony_ci state->colorspace = V4L2_COLORSPACE_SRGB; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci sd = &state->sd; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "detecting adv7511 client on address 0x%x\n", 18248c2ecf20Sopenharmony_ci client->addr << 1); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &adv7511_ops); 18278c2ecf20Sopenharmony_ci sd->internal_ops = &adv7511_int_ops; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci hdl = &state->hdl; 18308c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 10); 18318c2ecf20Sopenharmony_ci /* add in ascending ID order */ 18328c2ecf20Sopenharmony_ci state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, 18338c2ecf20Sopenharmony_ci V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 18348c2ecf20Sopenharmony_ci 0, V4L2_DV_TX_MODE_DVI_D); 18358c2ecf20Sopenharmony_ci state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, 18368c2ecf20Sopenharmony_ci V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); 18378c2ecf20Sopenharmony_ci state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, 18388c2ecf20Sopenharmony_ci V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); 18398c2ecf20Sopenharmony_ci state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, 18408c2ecf20Sopenharmony_ci V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); 18418c2ecf20Sopenharmony_ci state->rgb_quantization_range_ctrl = 18428c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, 18438c2ecf20Sopenharmony_ci V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 18448c2ecf20Sopenharmony_ci 0, V4L2_DV_RGB_RANGE_AUTO); 18458c2ecf20Sopenharmony_ci state->content_type_ctrl = 18468c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, 18478c2ecf20Sopenharmony_ci V4L2_CID_DV_TX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 18488c2ecf20Sopenharmony_ci 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); 18498c2ecf20Sopenharmony_ci sd->ctrl_handler = hdl; 18508c2ecf20Sopenharmony_ci if (hdl->error) { 18518c2ecf20Sopenharmony_ci err = hdl->error; 18528c2ecf20Sopenharmony_ci goto err_hdl; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci state->pad.flags = MEDIA_PAD_FL_SINK; 18558c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_DV_ENCODER; 18568c2ecf20Sopenharmony_ci err = media_entity_pads_init(&sd->entity, 1, &state->pad); 18578c2ecf20Sopenharmony_ci if (err) 18588c2ecf20Sopenharmony_ci goto err_hdl; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci /* EDID and CEC i2c addr */ 18618c2ecf20Sopenharmony_ci state->i2c_edid_addr = state->pdata.i2c_edid << 1; 18628c2ecf20Sopenharmony_ci state->i2c_cec_addr = state->pdata.i2c_cec << 1; 18638c2ecf20Sopenharmony_ci state->i2c_pktmem_addr = state->pdata.i2c_pktmem << 1; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci state->chip_revision = adv7511_rd(sd, 0x0); 18668c2ecf20Sopenharmony_ci chip_id[0] = adv7511_rd(sd, 0xf5); 18678c2ecf20Sopenharmony_ci chip_id[1] = adv7511_rd(sd, 0xf6); 18688c2ecf20Sopenharmony_ci if (chip_id[0] != 0x75 || chip_id[1] != 0x11) { 18698c2ecf20Sopenharmony_ci v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], 18708c2ecf20Sopenharmony_ci chip_id[1]); 18718c2ecf20Sopenharmony_ci err = -EIO; 18728c2ecf20Sopenharmony_ci goto err_entity; 18738c2ecf20Sopenharmony_ci } 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci state->i2c_edid = i2c_new_dummy_device(client->adapter, 18768c2ecf20Sopenharmony_ci state->i2c_edid_addr >> 1); 18778c2ecf20Sopenharmony_ci if (IS_ERR(state->i2c_edid)) { 18788c2ecf20Sopenharmony_ci v4l2_err(sd, "failed to register edid i2c client\n"); 18798c2ecf20Sopenharmony_ci err = PTR_ERR(state->i2c_edid); 18808c2ecf20Sopenharmony_ci goto err_entity; 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xe1, state->i2c_cec_addr); 18848c2ecf20Sopenharmony_ci if (state->pdata.cec_clk < 3000000 || 18858c2ecf20Sopenharmony_ci state->pdata.cec_clk > 100000000) { 18868c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n", 18878c2ecf20Sopenharmony_ci __func__, state->pdata.cec_clk); 18888c2ecf20Sopenharmony_ci state->pdata.cec_clk = 0; 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (state->pdata.cec_clk) { 18928c2ecf20Sopenharmony_ci state->i2c_cec = i2c_new_dummy_device(client->adapter, 18938c2ecf20Sopenharmony_ci state->i2c_cec_addr >> 1); 18948c2ecf20Sopenharmony_ci if (IS_ERR(state->i2c_cec)) { 18958c2ecf20Sopenharmony_ci v4l2_err(sd, "failed to register cec i2c client\n"); 18968c2ecf20Sopenharmony_ci err = PTR_ERR(state->i2c_cec); 18978c2ecf20Sopenharmony_ci goto err_unreg_edid; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */ 19008c2ecf20Sopenharmony_ci } else { 19018c2ecf20Sopenharmony_ci adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci state->i2c_pktmem = i2c_new_dummy_device(client->adapter, state->i2c_pktmem_addr >> 1); 19058c2ecf20Sopenharmony_ci if (IS_ERR(state->i2c_pktmem)) { 19068c2ecf20Sopenharmony_ci v4l2_err(sd, "failed to register pktmem i2c client\n"); 19078c2ecf20Sopenharmony_ci err = PTR_ERR(state->i2c_pktmem); 19088c2ecf20Sopenharmony_ci goto err_unreg_cec; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci state->work_queue = create_singlethread_workqueue(sd->name); 19128c2ecf20Sopenharmony_ci if (state->work_queue == NULL) { 19138c2ecf20Sopenharmony_ci v4l2_err(sd, "could not create workqueue\n"); 19148c2ecf20Sopenharmony_ci err = -ENOMEM; 19158c2ecf20Sopenharmony_ci goto err_unreg_pktmem; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci adv7511_init_setup(sd); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) 19238c2ecf20Sopenharmony_ci state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops, 19248c2ecf20Sopenharmony_ci state, dev_name(&client->dev), CEC_CAP_DEFAULTS, 19258c2ecf20Sopenharmony_ci ADV7511_MAX_ADDRS); 19268c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(state->cec_adap); 19278c2ecf20Sopenharmony_ci if (err) { 19288c2ecf20Sopenharmony_ci destroy_workqueue(state->work_queue); 19298c2ecf20Sopenharmony_ci goto err_unreg_pktmem; 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci#endif 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci adv7511_set_isr(sd, true); 19348c2ecf20Sopenharmony_ci adv7511_check_monitor_present_status(sd); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, 19378c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 19388c2ecf20Sopenharmony_ci return 0; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_cierr_unreg_pktmem: 19418c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_pktmem); 19428c2ecf20Sopenharmony_cierr_unreg_cec: 19438c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_cec); 19448c2ecf20Sopenharmony_cierr_unreg_edid: 19458c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_edid); 19468c2ecf20Sopenharmony_cierr_entity: 19478c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 19488c2ecf20Sopenharmony_cierr_hdl: 19498c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 19508c2ecf20Sopenharmony_ci return err; 19518c2ecf20Sopenharmony_ci} 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_cistatic int adv7511_remove(struct i2c_client *client) 19568c2ecf20Sopenharmony_ci{ 19578c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 19588c2ecf20Sopenharmony_ci struct adv7511_state *state = get_adv7511_state(sd); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci state->chip_revision = -1; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, 19638c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci adv7511_set_isr(sd, false); 19668c2ecf20Sopenharmony_ci adv7511_init_setup(sd); 19678c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->edid_handler); 19688c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_edid); 19698c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_cec); 19708c2ecf20Sopenharmony_ci i2c_unregister_device(state->i2c_pktmem); 19718c2ecf20Sopenharmony_ci destroy_workqueue(state->work_queue); 19728c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 19738c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 19748c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 19758c2ecf20Sopenharmony_ci return 0; 19768c2ecf20Sopenharmony_ci} 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_cistatic const struct i2c_device_id adv7511_id[] = { 19818c2ecf20Sopenharmony_ci { "adv7511-v4l2", 0 }, 19828c2ecf20Sopenharmony_ci { } 19838c2ecf20Sopenharmony_ci}; 19848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adv7511_id); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_cistatic struct i2c_driver adv7511_driver = { 19878c2ecf20Sopenharmony_ci .driver = { 19888c2ecf20Sopenharmony_ci .name = "adv7511-v4l2", 19898c2ecf20Sopenharmony_ci }, 19908c2ecf20Sopenharmony_ci .probe = adv7511_probe, 19918c2ecf20Sopenharmony_ci .remove = adv7511_remove, 19928c2ecf20Sopenharmony_ci .id_table = adv7511_id, 19938c2ecf20Sopenharmony_ci}; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cimodule_i2c_driver(adv7511_driver); 1996