18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tc358743 - Toshiba HDMI to CSI-2 bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights 68c2ecf20Sopenharmony_ci * reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * References (c = chapter, p = page): 118c2ecf20Sopenharmony_ci * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 128c2ecf20Sopenharmony_ci * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/clk.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/timer.h> 248c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 258c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 268c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 278c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 288c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 298c2ecf20Sopenharmony_ci#include <media/cec.h> 308c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 348c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 358c2ecf20Sopenharmony_ci#include <media/i2c/tc358743.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "tc358743_regs.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int debug; 408c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-3)"); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Toshiba TC358743 HDMI to CSI-2 bridge driver"); 448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ramakrishnan Muthukrishnan <ram@rkrishnan.org>"); 458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mikhail Khelik <mkhelik@cisco.com>"); 468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mats Randgaard <matrandg@cisco.com>"); 478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define EDID_NUM_BLOCKS_MAX 8 508c2ecf20Sopenharmony_ci#define EDID_BLOCK_SIZE 128 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define I2C_MAX_XFER_SIZE (EDID_BLOCK_SIZE + 2) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define POLL_INTERVAL_CEC_MS 10 558c2ecf20Sopenharmony_ci#define POLL_INTERVAL_MS 1000 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap tc358743_timings_cap = { 588c2ecf20Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 598c2ecf20Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 608c2ecf20Sopenharmony_ci .reserved = { 0 }, 618c2ecf20Sopenharmony_ci /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ 628c2ecf20Sopenharmony_ci V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 13000000, 165000000, 638c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 648c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, 658c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_PROGRESSIVE | 668c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_REDUCED_BLANKING | 678c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_CUSTOM) 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct tc358743_state { 718c2ecf20Sopenharmony_ci struct tc358743_platform_data pdata; 728c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_mipi_csi2 bus; 738c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 748c2ecf20Sopenharmony_ci struct media_pad pad; 758c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 768c2ecf20Sopenharmony_ci struct i2c_client *i2c_client; 778c2ecf20Sopenharmony_ci /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ 788c2ecf20Sopenharmony_ci struct mutex confctl_mutex; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* controls */ 818c2ecf20Sopenharmony_ci struct v4l2_ctrl *detect_tx_5v_ctrl; 828c2ecf20Sopenharmony_ci struct v4l2_ctrl *audio_sampling_rate_ctrl; 838c2ecf20Sopenharmony_ci struct v4l2_ctrl *audio_present_ctrl; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci struct delayed_work delayed_work_enable_hotplug; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci struct timer_list timer; 888c2ecf20Sopenharmony_ci struct work_struct work_i2c_poll; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* edid */ 918c2ecf20Sopenharmony_ci u8 edid_blocks_written; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 948c2ecf20Sopenharmony_ci u32 mbus_fmt_code; 958c2ecf20Sopenharmony_ci u8 csi_lanes_in_use; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci struct cec_adapter *cec_adap; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void tc358743_enable_interrupts(struct v4l2_subdev *sd, 1038c2ecf20Sopenharmony_ci bool cable_connected); 1048c2ecf20Sopenharmony_cistatic int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic inline struct tc358743_state *to_state(struct v4l2_subdev *sd) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return container_of(sd, struct tc358743_state, sd); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* --------------- I2C --------------- */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 1168c2ecf20Sopenharmony_ci struct i2c_client *client = state->i2c_client; 1178c2ecf20Sopenharmony_ci int err; 1188c2ecf20Sopenharmony_ci u8 buf[2] = { reg >> 8, reg & 0xff }; 1198c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 1208c2ecf20Sopenharmony_ci { 1218c2ecf20Sopenharmony_ci .addr = client->addr, 1228c2ecf20Sopenharmony_ci .flags = 0, 1238c2ecf20Sopenharmony_ci .len = 2, 1248c2ecf20Sopenharmony_ci .buf = buf, 1258c2ecf20Sopenharmony_ci }, 1268c2ecf20Sopenharmony_ci { 1278c2ecf20Sopenharmony_ci .addr = client->addr, 1288c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 1298c2ecf20Sopenharmony_ci .len = n, 1308c2ecf20Sopenharmony_ci .buf = values, 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci }; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 1358c2ecf20Sopenharmony_ci if (err != ARRAY_SIZE(msgs)) { 1368c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", 1378c2ecf20Sopenharmony_ci __func__, reg, client->addr); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 1448c2ecf20Sopenharmony_ci struct i2c_client *client = state->i2c_client; 1458c2ecf20Sopenharmony_ci int err, i; 1468c2ecf20Sopenharmony_ci struct i2c_msg msg; 1478c2ecf20Sopenharmony_ci u8 data[I2C_MAX_XFER_SIZE]; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if ((2 + n) > I2C_MAX_XFER_SIZE) { 1508c2ecf20Sopenharmony_ci n = I2C_MAX_XFER_SIZE - 2; 1518c2ecf20Sopenharmony_ci v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", 1528c2ecf20Sopenharmony_ci reg, 2 + n); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci msg.addr = client->addr; 1568c2ecf20Sopenharmony_ci msg.buf = data; 1578c2ecf20Sopenharmony_ci msg.len = 2 + n; 1588c2ecf20Sopenharmony_ci msg.flags = 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci data[0] = reg >> 8; 1618c2ecf20Sopenharmony_ci data[1] = reg & 0xff; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 1648c2ecf20Sopenharmony_ci data[2 + i] = values[i]; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, &msg, 1); 1678c2ecf20Sopenharmony_ci if (err != 1) { 1688c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", 1698c2ecf20Sopenharmony_ci __func__, reg, client->addr); 1708c2ecf20Sopenharmony_ci return; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (debug < 3) 1748c2ecf20Sopenharmony_ci return; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci switch (n) { 1778c2ecf20Sopenharmony_ci case 1: 1788c2ecf20Sopenharmony_ci v4l2_info(sd, "I2C write 0x%04x = 0x%02x", 1798c2ecf20Sopenharmony_ci reg, data[2]); 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case 2: 1828c2ecf20Sopenharmony_ci v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x", 1838c2ecf20Sopenharmony_ci reg, data[3], data[2]); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case 4: 1868c2ecf20Sopenharmony_ci v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x%02x%02x", 1878c2ecf20Sopenharmony_ci reg, data[5], data[4], data[3], data[2]); 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci default: 1908c2ecf20Sopenharmony_ci v4l2_info(sd, "I2C write %d bytes from address 0x%04x\n", 1918c2ecf20Sopenharmony_ci n, reg); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci __le32 val = 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci i2c_rd(sd, reg, (u8 __force *)&val, n); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return le32_to_cpu(val); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci __le32 raw = cpu_to_le32(val); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci i2c_wr(sd, reg, (u8 __force *)&raw, n); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return i2c_rdreg(sd, reg, 1); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci i2c_wrreg(sd, reg, val, 1); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, 2228c2ecf20Sopenharmony_ci u8 mask, u8 val) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 1) & mask) | val, 1); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return i2c_rdreg(sd, reg, 2); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci i2c_wrreg(sd, reg, val, 2); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci return i2c_rdreg(sd, reg, 4); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci i2c_wrreg(sd, reg, val, 4); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* --------------- STATUS --------------- */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic inline bool is_hdmi(struct v4l2_subdev *sd) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic inline bool tx_5v_power_present(struct v4l2_subdev *sd) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic inline bool no_signal(struct v4l2_subdev *sd) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic inline bool no_sync(struct v4l2_subdev *sd) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic inline bool audio_present(struct v4l2_subdev *sd) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int get_audio_sampling_rate(struct v4l2_subdev *sd) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci static const int code_to_rate[] = { 2828c2ecf20Sopenharmony_ci 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, 2838c2ecf20Sopenharmony_ci 88200, 768000, 96000, 705600, 176400, 0, 192000, 0 2848c2ecf20Sopenharmony_ci }; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Register FS_SET is not cleared when the cable is disconnected */ 2878c2ecf20Sopenharmony_ci if (no_signal(sd)) 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* --------------- TIMINGS --------------- */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic inline unsigned fps(const struct v4l2_bt_timings *t) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST((unsigned)t->pixelclock, 3018c2ecf20Sopenharmony_ci V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int tc358743_get_detected_timings(struct v4l2_subdev *sd, 3058c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 3088c2ecf20Sopenharmony_ci unsigned width, height, frame_width, frame_height, frame_interval, fps; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci memset(timings, 0, sizeof(struct v4l2_dv_timings)); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (no_signal(sd)) { 3138c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); 3148c2ecf20Sopenharmony_ci return -ENOLINK; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci if (no_sync(sd)) { 3178c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: no sync on signal\n", __func__); 3188c2ecf20Sopenharmony_ci return -ENOLCK; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci timings->type = V4L2_DV_BT_656_1120; 3228c2ecf20Sopenharmony_ci bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ? 3238c2ecf20Sopenharmony_ci V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci width = ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + 3268c2ecf20Sopenharmony_ci i2c_rd8(sd, DE_WIDTH_H_LO); 3278c2ecf20Sopenharmony_ci height = ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + 3288c2ecf20Sopenharmony_ci i2c_rd8(sd, DE_WIDTH_V_LO); 3298c2ecf20Sopenharmony_ci frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + 3308c2ecf20Sopenharmony_ci i2c_rd8(sd, H_SIZE_LO); 3318c2ecf20Sopenharmony_ci frame_height = (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + 3328c2ecf20Sopenharmony_ci i2c_rd8(sd, V_SIZE_LO)) / 2; 3338c2ecf20Sopenharmony_ci /* frame interval in milliseconds * 10 3348c2ecf20Sopenharmony_ci * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ 3358c2ecf20Sopenharmony_ci frame_interval = ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + 3368c2ecf20Sopenharmony_ci i2c_rd8(sd, FV_CNT_LO); 3378c2ecf20Sopenharmony_ci fps = (frame_interval > 0) ? 3388c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(10000, frame_interval) : 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci bt->width = width; 3418c2ecf20Sopenharmony_ci bt->height = height; 3428c2ecf20Sopenharmony_ci bt->vsync = frame_height - height; 3438c2ecf20Sopenharmony_ci bt->hsync = frame_width - width; 3448c2ecf20Sopenharmony_ci bt->pixelclock = frame_width * frame_height * fps; 3458c2ecf20Sopenharmony_ci if (bt->interlaced == V4L2_DV_INTERLACED) { 3468c2ecf20Sopenharmony_ci bt->height *= 2; 3478c2ecf20Sopenharmony_ci bt->il_vsync = bt->vsync + 1; 3488c2ecf20Sopenharmony_ci bt->pixelclock /= 2; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* --------------- HOTPLUG / HDCP / EDID --------------- */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void tc358743_delayed_work_enable_hotplug(struct work_struct *work) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 3598c2ecf20Sopenharmony_ci struct tc358743_state *state = container_of(dwork, 3608c2ecf20Sopenharmony_ci struct tc358743_state, delayed_work_enable_hotplug); 3618c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s:\n", __func__); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? 3718c2ecf20Sopenharmony_ci "enable" : "disable"); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (enable) { 3748c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, KEY_RD_CMD); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HDCP_MODE, ~MASK_MANUAL_AUTHENTICATION, 0); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HDCP_REG1, 0xff, 3798c2ecf20Sopenharmony_ci MASK_AUTH_UNAUTH_SEL_16_FRAMES | 3808c2ecf20Sopenharmony_ci MASK_AUTH_UNAUTH_AUTO); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, 3838c2ecf20Sopenharmony_ci SET_AUTO_P3_RESET_FRAMES(0x0f)); 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HDCP_MODE, ~MASK_MANUAL_AUTHENTICATION, 3868c2ecf20Sopenharmony_ci MASK_MANUAL_AUTHENTICATION); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void tc358743_disable_edid(struct v4l2_subdev *sd) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s:\n", __func__); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* DDC access to EDID is also disabled when hotplug is disabled. See 3998c2ecf20Sopenharmony_ci * register DDC_CTL */ 4008c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic void tc358743_enable_edid(struct v4l2_subdev *sd) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (state->edid_blocks_written == 0) { 4088c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: no EDID -> no hotplug\n", __func__); 4098c2ecf20Sopenharmony_ci tc358743_s_ctrl_detect_tx_5v(sd); 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s:\n", __func__); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when 4168c2ecf20Sopenharmony_ci * hotplug is enabled. See register DDC_CTL */ 4178c2ecf20Sopenharmony_ci schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci tc358743_enable_interrupts(sd, true); 4208c2ecf20Sopenharmony_ci tc358743_s_ctrl_detect_tx_5v(sd); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic void tc358743_erase_bksv(struct v4l2_subdev *sd) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int i; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 4288c2ecf20Sopenharmony_ci i2c_wr8(sd, BKSV + i, 0); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* --------------- AVI infoframe --------------- */ 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void print_avi_infoframe(struct v4l2_subdev *sd) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 4368c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 4378c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 4388c2ecf20Sopenharmony_ci u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!is_hdmi(sd)) { 4418c2ecf20Sopenharmony_ci v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { 4488c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci hdmi_infoframe_log(KERN_INFO, dev, &frame); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* --------------- CTRLS --------------- */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, 4628c2ecf20Sopenharmony_ci tx_5v_power_present(sd)); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, 4708c2ecf20Sopenharmony_ci get_audio_sampling_rate(sd)); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, 4788c2ecf20Sopenharmony_ci audio_present(sd)); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int tc358743_update_controls(struct v4l2_subdev *sd) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci int ret = 0; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ret |= tc358743_s_ctrl_detect_tx_5v(sd); 4868c2ecf20Sopenharmony_ci ret |= tc358743_s_ctrl_audio_sampling_rate(sd); 4878c2ecf20Sopenharmony_ci ret |= tc358743_s_ctrl_audio_present(sd); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return ret; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* --------------- INIT --------------- */ 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void tc358743_reset_phy(struct v4l2_subdev *sd) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s:\n", __func__); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); 4998c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci u16 sysctl = i2c_rd16(sd, SYSCTL); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci i2c_wr16(sd, SYSCTL, sysctl | mask); 5078c2ecf20Sopenharmony_ci i2c_wr16(sd, SYSCTL, sysctl & ~mask); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, 5138c2ecf20Sopenharmony_ci enable ? MASK_SLEEP : 0); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic inline void enable_stream(struct v4l2_subdev *sd, bool enable) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s: %sable\n", 5218c2ecf20Sopenharmony_ci __func__, enable ? "en" : "dis"); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (enable) { 5248c2ecf20Sopenharmony_ci /* It is critical for CSI receiver to see lane transition 5258c2ecf20Sopenharmony_ci * LP11->HS. Set to non-continuous mode to enable clock lane 5268c2ecf20Sopenharmony_ci * LP11 state. */ 5278c2ecf20Sopenharmony_ci i2c_wr32(sd, TXOPTIONCNTRL, 0); 5288c2ecf20Sopenharmony_ci /* Set to continuous mode to trigger LP11->HS transition */ 5298c2ecf20Sopenharmony_ci i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); 5308c2ecf20Sopenharmony_ci /* Unmute video */ 5318c2ecf20Sopenharmony_ci i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); 5328c2ecf20Sopenharmony_ci } else { 5338c2ecf20Sopenharmony_ci /* Mute video so that all data lanes go to LSP11 state. 5348c2ecf20Sopenharmony_ci * No data is output to CSI Tx block. */ 5358c2ecf20Sopenharmony_ci i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&state->confctl_mutex); 5398c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), 5408c2ecf20Sopenharmony_ci enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); 5418c2ecf20Sopenharmony_ci mutex_unlock(&state->confctl_mutex); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic void tc358743_set_pll(struct v4l2_subdev *sd) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 5478c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = &state->pdata; 5488c2ecf20Sopenharmony_ci u16 pllctl0 = i2c_rd16(sd, PLLCTL0); 5498c2ecf20Sopenharmony_ci u16 pllctl1 = i2c_rd16(sd, PLLCTL1); 5508c2ecf20Sopenharmony_ci u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | 5518c2ecf20Sopenharmony_ci SET_PLL_FBD(pdata->pll_fbd); 5528c2ecf20Sopenharmony_ci u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s:\n", __func__); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Only rewrite when needed (new value or disabled), since rewriting 5578c2ecf20Sopenharmony_ci * triggers another format change event. */ 5588c2ecf20Sopenharmony_ci if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { 5598c2ecf20Sopenharmony_ci u16 pll_frs; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (hsck > 500000000) 5628c2ecf20Sopenharmony_ci pll_frs = 0x0; 5638c2ecf20Sopenharmony_ci else if (hsck > 250000000) 5648c2ecf20Sopenharmony_ci pll_frs = 0x1; 5658c2ecf20Sopenharmony_ci else if (hsck > 125000000) 5668c2ecf20Sopenharmony_ci pll_frs = 0x2; 5678c2ecf20Sopenharmony_ci else 5688c2ecf20Sopenharmony_ci pll_frs = 0x3; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: updating PLL clock\n", __func__); 5718c2ecf20Sopenharmony_ci tc358743_sleep_mode(sd, true); 5728c2ecf20Sopenharmony_ci i2c_wr16(sd, PLLCTL0, pllctl0_new); 5738c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, PLLCTL1, 5748c2ecf20Sopenharmony_ci ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), 5758c2ecf20Sopenharmony_ci (SET_PLL_FRS(pll_frs) | MASK_RESETB | 5768c2ecf20Sopenharmony_ci MASK_PLL_EN)); 5778c2ecf20Sopenharmony_ci udelay(10); /* REF_02, Sheet "Source HDMI" */ 5788c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); 5798c2ecf20Sopenharmony_ci tc358743_sleep_mode(sd, false); 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic void tc358743_set_ref_clk(struct v4l2_subdev *sd) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 5868c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = &state->pdata; 5878c2ecf20Sopenharmony_ci u32 sys_freq; 5888c2ecf20Sopenharmony_ci u32 lockdet_ref; 5898c2ecf20Sopenharmony_ci u32 cec_freq; 5908c2ecf20Sopenharmony_ci u16 fh_min; 5918c2ecf20Sopenharmony_ci u16 fh_max; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci BUG_ON(!(pdata->refclk_hz == 26000000 || 5948c2ecf20Sopenharmony_ci pdata->refclk_hz == 27000000 || 5958c2ecf20Sopenharmony_ci pdata->refclk_hz == 42000000)); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci sys_freq = pdata->refclk_hz / 10000; 5988c2ecf20Sopenharmony_ci i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); 5998c2ecf20Sopenharmony_ci i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, 6028c2ecf20Sopenharmony_ci (pdata->refclk_hz == 42000000) ? 6038c2ecf20Sopenharmony_ci MASK_PHY_SYSCLK_IND : 0x0); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci fh_min = pdata->refclk_hz / 100000; 6068c2ecf20Sopenharmony_ci i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); 6078c2ecf20Sopenharmony_ci i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci fh_max = (fh_min * 66) / 10; 6108c2ecf20Sopenharmony_ci i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); 6118c2ecf20Sopenharmony_ci i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci lockdet_ref = pdata->refclk_hz / 100; 6148c2ecf20Sopenharmony_ci i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); 6158c2ecf20Sopenharmony_ci i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); 6168c2ecf20Sopenharmony_ci i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, 6198c2ecf20Sopenharmony_ci (pdata->refclk_hz == 27000000) ? 6208c2ecf20Sopenharmony_ci MASK_NCO_F0_MOD_27MHZ : 0x0); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * Trial and error suggests that the default register value 6248c2ecf20Sopenharmony_ci * of 656 is for a 42 MHz reference clock. Use that to derive 6258c2ecf20Sopenharmony_ci * a new value based on the actual reference clock. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci cec_freq = (656 * sys_freq) / 4200; 6288c2ecf20Sopenharmony_ci i2c_wr16(sd, CECHCLK, cec_freq); 6298c2ecf20Sopenharmony_ci i2c_wr16(sd, CECLCLK, cec_freq); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void tc358743_set_csi_color_space(struct v4l2_subdev *sd) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci switch (state->mbus_fmt_code) { 6378c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 6388c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: YCbCr 422 16-bit\n", __func__); 6398c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, VOUT_SET2, 6408c2ecf20Sopenharmony_ci ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, 6418c2ecf20Sopenharmony_ci MASK_SEL422 | MASK_VOUT_422FIL_100); 6428c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, 6438c2ecf20Sopenharmony_ci MASK_VOUT_COLOR_601_YCBCR_LIMITED); 6448c2ecf20Sopenharmony_ci mutex_lock(&state->confctl_mutex); 6458c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 6468c2ecf20Sopenharmony_ci MASK_YCBCRFMT_422_8_BIT); 6478c2ecf20Sopenharmony_ci mutex_unlock(&state->confctl_mutex); 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 6508c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: RGB 888 24-bit\n", __func__); 6518c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, VOUT_SET2, 6528c2ecf20Sopenharmony_ci ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, 6538c2ecf20Sopenharmony_ci 0x00); 6548c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, 6558c2ecf20Sopenharmony_ci MASK_VOUT_COLOR_RGB_FULL); 6568c2ecf20Sopenharmony_ci mutex_lock(&state->confctl_mutex); 6578c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); 6588c2ecf20Sopenharmony_ci mutex_unlock(&state->confctl_mutex); 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci default: 6618c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", 6628c2ecf20Sopenharmony_ci __func__, state->mbus_fmt_code); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 6698c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &state->timings.bt; 6708c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = &state->pdata; 6718c2ecf20Sopenharmony_ci u32 bits_pr_pixel = 6728c2ecf20Sopenharmony_ci (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; 6738c2ecf20Sopenharmony_ci u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; 6748c2ecf20Sopenharmony_ci u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return DIV_ROUND_UP(bps, bps_pr_lane); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void tc358743_set_csi(struct v4l2_subdev *sd) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 6828c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = &state->pdata; 6838c2ecf20Sopenharmony_ci unsigned lanes = tc358743_num_csi_lanes_needed(sd); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s:\n", __func__); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci state->csi_lanes_in_use = lanes; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci tc358743_reset(sd, MASK_CTXRST); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (lanes < 1) 6928c2ecf20Sopenharmony_ci i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); 6938c2ecf20Sopenharmony_ci if (lanes < 1) 6948c2ecf20Sopenharmony_ci i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); 6958c2ecf20Sopenharmony_ci if (lanes < 2) 6968c2ecf20Sopenharmony_ci i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); 6978c2ecf20Sopenharmony_ci if (lanes < 3) 6988c2ecf20Sopenharmony_ci i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); 6998c2ecf20Sopenharmony_ci if (lanes < 4) 7008c2ecf20Sopenharmony_ci i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); 7038c2ecf20Sopenharmony_ci i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); 7048c2ecf20Sopenharmony_ci i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); 7058c2ecf20Sopenharmony_ci i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); 7068c2ecf20Sopenharmony_ci i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); 7078c2ecf20Sopenharmony_ci i2c_wr32(sd, TWAKEUP, pdata->twakeup); 7088c2ecf20Sopenharmony_ci i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); 7098c2ecf20Sopenharmony_ci i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); 7108c2ecf20Sopenharmony_ci i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci i2c_wr32(sd, HSTXVREGEN, 7138c2ecf20Sopenharmony_ci ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | 7148c2ecf20Sopenharmony_ci ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | 7158c2ecf20Sopenharmony_ci ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | 7168c2ecf20Sopenharmony_ci ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | 7178c2ecf20Sopenharmony_ci ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & 7208c2ecf20Sopenharmony_ci V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); 7218c2ecf20Sopenharmony_ci i2c_wr32(sd, STARTCNTRL, MASK_START); 7228c2ecf20Sopenharmony_ci i2c_wr32(sd, CSI_START, MASK_STRT); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | 7258c2ecf20Sopenharmony_ci MASK_ADDRESS_CSI_CONTROL | 7268c2ecf20Sopenharmony_ci MASK_CSI_MODE | 7278c2ecf20Sopenharmony_ci MASK_TXHSMD | 7288c2ecf20Sopenharmony_ci ((lanes == 4) ? MASK_NOL_4 : 7298c2ecf20Sopenharmony_ci (lanes == 3) ? MASK_NOL_3 : 7308c2ecf20Sopenharmony_ci (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | 7338c2ecf20Sopenharmony_ci MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | MASK_QUNK | 7348c2ecf20Sopenharmony_ci MASK_WCER | MASK_INER); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci i2c_wr32(sd, CSI_CONFW, MASK_MODE_CLEAR | 7378c2ecf20Sopenharmony_ci MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | 7408c2ecf20Sopenharmony_ci MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 7468c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = &state->pdata; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* Default settings from REF_02, sheet "Source HDMI" 7498c2ecf20Sopenharmony_ci * and custom settings as platform data */ 7508c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); 7518c2ecf20Sopenharmony_ci i2c_wr8(sd, PHY_CTL1, SET_PHY_AUTO_RST1_US(1600) | 7528c2ecf20Sopenharmony_ci SET_FREQ_RANGE_MODE_CYCLES(1)); 7538c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, 7548c2ecf20Sopenharmony_ci (pdata->hdmi_phy_auto_reset_tmds_detected ? 7558c2ecf20Sopenharmony_ci MASK_PHY_AUTO_RST2 : 0) | 7568c2ecf20Sopenharmony_ci (pdata->hdmi_phy_auto_reset_tmds_in_range ? 7578c2ecf20Sopenharmony_ci MASK_PHY_AUTO_RST3 : 0) | 7588c2ecf20Sopenharmony_ci (pdata->hdmi_phy_auto_reset_tmds_valid ? 7598c2ecf20Sopenharmony_ci MASK_PHY_AUTO_RST4 : 0)); 7608c2ecf20Sopenharmony_ci i2c_wr8(sd, PHY_BIAS, 0x40); 7618c2ecf20Sopenharmony_ci i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); 7628c2ecf20Sopenharmony_ci i2c_wr8(sd, AVM_CTL, 45); 7638c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, 7648c2ecf20Sopenharmony_ci pdata->hdmi_detection_delay << 4); 7658c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), 7668c2ecf20Sopenharmony_ci (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? 7678c2ecf20Sopenharmony_ci MASK_H_PI_RST : 0) | 7688c2ecf20Sopenharmony_ci (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? 7698c2ecf20Sopenharmony_ci MASK_V_PI_RST : 0)); 7708c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Default settings from REF_02, sheet "Source HDMI" */ 7788c2ecf20Sopenharmony_ci i2c_wr8(sd, FORCE_MUTE, 0x00); 7798c2ecf20Sopenharmony_ci i2c_wr8(sd, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | 7808c2ecf20Sopenharmony_ci MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 | 7818c2ecf20Sopenharmony_ci MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); 7828c2ecf20Sopenharmony_ci i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); 7838c2ecf20Sopenharmony_ci i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); 7848c2ecf20Sopenharmony_ci i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); 7858c2ecf20Sopenharmony_ci i2c_wr8(sd, FS_MUTE, 0x00); 7868c2ecf20Sopenharmony_ci i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); 7878c2ecf20Sopenharmony_ci i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); 7888c2ecf20Sopenharmony_ci i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); 7898c2ecf20Sopenharmony_ci i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); 7908c2ecf20Sopenharmony_ci i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); 7918c2ecf20Sopenharmony_ci i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci mutex_lock(&state->confctl_mutex); 7948c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, CONFCTL, 0xffff, MASK_AUDCHNUM_2 | 7958c2ecf20Sopenharmony_ci MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); 7968c2ecf20Sopenharmony_ci mutex_unlock(&state->confctl_mutex); 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci /* Default settings from REF_02, sheet "Source HDMI" */ 8028c2ecf20Sopenharmony_ci i2c_wr8(sd, PK_INT_MODE, MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | 8038c2ecf20Sopenharmony_ci MASK_ACP_INT_MODE | MASK_VS_INT_MODE | 8048c2ecf20Sopenharmony_ci MASK_SPD_INT_MODE | MASK_MS_INT_MODE | 8058c2ecf20Sopenharmony_ci MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); 8068c2ecf20Sopenharmony_ci i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); 8078c2ecf20Sopenharmony_ci i2c_wr8(sd, NO_PKT_CLR, 0x53); 8088c2ecf20Sopenharmony_ci i2c_wr8(sd, ERR_PK_LIMIT, 0x01); 8098c2ecf20Sopenharmony_ci i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); 8108c2ecf20Sopenharmony_ci i2c_wr8(sd, NO_GDB_LIMIT, 0x10); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic void tc358743_initial_setup(struct v4l2_subdev *sd) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 8168c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = &state->pdata; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* 8198c2ecf20Sopenharmony_ci * IR is not supported by this driver. 8208c2ecf20Sopenharmony_ci * CEC is only enabled if needed. 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_ci i2c_wr16_and_or(sd, SYSCTL, ~(MASK_IRRST | MASK_CECRST), 8238c2ecf20Sopenharmony_ci (MASK_IRRST | MASK_CECRST)); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); 8268c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_TC358743_CEC 8278c2ecf20Sopenharmony_ci tc358743_reset(sd, MASK_CECRST); 8288c2ecf20Sopenharmony_ci#endif 8298c2ecf20Sopenharmony_ci tc358743_sleep_mode(sd, false); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci i2c_wr16(sd, FIFOCTL, pdata->fifo_level); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci tc358743_set_ref_clk(sd); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, 8368c2ecf20Sopenharmony_ci pdata->ddc5v_delay & MASK_DDC5V_MODE); 8378c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci tc358743_set_hdmi_phy(sd); 8408c2ecf20Sopenharmony_ci tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); 8418c2ecf20Sopenharmony_ci tc358743_set_hdmi_audio(sd); 8428c2ecf20Sopenharmony_ci tc358743_set_hdmi_info_frame_mode(sd); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* All CE and IT formats are detected as RGB full range in DVI mode */ 8458c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, 8488c2ecf20Sopenharmony_ci MASK_VOUTCOLORMODE_AUTO); 8498c2ecf20Sopenharmony_ci i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci/* --------------- CEC --------------- */ 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_TC358743_CEC 8558c2ecf20Sopenharmony_cistatic int tc358743_cec_adap_enable(struct cec_adapter *adap, bool enable) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct tc358743_state *state = adap->priv; 8588c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci i2c_wr32(sd, CECIMSK, enable ? MASK_CECTIM | MASK_CECRIM : 0); 8618c2ecf20Sopenharmony_ci i2c_wr32(sd, CECICLR, MASK_CECTICLR | MASK_CECRICLR); 8628c2ecf20Sopenharmony_ci i2c_wr32(sd, CECEN, enable); 8638c2ecf20Sopenharmony_ci if (enable) 8648c2ecf20Sopenharmony_ci i2c_wr32(sd, CECREN, MASK_CECREN); 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int tc358743_cec_adap_monitor_all_enable(struct cec_adapter *adap, 8698c2ecf20Sopenharmony_ci bool enable) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci struct tc358743_state *state = adap->priv; 8728c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 8738c2ecf20Sopenharmony_ci u32 reg; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci reg = i2c_rd32(sd, CECRCTL1); 8768c2ecf20Sopenharmony_ci if (enable) 8778c2ecf20Sopenharmony_ci reg |= MASK_CECOTH; 8788c2ecf20Sopenharmony_ci else 8798c2ecf20Sopenharmony_ci reg &= ~MASK_CECOTH; 8808c2ecf20Sopenharmony_ci i2c_wr32(sd, CECRCTL1, reg); 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic int tc358743_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct tc358743_state *state = adap->priv; 8878c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 8888c2ecf20Sopenharmony_ci unsigned int la = 0; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (log_addr != CEC_LOG_ADDR_INVALID) { 8918c2ecf20Sopenharmony_ci la = i2c_rd32(sd, CECADD); 8928c2ecf20Sopenharmony_ci la |= 1 << log_addr; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci i2c_wr32(sd, CECADD, la); 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic int tc358743_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 8998c2ecf20Sopenharmony_ci u32 signal_free_time, struct cec_msg *msg) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct tc358743_state *state = adap->priv; 9028c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 9038c2ecf20Sopenharmony_ci unsigned int i; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci i2c_wr32(sd, CECTCTL, 9068c2ecf20Sopenharmony_ci (cec_msg_is_broadcast(msg) ? MASK_CECBRD : 0) | 9078c2ecf20Sopenharmony_ci (signal_free_time - 1)); 9088c2ecf20Sopenharmony_ci for (i = 0; i < msg->len; i++) 9098c2ecf20Sopenharmony_ci i2c_wr32(sd, CECTBUF1 + i * 4, 9108c2ecf20Sopenharmony_ci msg->msg[i] | ((i == msg->len - 1) ? MASK_CECTEOM : 0)); 9118c2ecf20Sopenharmony_ci i2c_wr32(sd, CECTEN, MASK_CECTEN); 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic const struct cec_adap_ops tc358743_cec_adap_ops = { 9168c2ecf20Sopenharmony_ci .adap_enable = tc358743_cec_adap_enable, 9178c2ecf20Sopenharmony_ci .adap_log_addr = tc358743_cec_adap_log_addr, 9188c2ecf20Sopenharmony_ci .adap_transmit = tc358743_cec_adap_transmit, 9198c2ecf20Sopenharmony_ci .adap_monitor_all_enable = tc358743_cec_adap_monitor_all_enable, 9208c2ecf20Sopenharmony_ci}; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic void tc358743_cec_handler(struct v4l2_subdev *sd, u16 intstatus, 9238c2ecf20Sopenharmony_ci bool *handled) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 9268c2ecf20Sopenharmony_ci unsigned int cec_rxint, cec_txint; 9278c2ecf20Sopenharmony_ci unsigned int clr = 0; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci cec_rxint = i2c_rd32(sd, CECRSTAT); 9308c2ecf20Sopenharmony_ci cec_txint = i2c_rd32(sd, CECTSTAT); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (intstatus & MASK_CEC_RINT) 9338c2ecf20Sopenharmony_ci clr |= MASK_CECRICLR; 9348c2ecf20Sopenharmony_ci if (intstatus & MASK_CEC_TINT) 9358c2ecf20Sopenharmony_ci clr |= MASK_CECTICLR; 9368c2ecf20Sopenharmony_ci i2c_wr32(sd, CECICLR, clr); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if ((intstatus & MASK_CEC_TINT) && cec_txint) { 9398c2ecf20Sopenharmony_ci if (cec_txint & MASK_CECTIEND) 9408c2ecf20Sopenharmony_ci cec_transmit_attempt_done(state->cec_adap, 9418c2ecf20Sopenharmony_ci CEC_TX_STATUS_OK); 9428c2ecf20Sopenharmony_ci else if (cec_txint & MASK_CECTIAL) 9438c2ecf20Sopenharmony_ci cec_transmit_attempt_done(state->cec_adap, 9448c2ecf20Sopenharmony_ci CEC_TX_STATUS_ARB_LOST); 9458c2ecf20Sopenharmony_ci else if (cec_txint & MASK_CECTIACK) 9468c2ecf20Sopenharmony_ci cec_transmit_attempt_done(state->cec_adap, 9478c2ecf20Sopenharmony_ci CEC_TX_STATUS_NACK); 9488c2ecf20Sopenharmony_ci else if (cec_txint & MASK_CECTIUR) { 9498c2ecf20Sopenharmony_ci /* 9508c2ecf20Sopenharmony_ci * Not sure when this bit is set. Treat 9518c2ecf20Sopenharmony_ci * it as an error for now. 9528c2ecf20Sopenharmony_ci */ 9538c2ecf20Sopenharmony_ci cec_transmit_attempt_done(state->cec_adap, 9548c2ecf20Sopenharmony_ci CEC_TX_STATUS_ERROR); 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci if (handled) 9578c2ecf20Sopenharmony_ci *handled = true; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci if ((intstatus & MASK_CEC_RINT) && 9608c2ecf20Sopenharmony_ci (cec_rxint & MASK_CECRIEND)) { 9618c2ecf20Sopenharmony_ci struct cec_msg msg = {}; 9628c2ecf20Sopenharmony_ci unsigned int i; 9638c2ecf20Sopenharmony_ci unsigned int v; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci v = i2c_rd32(sd, CECRCTR); 9668c2ecf20Sopenharmony_ci msg.len = v & 0x1f; 9678c2ecf20Sopenharmony_ci for (i = 0; i < msg.len; i++) { 9688c2ecf20Sopenharmony_ci v = i2c_rd32(sd, CECRBUF1 + i * 4); 9698c2ecf20Sopenharmony_ci msg.msg[i] = v & 0xff; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci cec_received_msg(state->cec_adap, &msg); 9728c2ecf20Sopenharmony_ci if (handled) 9738c2ecf20Sopenharmony_ci *handled = true; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci i2c_wr16(sd, INTSTATUS, 9768c2ecf20Sopenharmony_ci intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci#endif 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/* --------------- IRQ --------------- */ 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic void tc358743_format_change(struct v4l2_subdev *sd) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 9868c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 9878c2ecf20Sopenharmony_ci const struct v4l2_event tc358743_ev_fmt = { 9888c2ecf20Sopenharmony_ci .type = V4L2_EVENT_SOURCE_CHANGE, 9898c2ecf20Sopenharmony_ci .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 9908c2ecf20Sopenharmony_ci }; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (tc358743_get_detected_timings(sd, &timings)) { 9938c2ecf20Sopenharmony_ci enable_stream(sd, false); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: No signal\n", 9968c2ecf20Sopenharmony_ci __func__); 9978c2ecf20Sopenharmony_ci } else { 9988c2ecf20Sopenharmony_ci if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false)) 9998c2ecf20Sopenharmony_ci enable_stream(sd, false); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (debug) 10028c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, 10038c2ecf20Sopenharmony_ci "tc358743_format_change: New format: ", 10048c2ecf20Sopenharmony_ci &timings, false); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (sd->devnode) 10088c2ecf20Sopenharmony_ci v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic void tc358743_init_interrupts(struct v4l2_subdev *sd) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci u16 i; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* clear interrupt status registers */ 10168c2ecf20Sopenharmony_ci for (i = SYS_INT; i <= KEY_INT; i++) 10178c2ecf20Sopenharmony_ci i2c_wr8(sd, i, 0xff); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci i2c_wr16(sd, INTSTATUS, 0xffff); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void tc358743_enable_interrupts(struct v4l2_subdev *sd, 10238c2ecf20Sopenharmony_ci bool cable_connected) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s: cable connected = %d\n", __func__, 10268c2ecf20Sopenharmony_ci cable_connected); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (cable_connected) { 10298c2ecf20Sopenharmony_ci i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_DVI_DET | 10308c2ecf20Sopenharmony_ci MASK_M_HDMI_DET) & 0xff); 10318c2ecf20Sopenharmony_ci i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); 10328c2ecf20Sopenharmony_ci i2c_wr8(sd, CBIT_INTM, ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | 10338c2ecf20Sopenharmony_ci MASK_M_AF_UNLOCK) & 0xff); 10348c2ecf20Sopenharmony_ci i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); 10358c2ecf20Sopenharmony_ci i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); 10388c2ecf20Sopenharmony_ci i2c_wr8(sd, CLK_INTM, 0xff); 10398c2ecf20Sopenharmony_ci i2c_wr8(sd, CBIT_INTM, 0xff); 10408c2ecf20Sopenharmony_ci i2c_wr8(sd, AUDIO_INTM, 0xff); 10418c2ecf20Sopenharmony_ci i2c_wr8(sd, MISC_INTM, 0xff); 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, 10468c2ecf20Sopenharmony_ci bool *handled) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); 10498c2ecf20Sopenharmony_ci u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci i2c_wr8(sd, AUDIO_INT, audio_int); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s: AUDIO_INT = 0x%02x\n", __func__, audio_int); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci tc358743_s_ctrl_audio_sampling_rate(sd); 10568c2ecf20Sopenharmony_ci tc358743_s_ctrl_audio_present(sd); 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, bool *handled) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: CSI_ERR = 0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, 10678c2ecf20Sopenharmony_ci bool *handled) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); 10708c2ecf20Sopenharmony_ci u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci i2c_wr8(sd, MISC_INT, misc_int); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s: MISC_INT = 0x%02x\n", __func__, misc_int); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (misc_int & MASK_I_SYNC_CHG) { 10778c2ecf20Sopenharmony_ci /* Reset the HDMI PHY to try to trigger proper lock on the 10788c2ecf20Sopenharmony_ci * incoming video format. Erase BKSV to prevent that old keys 10798c2ecf20Sopenharmony_ci * are used when a new source is connected. */ 10808c2ecf20Sopenharmony_ci if (no_sync(sd) || no_signal(sd)) { 10818c2ecf20Sopenharmony_ci tc358743_reset_phy(sd); 10828c2ecf20Sopenharmony_ci tc358743_erase_bksv(sd); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci tc358743_format_change(sd); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci misc_int &= ~MASK_I_SYNC_CHG; 10888c2ecf20Sopenharmony_ci if (handled) 10898c2ecf20Sopenharmony_ci *handled = true; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (misc_int) { 10938c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: Unhandled MISC_INT interrupts: 0x%02x\n", 10948c2ecf20Sopenharmony_ci __func__, misc_int); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, 10998c2ecf20Sopenharmony_ci bool *handled) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); 11028c2ecf20Sopenharmony_ci u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci i2c_wr8(sd, CBIT_INT, cbit_int); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s: CBIT_INT = 0x%02x\n", __func__, cbit_int); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (cbit_int & MASK_I_CBIT_FS) { 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: Audio sample rate changed\n", 11118c2ecf20Sopenharmony_ci __func__); 11128c2ecf20Sopenharmony_ci tc358743_s_ctrl_audio_sampling_rate(sd); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci cbit_int &= ~MASK_I_CBIT_FS; 11158c2ecf20Sopenharmony_ci if (handled) 11168c2ecf20Sopenharmony_ci *handled = true; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: Audio present changed\n", 11228c2ecf20Sopenharmony_ci __func__); 11238c2ecf20Sopenharmony_ci tc358743_s_ctrl_audio_present(sd); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); 11268c2ecf20Sopenharmony_ci if (handled) 11278c2ecf20Sopenharmony_ci *handled = true; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (cbit_int) { 11318c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts: 0x%02x\n", 11328c2ecf20Sopenharmony_ci __func__, cbit_int); 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, bool *handled) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); 11398c2ecf20Sopenharmony_ci u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Bit 7 and bit 6 are set even when they are masked */ 11428c2ecf20Sopenharmony_ci i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s: CLK_INT = 0x%02x\n", __func__, clk_int); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (clk_int & (MASK_I_IN_DE_CHG)) { 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: DE size or position has changed\n", 11498c2ecf20Sopenharmony_ci __func__); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* If the source switch to a new resolution with the same pixel 11528c2ecf20Sopenharmony_ci * frequency as the existing (e.g. 1080p25 -> 720p50), the 11538c2ecf20Sopenharmony_ci * I_SYNC_CHG interrupt is not always triggered, while the 11548c2ecf20Sopenharmony_ci * I_IN_DE_CHG interrupt seems to work fine. Format change 11558c2ecf20Sopenharmony_ci * notifications are only sent when the signal is stable to 11568c2ecf20Sopenharmony_ci * reduce the number of notifications. */ 11578c2ecf20Sopenharmony_ci if (!no_signal(sd) && !no_sync(sd)) 11588c2ecf20Sopenharmony_ci tc358743_format_change(sd); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci clk_int &= ~(MASK_I_IN_DE_CHG); 11618c2ecf20Sopenharmony_ci if (handled) 11628c2ecf20Sopenharmony_ci *handled = true; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (clk_int) { 11668c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: Unhandled CLK_INT interrupts: 0x%02x\n", 11678c2ecf20Sopenharmony_ci __func__, clk_int); 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, bool *handled) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 11748c2ecf20Sopenharmony_ci u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); 11758c2ecf20Sopenharmony_ci u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci i2c_wr8(sd, SYS_INT, sys_int); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, sd, "%s: SYS_INT = 0x%02x\n", __func__, sys_int); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (sys_int & MASK_I_DDC) { 11828c2ecf20Sopenharmony_ci bool tx_5v = tx_5v_power_present(sd); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: Tx 5V power present: %s\n", 11858c2ecf20Sopenharmony_ci __func__, tx_5v ? "yes" : "no"); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (tx_5v) { 11888c2ecf20Sopenharmony_ci tc358743_enable_edid(sd); 11898c2ecf20Sopenharmony_ci } else { 11908c2ecf20Sopenharmony_ci tc358743_enable_interrupts(sd, false); 11918c2ecf20Sopenharmony_ci tc358743_disable_edid(sd); 11928c2ecf20Sopenharmony_ci memset(&state->timings, 0, sizeof(state->timings)); 11938c2ecf20Sopenharmony_ci tc358743_erase_bksv(sd); 11948c2ecf20Sopenharmony_ci tc358743_update_controls(sd); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci sys_int &= ~MASK_I_DDC; 11988c2ecf20Sopenharmony_ci if (handled) 11998c2ecf20Sopenharmony_ci *handled = true; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (sys_int & MASK_I_DVI) { 12038c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: HDMI->DVI change detected\n", 12048c2ecf20Sopenharmony_ci __func__); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* Reset the HDMI PHY to try to trigger proper lock on the 12078c2ecf20Sopenharmony_ci * incoming video format. Erase BKSV to prevent that old keys 12088c2ecf20Sopenharmony_ci * are used when a new source is connected. */ 12098c2ecf20Sopenharmony_ci if (no_sync(sd) || no_signal(sd)) { 12108c2ecf20Sopenharmony_ci tc358743_reset_phy(sd); 12118c2ecf20Sopenharmony_ci tc358743_erase_bksv(sd); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci sys_int &= ~MASK_I_DVI; 12158c2ecf20Sopenharmony_ci if (handled) 12168c2ecf20Sopenharmony_ci *handled = true; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (sys_int & MASK_I_HDMI) { 12208c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: DVI->HDMI change detected\n", 12218c2ecf20Sopenharmony_ci __func__); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ 12248c2ecf20Sopenharmony_ci i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci sys_int &= ~MASK_I_HDMI; 12278c2ecf20Sopenharmony_ci if (handled) 12288c2ecf20Sopenharmony_ci *handled = true; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (sys_int) { 12328c2ecf20Sopenharmony_ci v4l2_err(sd, "%s: Unhandled SYS_INT interrupts: 0x%02x\n", 12338c2ecf20Sopenharmony_ci __func__, sys_int); 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci/* --------------- CORE OPS --------------- */ 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic int tc358743_log_status(struct v4l2_subdev *sd) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 12428c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 12438c2ecf20Sopenharmony_ci uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); 12448c2ecf20Sopenharmony_ci uint16_t sysctl = i2c_rd16(sd, SYSCTL); 12458c2ecf20Sopenharmony_ci u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); 12468c2ecf20Sopenharmony_ci const int deep_color_mode[4] = { 8, 10, 12, 16 }; 12478c2ecf20Sopenharmony_ci static const char * const input_color_space[] = { 12488c2ecf20Sopenharmony_ci "RGB", "YCbCr 601", "opRGB", "YCbCr 709", "NA (4)", 12498c2ecf20Sopenharmony_ci "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", 12508c2ecf20Sopenharmony_ci "NA(10)", "NA(11)", "NA(12)", "opYCC 601"}; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Chip status-----\n"); 12538c2ecf20Sopenharmony_ci v4l2_info(sd, "Chip ID: 0x%02x\n", 12548c2ecf20Sopenharmony_ci (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); 12558c2ecf20Sopenharmony_ci v4l2_info(sd, "Chip revision: 0x%02x\n", 12568c2ecf20Sopenharmony_ci i2c_rd16(sd, CHIPID) & MASK_REVID); 12578c2ecf20Sopenharmony_ci v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", 12588c2ecf20Sopenharmony_ci !!(sysctl & MASK_IRRST), 12598c2ecf20Sopenharmony_ci !!(sysctl & MASK_CECRST), 12608c2ecf20Sopenharmony_ci !!(sysctl & MASK_CTXRST), 12618c2ecf20Sopenharmony_ci !!(sysctl & MASK_HDMIRST)); 12628c2ecf20Sopenharmony_ci v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); 12638c2ecf20Sopenharmony_ci v4l2_info(sd, "Cable detected (+5V power): %s\n", 12648c2ecf20Sopenharmony_ci hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); 12658c2ecf20Sopenharmony_ci v4l2_info(sd, "DDC lines enabled: %s\n", 12668c2ecf20Sopenharmony_ci (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? 12678c2ecf20Sopenharmony_ci "yes" : "no"); 12688c2ecf20Sopenharmony_ci v4l2_info(sd, "Hotplug enabled: %s\n", 12698c2ecf20Sopenharmony_ci (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? 12708c2ecf20Sopenharmony_ci "yes" : "no"); 12718c2ecf20Sopenharmony_ci v4l2_info(sd, "CEC enabled: %s\n", 12728c2ecf20Sopenharmony_ci (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); 12738c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Signal status-----\n"); 12748c2ecf20Sopenharmony_ci v4l2_info(sd, "TMDS signal detected: %s\n", 12758c2ecf20Sopenharmony_ci hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); 12768c2ecf20Sopenharmony_ci v4l2_info(sd, "Stable sync signal: %s\n", 12778c2ecf20Sopenharmony_ci hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); 12788c2ecf20Sopenharmony_ci v4l2_info(sd, "PHY PLL locked: %s\n", 12798c2ecf20Sopenharmony_ci hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); 12808c2ecf20Sopenharmony_ci v4l2_info(sd, "PHY DE detected: %s\n", 12818c2ecf20Sopenharmony_ci hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (tc358743_get_detected_timings(sd, &timings)) { 12848c2ecf20Sopenharmony_ci v4l2_info(sd, "No video detected\n"); 12858c2ecf20Sopenharmony_ci } else { 12868c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, 12878c2ecf20Sopenharmony_ci true); 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, 12908c2ecf20Sopenharmony_ci true); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci v4l2_info(sd, "-----CSI-TX status-----\n"); 12938c2ecf20Sopenharmony_ci v4l2_info(sd, "Lanes needed: %d\n", 12948c2ecf20Sopenharmony_ci tc358743_num_csi_lanes_needed(sd)); 12958c2ecf20Sopenharmony_ci v4l2_info(sd, "Lanes in use: %d\n", 12968c2ecf20Sopenharmony_ci state->csi_lanes_in_use); 12978c2ecf20Sopenharmony_ci v4l2_info(sd, "Waiting for particular sync signal: %s\n", 12988c2ecf20Sopenharmony_ci (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? 12998c2ecf20Sopenharmony_ci "yes" : "no"); 13008c2ecf20Sopenharmony_ci v4l2_info(sd, "Transmit mode: %s\n", 13018c2ecf20Sopenharmony_ci (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? 13028c2ecf20Sopenharmony_ci "yes" : "no"); 13038c2ecf20Sopenharmony_ci v4l2_info(sd, "Receive mode: %s\n", 13048c2ecf20Sopenharmony_ci (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? 13058c2ecf20Sopenharmony_ci "yes" : "no"); 13068c2ecf20Sopenharmony_ci v4l2_info(sd, "Stopped: %s\n", 13078c2ecf20Sopenharmony_ci (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? 13088c2ecf20Sopenharmony_ci "yes" : "no"); 13098c2ecf20Sopenharmony_ci v4l2_info(sd, "Color space: %s\n", 13108c2ecf20Sopenharmony_ci state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? 13118c2ecf20Sopenharmony_ci "YCbCr 422 16-bit" : 13128c2ecf20Sopenharmony_ci state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? 13138c2ecf20Sopenharmony_ci "RGB 888 24-bit" : "Unsupported"); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); 13168c2ecf20Sopenharmony_ci v4l2_info(sd, "HDCP encrypted content: %s\n", 13178c2ecf20Sopenharmony_ci hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); 13188c2ecf20Sopenharmony_ci v4l2_info(sd, "Input color space: %s %s range\n", 13198c2ecf20Sopenharmony_ci input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], 13208c2ecf20Sopenharmony_ci (vi_status3 & MASK_LIMITED) ? "limited" : "full"); 13218c2ecf20Sopenharmony_ci if (!is_hdmi(sd)) 13228c2ecf20Sopenharmony_ci return 0; 13238c2ecf20Sopenharmony_ci v4l2_info(sd, "AV Mute: %s\n", hdmi_sys_status & MASK_S_AVMUTE ? "on" : 13248c2ecf20Sopenharmony_ci "off"); 13258c2ecf20Sopenharmony_ci v4l2_info(sd, "Deep color mode: %d-bits per channel\n", 13268c2ecf20Sopenharmony_ci deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & 13278c2ecf20Sopenharmony_ci MASK_S_DEEPCOLOR) >> 2]); 13288c2ecf20Sopenharmony_ci print_avi_infoframe(sd); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci return 0; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 13348c2ecf20Sopenharmony_cistatic void tc358743_print_register_map(struct v4l2_subdev *sd) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci v4l2_info(sd, "0x0000-0x00FF: Global Control Register\n"); 13378c2ecf20Sopenharmony_ci v4l2_info(sd, "0x0100-0x01FF: CSI2-TX PHY Register\n"); 13388c2ecf20Sopenharmony_ci v4l2_info(sd, "0x0200-0x03FF: CSI2-TX PPI Register\n"); 13398c2ecf20Sopenharmony_ci v4l2_info(sd, "0x0400-0x05FF: Reserved\n"); 13408c2ecf20Sopenharmony_ci v4l2_info(sd, "0x0600-0x06FF: CEC Register\n"); 13418c2ecf20Sopenharmony_ci v4l2_info(sd, "0x0700-0x84FF: Reserved\n"); 13428c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8500-0x85FF: HDMIRX System Control Register\n"); 13438c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8600-0x86FF: HDMIRX Audio Control Register\n"); 13448c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8700-0x87FF: HDMIRX InfoFrame packet data Register\n"); 13458c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8800-0x88FF: HDMIRX HDCP Port Register\n"); 13468c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8900-0x89FF: HDMIRX Video Output Port & 3D Register\n"); 13478c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8A00-0x8BFF: Reserved\n"); 13488c2ecf20Sopenharmony_ci v4l2_info(sd, "0x8C00-0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); 13498c2ecf20Sopenharmony_ci v4l2_info(sd, "0x9000-0x90FF: HDMIRX GBD Extraction Control\n"); 13508c2ecf20Sopenharmony_ci v4l2_info(sd, "0x9100-0x92FF: HDMIRX GBD RAM read\n"); 13518c2ecf20Sopenharmony_ci v4l2_info(sd, "0x9300- : Reserved\n"); 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_cistatic int tc358743_get_reg_size(u16 address) 13558c2ecf20Sopenharmony_ci{ 13568c2ecf20Sopenharmony_ci /* REF_01 p. 66-72 */ 13578c2ecf20Sopenharmony_ci if (address <= 0x00ff) 13588c2ecf20Sopenharmony_ci return 2; 13598c2ecf20Sopenharmony_ci else if ((address >= 0x0100) && (address <= 0x06FF)) 13608c2ecf20Sopenharmony_ci return 4; 13618c2ecf20Sopenharmony_ci else if ((address >= 0x0700) && (address <= 0x84ff)) 13628c2ecf20Sopenharmony_ci return 2; 13638c2ecf20Sopenharmony_ci else 13648c2ecf20Sopenharmony_ci return 1; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic int tc358743_g_register(struct v4l2_subdev *sd, 13688c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci if (reg->reg > 0xffff) { 13718c2ecf20Sopenharmony_ci tc358743_print_register_map(sd); 13728c2ecf20Sopenharmony_ci return -EINVAL; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci reg->size = tc358743_get_reg_size(reg->reg); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci reg->val = i2c_rdreg(sd, reg->reg, reg->size); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci return 0; 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cistatic int tc358743_s_register(struct v4l2_subdev *sd, 13838c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci if (reg->reg > 0xffff) { 13868c2ecf20Sopenharmony_ci tc358743_print_register_map(sd); 13878c2ecf20Sopenharmony_ci return -EINVAL; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* It should not be possible for the user to enable HDCP with a simple 13918c2ecf20Sopenharmony_ci * v4l2-dbg command. 13928c2ecf20Sopenharmony_ci * 13938c2ecf20Sopenharmony_ci * DO NOT REMOVE THIS unless all other issues with HDCP have been 13948c2ecf20Sopenharmony_ci * resolved. 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_ci if (reg->reg == HDCP_MODE || 13978c2ecf20Sopenharmony_ci reg->reg == HDCP_REG1 || 13988c2ecf20Sopenharmony_ci reg->reg == HDCP_REG2 || 13998c2ecf20Sopenharmony_ci reg->reg == HDCP_REG3 || 14008c2ecf20Sopenharmony_ci reg->reg == BCAPS) 14018c2ecf20Sopenharmony_ci return 0; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci i2c_wrreg(sd, (u16)reg->reg, reg->val, 14048c2ecf20Sopenharmony_ci tc358743_get_reg_size(reg->reg)); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return 0; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci#endif 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_cistatic int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci u16 intstatus = i2c_rd16(sd, INTSTATUS); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: IntStatus = 0x%04x\n", __func__, intstatus); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (intstatus & MASK_HDMI_INT) { 14178c2ecf20Sopenharmony_ci u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); 14188c2ecf20Sopenharmony_ci u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (hdmi_int0 & MASK_I_MISC) 14218c2ecf20Sopenharmony_ci tc358743_hdmi_misc_int_handler(sd, handled); 14228c2ecf20Sopenharmony_ci if (hdmi_int1 & MASK_I_CBIT) 14238c2ecf20Sopenharmony_ci tc358743_hdmi_cbit_int_handler(sd, handled); 14248c2ecf20Sopenharmony_ci if (hdmi_int1 & MASK_I_CLK) 14258c2ecf20Sopenharmony_ci tc358743_hdmi_clk_int_handler(sd, handled); 14268c2ecf20Sopenharmony_ci if (hdmi_int1 & MASK_I_SYS) 14278c2ecf20Sopenharmony_ci tc358743_hdmi_sys_int_handler(sd, handled); 14288c2ecf20Sopenharmony_ci if (hdmi_int1 & MASK_I_AUD) 14298c2ecf20Sopenharmony_ci tc358743_hdmi_audio_int_handler(sd, handled); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); 14328c2ecf20Sopenharmony_ci intstatus &= ~MASK_HDMI_INT; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_TC358743_CEC 14368c2ecf20Sopenharmony_ci if (intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)) { 14378c2ecf20Sopenharmony_ci tc358743_cec_handler(sd, intstatus, handled); 14388c2ecf20Sopenharmony_ci i2c_wr16(sd, INTSTATUS, 14398c2ecf20Sopenharmony_ci intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); 14408c2ecf20Sopenharmony_ci intstatus &= ~(MASK_CEC_RINT | MASK_CEC_TINT); 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci#endif 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (intstatus & MASK_CSI_INT) { 14458c2ecf20Sopenharmony_ci u32 csi_int = i2c_rd32(sd, CSI_INT); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (csi_int & MASK_INTER) 14488c2ecf20Sopenharmony_ci tc358743_csi_err_int_handler(sd, handled); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci intstatus = i2c_rd16(sd, INTSTATUS); 14548c2ecf20Sopenharmony_ci if (intstatus) { 14558c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 14568c2ecf20Sopenharmony_ci "%s: Unhandled IntStatus interrupts: 0x%02x\n", 14578c2ecf20Sopenharmony_ci __func__, intstatus); 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci return 0; 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_cistatic irqreturn_t tc358743_irq_handler(int irq, void *dev_id) 14648c2ecf20Sopenharmony_ci{ 14658c2ecf20Sopenharmony_ci struct tc358743_state *state = dev_id; 14668c2ecf20Sopenharmony_ci bool handled = false; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci tc358743_isr(&state->sd, 0, &handled); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci return handled ? IRQ_HANDLED : IRQ_NONE; 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic void tc358743_irq_poll_timer(struct timer_list *t) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct tc358743_state *state = from_timer(state, t, timer); 14768c2ecf20Sopenharmony_ci unsigned int msecs; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci schedule_work(&state->work_i2c_poll); 14798c2ecf20Sopenharmony_ci /* 14808c2ecf20Sopenharmony_ci * If CEC is present, then we need to poll more frequently, 14818c2ecf20Sopenharmony_ci * otherwise we will miss CEC messages. 14828c2ecf20Sopenharmony_ci */ 14838c2ecf20Sopenharmony_ci msecs = state->cec_adap ? POLL_INTERVAL_CEC_MS : POLL_INTERVAL_MS; 14848c2ecf20Sopenharmony_ci mod_timer(&state->timer, jiffies + msecs_to_jiffies(msecs)); 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic void tc358743_work_i2c_poll(struct work_struct *work) 14888c2ecf20Sopenharmony_ci{ 14898c2ecf20Sopenharmony_ci struct tc358743_state *state = container_of(work, 14908c2ecf20Sopenharmony_ci struct tc358743_state, work_i2c_poll); 14918c2ecf20Sopenharmony_ci bool handled; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci tc358743_isr(&state->sd, 0, &handled); 14948c2ecf20Sopenharmony_ci} 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_cistatic int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, 14978c2ecf20Sopenharmony_ci struct v4l2_event_subscription *sub) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci switch (sub->type) { 15008c2ecf20Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 15018c2ecf20Sopenharmony_ci return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); 15028c2ecf20Sopenharmony_ci case V4L2_EVENT_CTRL: 15038c2ecf20Sopenharmony_ci return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); 15048c2ecf20Sopenharmony_ci default: 15058c2ecf20Sopenharmony_ci return -EINVAL; 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci/* --------------- VIDEO OPS --------------- */ 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci *status = 0; 15148c2ecf20Sopenharmony_ci *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; 15158c2ecf20Sopenharmony_ci *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci return 0; 15208c2ecf20Sopenharmony_ci} 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_cistatic int tc358743_s_dv_timings(struct v4l2_subdev *sd, 15238c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 15248c2ecf20Sopenharmony_ci{ 15258c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (!timings) 15288c2ecf20Sopenharmony_ci return -EINVAL; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (debug) 15318c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ", 15328c2ecf20Sopenharmony_ci timings, false); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) { 15358c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); 15368c2ecf20Sopenharmony_ci return 0; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, 15408c2ecf20Sopenharmony_ci &tc358743_timings_cap, NULL, NULL)) { 15418c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); 15428c2ecf20Sopenharmony_ci return -ERANGE; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci state->timings = *timings; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci enable_stream(sd, false); 15488c2ecf20Sopenharmony_ci tc358743_set_pll(sd); 15498c2ecf20Sopenharmony_ci tc358743_set_csi(sd); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci return 0; 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_cistatic int tc358743_g_dv_timings(struct v4l2_subdev *sd, 15558c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci *timings = state->timings; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci return 0; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic int tc358743_enum_dv_timings(struct v4l2_subdev *sd, 15658c2ecf20Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci if (timings->pad != 0) 15688c2ecf20Sopenharmony_ci return -EINVAL; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci return v4l2_enum_dv_timings_cap(timings, 15718c2ecf20Sopenharmony_ci &tc358743_timings_cap, NULL, NULL); 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cistatic int tc358743_query_dv_timings(struct v4l2_subdev *sd, 15758c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 15768c2ecf20Sopenharmony_ci{ 15778c2ecf20Sopenharmony_ci int ret; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci ret = tc358743_get_detected_timings(sd, timings); 15808c2ecf20Sopenharmony_ci if (ret) 15818c2ecf20Sopenharmony_ci return ret; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (debug) 15848c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", 15858c2ecf20Sopenharmony_ci timings, false); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, 15888c2ecf20Sopenharmony_ci &tc358743_timings_cap, NULL, NULL)) { 15898c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); 15908c2ecf20Sopenharmony_ci return -ERANGE; 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci return 0; 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic int tc358743_dv_timings_cap(struct v4l2_subdev *sd, 15978c2ecf20Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci if (cap->pad != 0) 16008c2ecf20Sopenharmony_ci return -EINVAL; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci *cap = tc358743_timings_cap; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci return 0; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic int tc358743_get_mbus_config(struct v4l2_subdev *sd, 16088c2ecf20Sopenharmony_ci unsigned int pad, 16098c2ecf20Sopenharmony_ci struct v4l2_mbus_config *cfg) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci cfg->type = V4L2_MBUS_CSI2_DPHY; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci /* Support for non-continuous CSI-2 clock is missing in the driver */ 16168c2ecf20Sopenharmony_ci cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci switch (state->csi_lanes_in_use) { 16198c2ecf20Sopenharmony_ci case 1: 16208c2ecf20Sopenharmony_ci cfg->flags |= V4L2_MBUS_CSI2_1_LANE; 16218c2ecf20Sopenharmony_ci break; 16228c2ecf20Sopenharmony_ci case 2: 16238c2ecf20Sopenharmony_ci cfg->flags |= V4L2_MBUS_CSI2_2_LANE; 16248c2ecf20Sopenharmony_ci break; 16258c2ecf20Sopenharmony_ci case 3: 16268c2ecf20Sopenharmony_ci cfg->flags |= V4L2_MBUS_CSI2_3_LANE; 16278c2ecf20Sopenharmony_ci break; 16288c2ecf20Sopenharmony_ci case 4: 16298c2ecf20Sopenharmony_ci cfg->flags |= V4L2_MBUS_CSI2_4_LANE; 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci default: 16328c2ecf20Sopenharmony_ci return -EINVAL; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci return 0; 16368c2ecf20Sopenharmony_ci} 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_cistatic int tc358743_s_stream(struct v4l2_subdev *sd, int enable) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci enable_stream(sd, enable); 16418c2ecf20Sopenharmony_ci if (!enable) { 16428c2ecf20Sopenharmony_ci /* Put all lanes in LP-11 state (STOPSTATE) */ 16438c2ecf20Sopenharmony_ci tc358743_set_csi(sd); 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci return 0; 16478c2ecf20Sopenharmony_ci} 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci/* --------------- PAD OPS --------------- */ 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int tc358743_enum_mbus_code(struct v4l2_subdev *sd, 16528c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 16538c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci switch (code->index) { 16568c2ecf20Sopenharmony_ci case 0: 16578c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_RGB888_1X24; 16588c2ecf20Sopenharmony_ci break; 16598c2ecf20Sopenharmony_ci case 1: 16608c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_UYVY8_1X16; 16618c2ecf20Sopenharmony_ci break; 16628c2ecf20Sopenharmony_ci default: 16638c2ecf20Sopenharmony_ci return -EINVAL; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci return 0; 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_cistatic int tc358743_get_fmt(struct v4l2_subdev *sd, 16698c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 16708c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 16738c2ecf20Sopenharmony_ci u8 vi_rep = i2c_rd8(sd, VI_REP); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (format->pad != 0) 16768c2ecf20Sopenharmony_ci return -EINVAL; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci format->format.code = state->mbus_fmt_code; 16798c2ecf20Sopenharmony_ci format->format.width = state->timings.bt.width; 16808c2ecf20Sopenharmony_ci format->format.height = state->timings.bt.height; 16818c2ecf20Sopenharmony_ci format->format.field = V4L2_FIELD_NONE; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci switch (vi_rep & MASK_VOUT_COLOR_SEL) { 16848c2ecf20Sopenharmony_ci case MASK_VOUT_COLOR_RGB_FULL: 16858c2ecf20Sopenharmony_ci case MASK_VOUT_COLOR_RGB_LIMITED: 16868c2ecf20Sopenharmony_ci format->format.colorspace = V4L2_COLORSPACE_SRGB; 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci case MASK_VOUT_COLOR_601_YCBCR_LIMITED: 16898c2ecf20Sopenharmony_ci case MASK_VOUT_COLOR_601_YCBCR_FULL: 16908c2ecf20Sopenharmony_ci format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; 16918c2ecf20Sopenharmony_ci break; 16928c2ecf20Sopenharmony_ci case MASK_VOUT_COLOR_709_YCBCR_FULL: 16938c2ecf20Sopenharmony_ci case MASK_VOUT_COLOR_709_YCBCR_LIMITED: 16948c2ecf20Sopenharmony_ci format->format.colorspace = V4L2_COLORSPACE_REC709; 16958c2ecf20Sopenharmony_ci break; 16968c2ecf20Sopenharmony_ci default: 16978c2ecf20Sopenharmony_ci format->format.colorspace = 0; 16988c2ecf20Sopenharmony_ci break; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci return 0; 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_cistatic int tc358743_set_fmt(struct v4l2_subdev *sd, 17058c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17068c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 17078c2ecf20Sopenharmony_ci{ 17088c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci u32 code = format->format.code; /* is overwritten by get_fmt */ 17118c2ecf20Sopenharmony_ci int ret = tc358743_get_fmt(sd, cfg, format); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci format->format.code = code; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (ret) 17168c2ecf20Sopenharmony_ci return ret; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci switch (code) { 17198c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 17208c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 17218c2ecf20Sopenharmony_ci break; 17228c2ecf20Sopenharmony_ci default: 17238c2ecf20Sopenharmony_ci return -EINVAL; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) 17278c2ecf20Sopenharmony_ci return 0; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci state->mbus_fmt_code = format->format.code; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci enable_stream(sd, false); 17328c2ecf20Sopenharmony_ci tc358743_set_pll(sd); 17338c2ecf20Sopenharmony_ci tc358743_set_csi(sd); 17348c2ecf20Sopenharmony_ci tc358743_set_csi_color_space(sd); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci return 0; 17378c2ecf20Sopenharmony_ci} 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_cistatic int tc358743_g_edid(struct v4l2_subdev *sd, 17408c2ecf20Sopenharmony_ci struct v4l2_subdev_edid *edid) 17418c2ecf20Sopenharmony_ci{ 17428c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci if (edid->pad != 0) 17478c2ecf20Sopenharmony_ci return -EINVAL; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci if (edid->start_block == 0 && edid->blocks == 0) { 17508c2ecf20Sopenharmony_ci edid->blocks = state->edid_blocks_written; 17518c2ecf20Sopenharmony_ci return 0; 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci if (state->edid_blocks_written == 0) 17558c2ecf20Sopenharmony_ci return -ENODATA; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (edid->start_block >= state->edid_blocks_written || 17588c2ecf20Sopenharmony_ci edid->blocks == 0) 17598c2ecf20Sopenharmony_ci return -EINVAL; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (edid->start_block + edid->blocks > state->edid_blocks_written) 17628c2ecf20Sopenharmony_ci edid->blocks = state->edid_blocks_written - edid->start_block; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, 17658c2ecf20Sopenharmony_ci edid->blocks * EDID_BLOCK_SIZE); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci return 0; 17688c2ecf20Sopenharmony_ci} 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_cistatic int tc358743_s_edid(struct v4l2_subdev *sd, 17718c2ecf20Sopenharmony_ci struct v4l2_subdev_edid *edid) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 17748c2ecf20Sopenharmony_ci u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; 17758c2ecf20Sopenharmony_ci u16 pa; 17768c2ecf20Sopenharmony_ci int err; 17778c2ecf20Sopenharmony_ci int i; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n", 17808c2ecf20Sopenharmony_ci __func__, edid->pad, edid->start_block, edid->blocks); 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci if (edid->pad != 0) 17858c2ecf20Sopenharmony_ci return -EINVAL; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci if (edid->start_block != 0) 17888c2ecf20Sopenharmony_ci return -EINVAL; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci if (edid->blocks > EDID_NUM_BLOCKS_MAX) { 17918c2ecf20Sopenharmony_ci edid->blocks = EDID_NUM_BLOCKS_MAX; 17928c2ecf20Sopenharmony_ci return -E2BIG; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); 17958c2ecf20Sopenharmony_ci err = v4l2_phys_addr_validate(pa, &pa, NULL); 17968c2ecf20Sopenharmony_ci if (err) 17978c2ecf20Sopenharmony_ci return err; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci cec_phys_addr_invalidate(state->cec_adap); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci tc358743_disable_edid(sd); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); 18048c2ecf20Sopenharmony_ci i2c_wr8(sd, EDID_LEN2, edid_len >> 8); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (edid->blocks == 0) { 18078c2ecf20Sopenharmony_ci state->edid_blocks_written = 0; 18088c2ecf20Sopenharmony_ci return 0; 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci for (i = 0; i < edid_len; i += EDID_BLOCK_SIZE) 18128c2ecf20Sopenharmony_ci i2c_wr(sd, EDID_RAM + i, edid->edid + i, EDID_BLOCK_SIZE); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci state->edid_blocks_written = edid->blocks; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci cec_s_phys_addr(state->cec_adap, pa, false); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (tx_5v_power_present(sd)) 18198c2ecf20Sopenharmony_ci tc358743_enable_edid(sd); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci return 0; 18228c2ecf20Sopenharmony_ci} 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops tc358743_core_ops = { 18278c2ecf20Sopenharmony_ci .log_status = tc358743_log_status, 18288c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 18298c2ecf20Sopenharmony_ci .g_register = tc358743_g_register, 18308c2ecf20Sopenharmony_ci .s_register = tc358743_s_register, 18318c2ecf20Sopenharmony_ci#endif 18328c2ecf20Sopenharmony_ci .interrupt_service_routine = tc358743_isr, 18338c2ecf20Sopenharmony_ci .subscribe_event = tc358743_subscribe_event, 18348c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 18358c2ecf20Sopenharmony_ci}; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops tc358743_video_ops = { 18388c2ecf20Sopenharmony_ci .g_input_status = tc358743_g_input_status, 18398c2ecf20Sopenharmony_ci .s_dv_timings = tc358743_s_dv_timings, 18408c2ecf20Sopenharmony_ci .g_dv_timings = tc358743_g_dv_timings, 18418c2ecf20Sopenharmony_ci .query_dv_timings = tc358743_query_dv_timings, 18428c2ecf20Sopenharmony_ci .s_stream = tc358743_s_stream, 18438c2ecf20Sopenharmony_ci}; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops tc358743_pad_ops = { 18468c2ecf20Sopenharmony_ci .enum_mbus_code = tc358743_enum_mbus_code, 18478c2ecf20Sopenharmony_ci .set_fmt = tc358743_set_fmt, 18488c2ecf20Sopenharmony_ci .get_fmt = tc358743_get_fmt, 18498c2ecf20Sopenharmony_ci .get_edid = tc358743_g_edid, 18508c2ecf20Sopenharmony_ci .set_edid = tc358743_s_edid, 18518c2ecf20Sopenharmony_ci .enum_dv_timings = tc358743_enum_dv_timings, 18528c2ecf20Sopenharmony_ci .dv_timings_cap = tc358743_dv_timings_cap, 18538c2ecf20Sopenharmony_ci .get_mbus_config = tc358743_get_mbus_config, 18548c2ecf20Sopenharmony_ci}; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops tc358743_ops = { 18578c2ecf20Sopenharmony_ci .core = &tc358743_core_ops, 18588c2ecf20Sopenharmony_ci .video = &tc358743_video_ops, 18598c2ecf20Sopenharmony_ci .pad = &tc358743_pad_ops, 18608c2ecf20Sopenharmony_ci}; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci/* --------------- CUSTOM CTRLS --------------- */ 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config tc358743_ctrl_audio_sampling_rate = { 18658c2ecf20Sopenharmony_ci .id = TC358743_CID_AUDIO_SAMPLING_RATE, 18668c2ecf20Sopenharmony_ci .name = "Audio sampling rate", 18678c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 18688c2ecf20Sopenharmony_ci .min = 0, 18698c2ecf20Sopenharmony_ci .max = 768000, 18708c2ecf20Sopenharmony_ci .step = 1, 18718c2ecf20Sopenharmony_ci .def = 0, 18728c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_READ_ONLY, 18738c2ecf20Sopenharmony_ci}; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { 18768c2ecf20Sopenharmony_ci .id = TC358743_CID_AUDIO_PRESENT, 18778c2ecf20Sopenharmony_ci .name = "Audio present", 18788c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 18798c2ecf20Sopenharmony_ci .min = 0, 18808c2ecf20Sopenharmony_ci .max = 1, 18818c2ecf20Sopenharmony_ci .step = 1, 18828c2ecf20Sopenharmony_ci .def = 0, 18838c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_READ_ONLY, 18848c2ecf20Sopenharmony_ci}; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci/* --------------- PROBE / REMOVE --------------- */ 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 18898c2ecf20Sopenharmony_cistatic void tc358743_gpio_reset(struct tc358743_state *state) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 18928c2ecf20Sopenharmony_ci gpiod_set_value(state->reset_gpio, 1); 18938c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 18948c2ecf20Sopenharmony_ci gpiod_set_value(state->reset_gpio, 0); 18958c2ecf20Sopenharmony_ci msleep(20); 18968c2ecf20Sopenharmony_ci} 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_cistatic int tc358743_probe_of(struct tc358743_state *state) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci struct device *dev = &state->i2c_client->dev; 19018c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; 19028c2ecf20Sopenharmony_ci struct device_node *ep; 19038c2ecf20Sopenharmony_ci struct clk *refclk; 19048c2ecf20Sopenharmony_ci u32 bps_pr_lane; 19058c2ecf20Sopenharmony_ci int ret; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci refclk = devm_clk_get(dev, "refclk"); 19088c2ecf20Sopenharmony_ci if (IS_ERR(refclk)) { 19098c2ecf20Sopenharmony_ci if (PTR_ERR(refclk) != -EPROBE_DEFER) 19108c2ecf20Sopenharmony_ci dev_err(dev, "failed to get refclk: %ld\n", 19118c2ecf20Sopenharmony_ci PTR_ERR(refclk)); 19128c2ecf20Sopenharmony_ci return PTR_ERR(refclk); 19138c2ecf20Sopenharmony_ci } 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci ep = of_graph_get_next_endpoint(dev->of_node, NULL); 19168c2ecf20Sopenharmony_ci if (!ep) { 19178c2ecf20Sopenharmony_ci dev_err(dev, "missing endpoint node\n"); 19188c2ecf20Sopenharmony_ci return -EINVAL; 19198c2ecf20Sopenharmony_ci } 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); 19228c2ecf20Sopenharmony_ci if (ret) { 19238c2ecf20Sopenharmony_ci dev_err(dev, "failed to parse endpoint\n"); 19248c2ecf20Sopenharmony_ci goto put_node; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || 19288c2ecf20Sopenharmony_ci endpoint.bus.mipi_csi2.num_data_lanes == 0 || 19298c2ecf20Sopenharmony_ci endpoint.nr_of_link_frequencies == 0) { 19308c2ecf20Sopenharmony_ci dev_err(dev, "missing CSI-2 properties in endpoint\n"); 19318c2ecf20Sopenharmony_ci ret = -EINVAL; 19328c2ecf20Sopenharmony_ci goto free_endpoint; 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci if (endpoint.bus.mipi_csi2.num_data_lanes > 4) { 19368c2ecf20Sopenharmony_ci dev_err(dev, "invalid number of lanes\n"); 19378c2ecf20Sopenharmony_ci ret = -EINVAL; 19388c2ecf20Sopenharmony_ci goto free_endpoint; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci state->bus = endpoint.bus.mipi_csi2; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci ret = clk_prepare_enable(refclk); 19448c2ecf20Sopenharmony_ci if (ret) { 19458c2ecf20Sopenharmony_ci dev_err(dev, "Failed! to enable clock\n"); 19468c2ecf20Sopenharmony_ci goto free_endpoint; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci state->pdata.refclk_hz = clk_get_rate(refclk); 19508c2ecf20Sopenharmony_ci state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; 19518c2ecf20Sopenharmony_ci state->pdata.enable_hdcp = false; 19528c2ecf20Sopenharmony_ci /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ 19538c2ecf20Sopenharmony_ci state->pdata.fifo_level = 16; 19548c2ecf20Sopenharmony_ci /* 19558c2ecf20Sopenharmony_ci * The PLL input clock is obtained by dividing refclk by pll_prd. 19568c2ecf20Sopenharmony_ci * It must be between 6 MHz and 40 MHz, lower frequency is better. 19578c2ecf20Sopenharmony_ci */ 19588c2ecf20Sopenharmony_ci switch (state->pdata.refclk_hz) { 19598c2ecf20Sopenharmony_ci case 26000000: 19608c2ecf20Sopenharmony_ci case 27000000: 19618c2ecf20Sopenharmony_ci case 42000000: 19628c2ecf20Sopenharmony_ci state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; 19638c2ecf20Sopenharmony_ci break; 19648c2ecf20Sopenharmony_ci default: 19658c2ecf20Sopenharmony_ci dev_err(dev, "unsupported refclk rate: %u Hz\n", 19668c2ecf20Sopenharmony_ci state->pdata.refclk_hz); 19678c2ecf20Sopenharmony_ci goto disable_clk; 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci /* 19718c2ecf20Sopenharmony_ci * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. 19728c2ecf20Sopenharmony_ci * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. 19738c2ecf20Sopenharmony_ci */ 19748c2ecf20Sopenharmony_ci bps_pr_lane = 2 * endpoint.link_frequencies[0]; 19758c2ecf20Sopenharmony_ci if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { 19768c2ecf20Sopenharmony_ci dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); 19778c2ecf20Sopenharmony_ci ret = -EINVAL; 19788c2ecf20Sopenharmony_ci goto disable_clk; 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ 19828c2ecf20Sopenharmony_ci state->pdata.pll_fbd = bps_pr_lane / 19838c2ecf20Sopenharmony_ci state->pdata.refclk_hz * state->pdata.pll_prd; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* 19868c2ecf20Sopenharmony_ci * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz 19878c2ecf20Sopenharmony_ci * link frequency). In principle it should be possible to calculate 19888c2ecf20Sopenharmony_ci * them based on link frequency and resolution. 19898c2ecf20Sopenharmony_ci */ 19908c2ecf20Sopenharmony_ci if (bps_pr_lane != 594000000U) 19918c2ecf20Sopenharmony_ci dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); 19928c2ecf20Sopenharmony_ci state->pdata.lineinitcnt = 0xe80; 19938c2ecf20Sopenharmony_ci state->pdata.lptxtimecnt = 0x003; 19948c2ecf20Sopenharmony_ci /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ 19958c2ecf20Sopenharmony_ci state->pdata.tclk_headercnt = 0x1403; 19968c2ecf20Sopenharmony_ci state->pdata.tclk_trailcnt = 0x00; 19978c2ecf20Sopenharmony_ci /* ths-preparecnt: 3, ths-zerocnt: 1 */ 19988c2ecf20Sopenharmony_ci state->pdata.ths_headercnt = 0x0103; 19998c2ecf20Sopenharmony_ci state->pdata.twakeup = 0x4882; 20008c2ecf20Sopenharmony_ci state->pdata.tclk_postcnt = 0x008; 20018c2ecf20Sopenharmony_ci state->pdata.ths_trailcnt = 0x2; 20028c2ecf20Sopenharmony_ci state->pdata.hstxvregcnt = 0; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci state->reset_gpio = devm_gpiod_get_optional(dev, "reset", 20058c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 20068c2ecf20Sopenharmony_ci if (IS_ERR(state->reset_gpio)) { 20078c2ecf20Sopenharmony_ci dev_err(dev, "failed to get reset gpio\n"); 20088c2ecf20Sopenharmony_ci ret = PTR_ERR(state->reset_gpio); 20098c2ecf20Sopenharmony_ci goto disable_clk; 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci if (state->reset_gpio) 20138c2ecf20Sopenharmony_ci tc358743_gpio_reset(state); 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci ret = 0; 20168c2ecf20Sopenharmony_ci goto free_endpoint; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_cidisable_clk: 20198c2ecf20Sopenharmony_ci clk_disable_unprepare(refclk); 20208c2ecf20Sopenharmony_cifree_endpoint: 20218c2ecf20Sopenharmony_ci v4l2_fwnode_endpoint_free(&endpoint); 20228c2ecf20Sopenharmony_ciput_node: 20238c2ecf20Sopenharmony_ci of_node_put(ep); 20248c2ecf20Sopenharmony_ci return ret; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci#else 20278c2ecf20Sopenharmony_cistatic inline int tc358743_probe_of(struct tc358743_state *state) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci return -ENODEV; 20308c2ecf20Sopenharmony_ci} 20318c2ecf20Sopenharmony_ci#endif 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_cistatic int tc358743_probe(struct i2c_client *client) 20348c2ecf20Sopenharmony_ci{ 20358c2ecf20Sopenharmony_ci static struct v4l2_dv_timings default_timing = 20368c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_640X480P59_94; 20378c2ecf20Sopenharmony_ci struct tc358743_state *state; 20388c2ecf20Sopenharmony_ci struct tc358743_platform_data *pdata = client->dev.platform_data; 20398c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 20408c2ecf20Sopenharmony_ci u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK; 20418c2ecf20Sopenharmony_ci int err; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 20448c2ecf20Sopenharmony_ci return -EIO; 20458c2ecf20Sopenharmony_ci v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", 20468c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), 20498c2ecf20Sopenharmony_ci GFP_KERNEL); 20508c2ecf20Sopenharmony_ci if (!state) 20518c2ecf20Sopenharmony_ci return -ENOMEM; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci state->i2c_client = client; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci /* platform data */ 20568c2ecf20Sopenharmony_ci if (pdata) { 20578c2ecf20Sopenharmony_ci state->pdata = *pdata; 20588c2ecf20Sopenharmony_ci state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; 20598c2ecf20Sopenharmony_ci } else { 20608c2ecf20Sopenharmony_ci err = tc358743_probe_of(state); 20618c2ecf20Sopenharmony_ci if (err == -ENODEV) 20628c2ecf20Sopenharmony_ci v4l_err(client, "No platform data!\n"); 20638c2ecf20Sopenharmony_ci if (err) 20648c2ecf20Sopenharmony_ci return err; 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci sd = &state->sd; 20688c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &tc358743_ops); 20698c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci /* i2c access */ 20728c2ecf20Sopenharmony_ci if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { 20738c2ecf20Sopenharmony_ci v4l2_info(sd, "not a TC358743 on address 0x%x\n", 20748c2ecf20Sopenharmony_ci client->addr << 1); 20758c2ecf20Sopenharmony_ci return -ENODEV; 20768c2ecf20Sopenharmony_ci } 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci /* control handlers */ 20798c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&state->hdl, 3); 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, 20828c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci /* custom controls */ 20858c2ecf20Sopenharmony_ci state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&state->hdl, 20868c2ecf20Sopenharmony_ci &tc358743_ctrl_audio_sampling_rate, NULL); 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci state->audio_present_ctrl = v4l2_ctrl_new_custom(&state->hdl, 20898c2ecf20Sopenharmony_ci &tc358743_ctrl_audio_present, NULL); 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci sd->ctrl_handler = &state->hdl; 20928c2ecf20Sopenharmony_ci if (state->hdl.error) { 20938c2ecf20Sopenharmony_ci err = state->hdl.error; 20948c2ecf20Sopenharmony_ci goto err_hdl; 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci if (tc358743_update_controls(sd)) { 20988c2ecf20Sopenharmony_ci err = -ENODEV; 20998c2ecf20Sopenharmony_ci goto err_hdl; 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci state->pad.flags = MEDIA_PAD_FL_SOURCE; 21038c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 21048c2ecf20Sopenharmony_ci err = media_entity_pads_init(&sd->entity, 1, &state->pad); 21058c2ecf20Sopenharmony_ci if (err < 0) 21068c2ecf20Sopenharmony_ci goto err_hdl; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci sd->dev = &client->dev; 21118c2ecf20Sopenharmony_ci err = v4l2_async_register_subdev(sd); 21128c2ecf20Sopenharmony_ci if (err < 0) 21138c2ecf20Sopenharmony_ci goto err_hdl; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci mutex_init(&state->confctl_mutex); 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, 21188c2ecf20Sopenharmony_ci tc358743_delayed_work_enable_hotplug); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_TC358743_CEC 21218c2ecf20Sopenharmony_ci state->cec_adap = cec_allocate_adapter(&tc358743_cec_adap_ops, 21228c2ecf20Sopenharmony_ci state, dev_name(&client->dev), 21238c2ecf20Sopenharmony_ci CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL, CEC_MAX_LOG_ADDRS); 21248c2ecf20Sopenharmony_ci if (IS_ERR(state->cec_adap)) { 21258c2ecf20Sopenharmony_ci err = PTR_ERR(state->cec_adap); 21268c2ecf20Sopenharmony_ci goto err_hdl; 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci irq_mask |= MASK_CEC_RMSK | MASK_CEC_TMSK; 21298c2ecf20Sopenharmony_ci#endif 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci tc358743_initial_setup(sd); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci tc358743_s_dv_timings(sd, &default_timing); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci tc358743_set_csi_color_space(sd); 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci tc358743_init_interrupts(sd); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci if (state->i2c_client->irq) { 21408c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(&client->dev, 21418c2ecf20Sopenharmony_ci state->i2c_client->irq, 21428c2ecf20Sopenharmony_ci NULL, tc358743_irq_handler, 21438c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 21448c2ecf20Sopenharmony_ci "tc358743", state); 21458c2ecf20Sopenharmony_ci if (err) 21468c2ecf20Sopenharmony_ci goto err_work_queues; 21478c2ecf20Sopenharmony_ci } else { 21488c2ecf20Sopenharmony_ci INIT_WORK(&state->work_i2c_poll, 21498c2ecf20Sopenharmony_ci tc358743_work_i2c_poll); 21508c2ecf20Sopenharmony_ci timer_setup(&state->timer, tc358743_irq_poll_timer, 0); 21518c2ecf20Sopenharmony_ci state->timer.expires = jiffies + 21528c2ecf20Sopenharmony_ci msecs_to_jiffies(POLL_INTERVAL_MS); 21538c2ecf20Sopenharmony_ci add_timer(&state->timer); 21548c2ecf20Sopenharmony_ci } 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci err = cec_register_adapter(state->cec_adap, &client->dev); 21578c2ecf20Sopenharmony_ci if (err < 0) { 21588c2ecf20Sopenharmony_ci pr_err("%s: failed to register the cec device\n", __func__); 21598c2ecf20Sopenharmony_ci cec_delete_adapter(state->cec_adap); 21608c2ecf20Sopenharmony_ci state->cec_adap = NULL; 21618c2ecf20Sopenharmony_ci goto err_work_queues; 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); 21658c2ecf20Sopenharmony_ci i2c_wr16(sd, INTMASK, ~irq_mask); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci err = v4l2_ctrl_handler_setup(sd->ctrl_handler); 21688c2ecf20Sopenharmony_ci if (err) 21698c2ecf20Sopenharmony_ci goto err_work_queues; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, 21728c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci return 0; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_cierr_work_queues: 21778c2ecf20Sopenharmony_ci cec_unregister_adapter(state->cec_adap); 21788c2ecf20Sopenharmony_ci if (!state->i2c_client->irq) 21798c2ecf20Sopenharmony_ci flush_work(&state->work_i2c_poll); 21808c2ecf20Sopenharmony_ci cancel_delayed_work(&state->delayed_work_enable_hotplug); 21818c2ecf20Sopenharmony_ci mutex_destroy(&state->confctl_mutex); 21828c2ecf20Sopenharmony_cierr_hdl: 21838c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 21848c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 21858c2ecf20Sopenharmony_ci return err; 21868c2ecf20Sopenharmony_ci} 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_cistatic int tc358743_remove(struct i2c_client *client) 21898c2ecf20Sopenharmony_ci{ 21908c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 21918c2ecf20Sopenharmony_ci struct tc358743_state *state = to_state(sd); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci if (!state->i2c_client->irq) { 21948c2ecf20Sopenharmony_ci del_timer_sync(&state->timer); 21958c2ecf20Sopenharmony_ci flush_work(&state->work_i2c_poll); 21968c2ecf20Sopenharmony_ci } 21978c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); 21988c2ecf20Sopenharmony_ci cec_unregister_adapter(state->cec_adap); 21998c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 22008c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 22018c2ecf20Sopenharmony_ci mutex_destroy(&state->confctl_mutex); 22028c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 22038c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci return 0; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistatic const struct i2c_device_id tc358743_id[] = { 22098c2ecf20Sopenharmony_ci {"tc358743", 0}, 22108c2ecf20Sopenharmony_ci {} 22118c2ecf20Sopenharmony_ci}; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tc358743_id); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 22168c2ecf20Sopenharmony_cistatic const struct of_device_id tc358743_of_match[] = { 22178c2ecf20Sopenharmony_ci { .compatible = "toshiba,tc358743" }, 22188c2ecf20Sopenharmony_ci {}, 22198c2ecf20Sopenharmony_ci}; 22208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tc358743_of_match); 22218c2ecf20Sopenharmony_ci#endif 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_cistatic struct i2c_driver tc358743_driver = { 22248c2ecf20Sopenharmony_ci .driver = { 22258c2ecf20Sopenharmony_ci .name = "tc358743", 22268c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(tc358743_of_match), 22278c2ecf20Sopenharmony_ci }, 22288c2ecf20Sopenharmony_ci .probe_new = tc358743_probe, 22298c2ecf20Sopenharmony_ci .remove = tc358743_remove, 22308c2ecf20Sopenharmony_ci .id_table = tc358743_id, 22318c2ecf20Sopenharmony_ci}; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_cimodule_i2c_driver(tc358743_driver); 2234