18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Gateworks Corporation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/delay.h> 68c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 78c2ecf20Sopenharmony_ci#include <linux/i2c.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 178c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 248c2ecf20Sopenharmony_ci#include <media/i2c/tda1997x.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <sound/core.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm.h> 288c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 298c2ecf20Sopenharmony_ci#include <sound/soc.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <dt-bindings/media/tda1997x.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "tda1997x_regs.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define TDA1997X_MBUS_CODES 5 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* debug level */ 388c2ecf20Sopenharmony_cistatic int debug; 398c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-2)"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Audio formats */ 438c2ecf20Sopenharmony_cistatic const char * const audtype_names[] = { 448c2ecf20Sopenharmony_ci "PCM", /* PCM Samples */ 458c2ecf20Sopenharmony_ci "HBR", /* High Bit Rate Audio */ 468c2ecf20Sopenharmony_ci "OBA", /* One-Bit Audio */ 478c2ecf20Sopenharmony_ci "DST" /* Direct Stream Transfer */ 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Audio output port formats */ 518c2ecf20Sopenharmony_cienum audfmt_types { 528c2ecf20Sopenharmony_ci AUDFMT_TYPE_DISABLED = 0, 538c2ecf20Sopenharmony_ci AUDFMT_TYPE_I2S, 548c2ecf20Sopenharmony_ci AUDFMT_TYPE_SPDIF, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_cistatic const char * const audfmt_names[] = { 578c2ecf20Sopenharmony_ci "Disabled", 588c2ecf20Sopenharmony_ci "I2S", 598c2ecf20Sopenharmony_ci "SPDIF", 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Video input formats */ 638c2ecf20Sopenharmony_cistatic const char * const hdmi_colorspace_names[] = { 648c2ecf20Sopenharmony_ci "RGB", "YUV422", "YUV444", "YUV420", "", "", "", "", 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_cistatic const char * const hdmi_colorimetry_names[] = { 678c2ecf20Sopenharmony_ci "", "ITU601", "ITU709", "Extended", 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_cistatic const char * const v4l2_quantization_names[] = { 708c2ecf20Sopenharmony_ci "Default", 718c2ecf20Sopenharmony_ci "Full Range (0-255)", 728c2ecf20Sopenharmony_ci "Limited Range (16-235)", 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Video output port formats */ 768c2ecf20Sopenharmony_cistatic const char * const vidfmt_names[] = { 778c2ecf20Sopenharmony_ci "RGB444/YUV444", /* RGB/YUV444 16bit data bus, 8bpp */ 788c2ecf20Sopenharmony_ci "YUV422 semi-planar", /* YUV422 16bit data base, 8bpp */ 798c2ecf20Sopenharmony_ci "YUV422 CCIR656", /* BT656 (YUV 8bpp 2 clock per pixel) */ 808c2ecf20Sopenharmony_ci "Invalid", 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Colorspace conversion matrices 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistruct color_matrix_coefs { 878c2ecf20Sopenharmony_ci const char *name; 888c2ecf20Sopenharmony_ci /* Input offsets */ 898c2ecf20Sopenharmony_ci s16 offint1; 908c2ecf20Sopenharmony_ci s16 offint2; 918c2ecf20Sopenharmony_ci s16 offint3; 928c2ecf20Sopenharmony_ci /* Coeficients */ 938c2ecf20Sopenharmony_ci s16 p11coef; 948c2ecf20Sopenharmony_ci s16 p12coef; 958c2ecf20Sopenharmony_ci s16 p13coef; 968c2ecf20Sopenharmony_ci s16 p21coef; 978c2ecf20Sopenharmony_ci s16 p22coef; 988c2ecf20Sopenharmony_ci s16 p23coef; 998c2ecf20Sopenharmony_ci s16 p31coef; 1008c2ecf20Sopenharmony_ci s16 p32coef; 1018c2ecf20Sopenharmony_ci s16 p33coef; 1028c2ecf20Sopenharmony_ci /* Output offsets */ 1038c2ecf20Sopenharmony_ci s16 offout1; 1048c2ecf20Sopenharmony_ci s16 offout2; 1058c2ecf20Sopenharmony_ci s16 offout3; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cienum { 1098c2ecf20Sopenharmony_ci ITU709_RGBFULL, 1108c2ecf20Sopenharmony_ci ITU601_RGBFULL, 1118c2ecf20Sopenharmony_ci RGBLIMITED_RGBFULL, 1128c2ecf20Sopenharmony_ci RGBLIMITED_ITU601, 1138c2ecf20Sopenharmony_ci RGBLIMITED_ITU709, 1148c2ecf20Sopenharmony_ci RGBFULL_ITU601, 1158c2ecf20Sopenharmony_ci RGBFULL_ITU709, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* NB: 4096 is 1.0 using fixed point numbers */ 1198c2ecf20Sopenharmony_cistatic const struct color_matrix_coefs conv_matrix[] = { 1208c2ecf20Sopenharmony_ci { 1218c2ecf20Sopenharmony_ci "YUV709 -> RGB full", 1228c2ecf20Sopenharmony_ci -256, -2048, -2048, 1238c2ecf20Sopenharmony_ci 4769, -2183, -873, 1248c2ecf20Sopenharmony_ci 4769, 7343, 0, 1258c2ecf20Sopenharmony_ci 4769, 0, 8652, 1268c2ecf20Sopenharmony_ci 0, 0, 0, 1278c2ecf20Sopenharmony_ci }, 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci "YUV601 -> RGB full", 1308c2ecf20Sopenharmony_ci -256, -2048, -2048, 1318c2ecf20Sopenharmony_ci 4769, -3330, -1602, 1328c2ecf20Sopenharmony_ci 4769, 6538, 0, 1338c2ecf20Sopenharmony_ci 4769, 0, 8264, 1348c2ecf20Sopenharmony_ci 256, 256, 256, 1358c2ecf20Sopenharmony_ci }, 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci "RGB limited -> RGB full", 1388c2ecf20Sopenharmony_ci -256, -256, -256, 1398c2ecf20Sopenharmony_ci 0, 4769, 0, 1408c2ecf20Sopenharmony_ci 0, 0, 4769, 1418c2ecf20Sopenharmony_ci 4769, 0, 0, 1428c2ecf20Sopenharmony_ci 0, 0, 0, 1438c2ecf20Sopenharmony_ci }, 1448c2ecf20Sopenharmony_ci { 1458c2ecf20Sopenharmony_ci "RGB limited -> ITU601", 1468c2ecf20Sopenharmony_ci -256, -256, -256, 1478c2ecf20Sopenharmony_ci 2404, 1225, 467, 1488c2ecf20Sopenharmony_ci -1754, 2095, -341, 1498c2ecf20Sopenharmony_ci -1388, -707, 2095, 1508c2ecf20Sopenharmony_ci 256, 2048, 2048, 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci { 1538c2ecf20Sopenharmony_ci "RGB limited -> ITU709", 1548c2ecf20Sopenharmony_ci -256, -256, -256, 1558c2ecf20Sopenharmony_ci 2918, 867, 295, 1568c2ecf20Sopenharmony_ci -1894, 2087, -190, 1578c2ecf20Sopenharmony_ci -1607, -477, 2087, 1588c2ecf20Sopenharmony_ci 256, 2048, 2048, 1598c2ecf20Sopenharmony_ci }, 1608c2ecf20Sopenharmony_ci { 1618c2ecf20Sopenharmony_ci "RGB full -> ITU601", 1628c2ecf20Sopenharmony_ci 0, 0, 0, 1638c2ecf20Sopenharmony_ci 2065, 1052, 401, 1648c2ecf20Sopenharmony_ci -1506, 1799, -293, 1658c2ecf20Sopenharmony_ci -1192, -607, 1799, 1668c2ecf20Sopenharmony_ci 256, 2048, 2048, 1678c2ecf20Sopenharmony_ci }, 1688c2ecf20Sopenharmony_ci { 1698c2ecf20Sopenharmony_ci "RGB full -> ITU709", 1708c2ecf20Sopenharmony_ci 0, 0, 0, 1718c2ecf20Sopenharmony_ci 2506, 745, 253, 1728c2ecf20Sopenharmony_ci -1627, 1792, -163, 1738c2ecf20Sopenharmony_ci -1380, -410, 1792, 1748c2ecf20Sopenharmony_ci 256, 2048, 2048, 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap = { 1798c2ecf20Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 1808c2ecf20Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 1818c2ecf20Sopenharmony_ci .reserved = { 0 }, 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci V4L2_INIT_BT_TIMINGS( 1848c2ecf20Sopenharmony_ci 640, 1920, /* min/max width */ 1858c2ecf20Sopenharmony_ci 350, 1200, /* min/max height */ 1868c2ecf20Sopenharmony_ci 13000000, 165000000, /* min/max pixelclock */ 1878c2ecf20Sopenharmony_ci /* standards */ 1888c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 1898c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, 1908c2ecf20Sopenharmony_ci /* capabilities */ 1918c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE | 1928c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_REDUCED_BLANKING | 1938c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_CUSTOM 1948c2ecf20Sopenharmony_ci ) 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* regulator supplies */ 1988c2ecf20Sopenharmony_cistatic const char * const tda1997x_supply_name[] = { 1998c2ecf20Sopenharmony_ci "DOVDD", /* Digital I/O supply */ 2008c2ecf20Sopenharmony_ci "DVDD", /* Digital Core supply */ 2018c2ecf20Sopenharmony_ci "AVDD", /* Analog supply */ 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#define TDA1997X_NUM_SUPPLIES ARRAY_SIZE(tda1997x_supply_name) 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cienum tda1997x_type { 2078c2ecf20Sopenharmony_ci TDA19971, 2088c2ecf20Sopenharmony_ci TDA19973, 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cienum tda1997x_hdmi_pads { 2128c2ecf20Sopenharmony_ci TDA1997X_PAD_SOURCE, 2138c2ecf20Sopenharmony_ci TDA1997X_NUM_PADS, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistruct tda1997x_chip_info { 2178c2ecf20Sopenharmony_ci enum tda1997x_type type; 2188c2ecf20Sopenharmony_ci const char *name; 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistruct tda1997x_state { 2228c2ecf20Sopenharmony_ci const struct tda1997x_chip_info *info; 2238c2ecf20Sopenharmony_ci struct tda1997x_platform_data pdata; 2248c2ecf20Sopenharmony_ci struct i2c_client *client; 2258c2ecf20Sopenharmony_ci struct i2c_client *client_cec; 2268c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 2278c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[TDA1997X_NUM_SUPPLIES]; 2288c2ecf20Sopenharmony_ci struct media_pad pads[TDA1997X_NUM_PADS]; 2298c2ecf20Sopenharmony_ci struct mutex lock; 2308c2ecf20Sopenharmony_ci struct mutex page_lock; 2318c2ecf20Sopenharmony_ci char page; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* detected info from chip */ 2348c2ecf20Sopenharmony_ci int chip_revision; 2358c2ecf20Sopenharmony_ci char port_30bit; 2368c2ecf20Sopenharmony_ci char output_2p5; 2378c2ecf20Sopenharmony_ci char tmdsb_clk; 2388c2ecf20Sopenharmony_ci char tmdsb_soc; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* status info */ 2418c2ecf20Sopenharmony_ci char hdmi_status; 2428c2ecf20Sopenharmony_ci char mptrw_in_progress; 2438c2ecf20Sopenharmony_ci char activity_status; 2448c2ecf20Sopenharmony_ci char input_detect[2]; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* video */ 2478c2ecf20Sopenharmony_ci struct hdmi_avi_infoframe avi_infoframe; 2488c2ecf20Sopenharmony_ci struct v4l2_hdmi_colorimetry colorimetry; 2498c2ecf20Sopenharmony_ci u32 rgb_quantization_range; 2508c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 2518c2ecf20Sopenharmony_ci int fps; 2528c2ecf20Sopenharmony_ci const struct color_matrix_coefs *conv; 2538c2ecf20Sopenharmony_ci u32 mbus_codes[TDA1997X_MBUS_CODES]; /* available modes */ 2548c2ecf20Sopenharmony_ci u32 mbus_code; /* current mode */ 2558c2ecf20Sopenharmony_ci u8 vid_fmt; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* controls */ 2588c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 2598c2ecf20Sopenharmony_ci struct v4l2_ctrl *detect_tx_5v_ctrl; 2608c2ecf20Sopenharmony_ci struct v4l2_ctrl *rgb_quantization_range_ctrl; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* audio */ 2638c2ecf20Sopenharmony_ci u8 audio_ch_alloc; 2648c2ecf20Sopenharmony_ci int audio_samplerate; 2658c2ecf20Sopenharmony_ci int audio_channels; 2668c2ecf20Sopenharmony_ci int audio_samplesize; 2678c2ecf20Sopenharmony_ci int audio_type; 2688c2ecf20Sopenharmony_ci struct mutex audio_lock; 2698c2ecf20Sopenharmony_ci struct snd_pcm_substream *audio_stream; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* EDID */ 2728c2ecf20Sopenharmony_ci struct { 2738c2ecf20Sopenharmony_ci u8 edid[256]; 2748c2ecf20Sopenharmony_ci u32 present; 2758c2ecf20Sopenharmony_ci unsigned int blocks; 2768c2ecf20Sopenharmony_ci } edid; 2778c2ecf20Sopenharmony_ci struct delayed_work delayed_work_enable_hpd; 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const struct v4l2_event tda1997x_ev_fmt = { 2818c2ecf20Sopenharmony_ci .type = V4L2_EVENT_SOURCE_CHANGE, 2828c2ecf20Sopenharmony_ci .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct tda1997x_chip_info tda1997x_chip_info[] = { 2868c2ecf20Sopenharmony_ci [TDA19971] = { 2878c2ecf20Sopenharmony_ci .type = TDA19971, 2888c2ecf20Sopenharmony_ci .name = "tda19971", 2898c2ecf20Sopenharmony_ci }, 2908c2ecf20Sopenharmony_ci [TDA19973] = { 2918c2ecf20Sopenharmony_ci .type = TDA19973, 2928c2ecf20Sopenharmony_ci .name = "tda19973", 2938c2ecf20Sopenharmony_ci }, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic inline struct tda1997x_state *to_state(struct v4l2_subdev *sd) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci return container_of(sd, struct tda1997x_state, sd); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct tda1997x_state, hdl)->sd; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 3098c2ecf20Sopenharmony_ci int val; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(state->client_cec, reg); 3128c2ecf20Sopenharmony_ci if (val < 0) { 3138c2ecf20Sopenharmony_ci v4l_err(state->client, "read reg error: reg=%2x\n", reg); 3148c2ecf20Sopenharmony_ci val = -1; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return val; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 3238c2ecf20Sopenharmony_ci int ret = 0; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(state->client_cec, reg, val); 3268c2ecf20Sopenharmony_ci if (ret < 0) { 3278c2ecf20Sopenharmony_ci v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", 3288c2ecf20Sopenharmony_ci reg, val); 3298c2ecf20Sopenharmony_ci ret = -1; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3368c2ecf20Sopenharmony_ci * I2C transfer 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int tda1997x_setpage(struct v4l2_subdev *sd, u8 page) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 3428c2ecf20Sopenharmony_ci int ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (state->page != page) { 3458c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(state->client, 3468c2ecf20Sopenharmony_ci REG_CURPAGE_00H, page); 3478c2ecf20Sopenharmony_ci if (ret < 0) { 3488c2ecf20Sopenharmony_ci v4l_err(state->client, 3498c2ecf20Sopenharmony_ci "write reg error:reg=%2x,val=%2x\n", 3508c2ecf20Sopenharmony_ci REG_CURPAGE_00H, page); 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci state->page = page; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic inline int io_read(struct v4l2_subdev *sd, u16 reg) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 3618c2ecf20Sopenharmony_ci int val; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mutex_lock(&state->page_lock); 3648c2ecf20Sopenharmony_ci if (tda1997x_setpage(sd, reg >> 8)) { 3658c2ecf20Sopenharmony_ci val = -1; 3668c2ecf20Sopenharmony_ci goto out; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(state->client, reg&0xff); 3708c2ecf20Sopenharmony_ci if (val < 0) { 3718c2ecf20Sopenharmony_ci v4l_err(state->client, "read reg error: reg=%2x\n", reg & 0xff); 3728c2ecf20Sopenharmony_ci val = -1; 3738c2ecf20Sopenharmony_ci goto out; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ciout: 3778c2ecf20Sopenharmony_ci mutex_unlock(&state->page_lock); 3788c2ecf20Sopenharmony_ci return val; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic inline long io_read16(struct v4l2_subdev *sd, u16 reg) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int val; 3848c2ecf20Sopenharmony_ci long lval = 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci val = io_read(sd, reg); 3878c2ecf20Sopenharmony_ci if (val < 0) 3888c2ecf20Sopenharmony_ci return val; 3898c2ecf20Sopenharmony_ci lval |= (val << 8); 3908c2ecf20Sopenharmony_ci val = io_read(sd, reg + 1); 3918c2ecf20Sopenharmony_ci if (val < 0) 3928c2ecf20Sopenharmony_ci return val; 3938c2ecf20Sopenharmony_ci lval |= val; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return lval; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic inline long io_read24(struct v4l2_subdev *sd, u16 reg) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci int val; 4018c2ecf20Sopenharmony_ci long lval = 0; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci val = io_read(sd, reg); 4048c2ecf20Sopenharmony_ci if (val < 0) 4058c2ecf20Sopenharmony_ci return val; 4068c2ecf20Sopenharmony_ci lval |= (val << 16); 4078c2ecf20Sopenharmony_ci val = io_read(sd, reg + 1); 4088c2ecf20Sopenharmony_ci if (val < 0) 4098c2ecf20Sopenharmony_ci return val; 4108c2ecf20Sopenharmony_ci lval |= (val << 8); 4118c2ecf20Sopenharmony_ci val = io_read(sd, reg + 2); 4128c2ecf20Sopenharmony_ci if (val < 0) 4138c2ecf20Sopenharmony_ci return val; 4148c2ecf20Sopenharmony_ci lval |= val; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return lval; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci int i; 4228c2ecf20Sopenharmony_ci int sz = 0; 4238c2ecf20Sopenharmony_ci int val; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 4268c2ecf20Sopenharmony_ci val = io_read(sd, reg + i); 4278c2ecf20Sopenharmony_ci if (val < 0) 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci data[i] = val; 4308c2ecf20Sopenharmony_ci sz++; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return sz; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int io_write(struct v4l2_subdev *sd, u16 reg, u8 val) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 4398c2ecf20Sopenharmony_ci s32 ret = 0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci mutex_lock(&state->page_lock); 4428c2ecf20Sopenharmony_ci if (tda1997x_setpage(sd, reg >> 8)) { 4438c2ecf20Sopenharmony_ci ret = -1; 4448c2ecf20Sopenharmony_ci goto out; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(state->client, reg & 0xff, val); 4488c2ecf20Sopenharmony_ci if (ret < 0) { 4498c2ecf20Sopenharmony_ci v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", 4508c2ecf20Sopenharmony_ci reg&0xff, val); 4518c2ecf20Sopenharmony_ci ret = -1; 4528c2ecf20Sopenharmony_ci goto out; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ciout: 4568c2ecf20Sopenharmony_ci mutex_unlock(&state->page_lock); 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci int ret; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = io_write(sd, reg, (val >> 8) & 0xff); 4658c2ecf20Sopenharmony_ci if (ret < 0) 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci ret = io_write(sd, reg + 1, val & 0xff); 4688c2ecf20Sopenharmony_ci if (ret < 0) 4698c2ecf20Sopenharmony_ci return ret; 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci int ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = io_write(sd, reg, (val >> 16) & 0xff); 4788c2ecf20Sopenharmony_ci if (ret < 0) 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci ret = io_write(sd, reg + 1, (val >> 8) & 0xff); 4818c2ecf20Sopenharmony_ci if (ret < 0) 4828c2ecf20Sopenharmony_ci return ret; 4838c2ecf20Sopenharmony_ci ret = io_write(sd, reg + 2, val & 0xff); 4848c2ecf20Sopenharmony_ci if (ret < 0) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 4908c2ecf20Sopenharmony_ci * Hotplug 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cienum hpd_mode { 4948c2ecf20Sopenharmony_ci HPD_LOW_BP, /* HPD low and pulse of at least 100ms */ 4958c2ecf20Sopenharmony_ci HPD_LOW_OTHER, /* HPD low and pulse of at least 100ms */ 4968c2ecf20Sopenharmony_ci HPD_HIGH_BP, /* HIGH */ 4978c2ecf20Sopenharmony_ci HPD_HIGH_OTHER, 4988c2ecf20Sopenharmony_ci HPD_PULSE, /* HPD low pulse */ 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* manual HPD (Hot Plug Detect) control */ 5028c2ecf20Sopenharmony_cistatic int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci u8 hpd_auto, hpd_pwr, hpd_man; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci hpd_auto = io_read(sd, REG_HPD_AUTO_CTRL); 5078c2ecf20Sopenharmony_ci hpd_pwr = io_read(sd, REG_HPD_POWER); 5088c2ecf20Sopenharmony_ci hpd_man = io_read(sd, REG_HPD_MAN_CTRL); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* mask out unused bits */ 5118c2ecf20Sopenharmony_ci hpd_man &= (HPD_MAN_CTRL_HPD_PULSE | 5128c2ecf20Sopenharmony_ci HPD_MAN_CTRL_5VEN | 5138c2ecf20Sopenharmony_ci HPD_MAN_CTRL_HPD_B | 5148c2ecf20Sopenharmony_ci HPD_MAN_CTRL_HPD_A); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci switch (mode) { 5178c2ecf20Sopenharmony_ci /* HPD low and pulse of at least 100ms */ 5188c2ecf20Sopenharmony_ci case HPD_LOW_BP: 5198c2ecf20Sopenharmony_ci /* hpd_bp=0 */ 5208c2ecf20Sopenharmony_ci hpd_pwr &= ~HPD_POWER_BP_MASK; 5218c2ecf20Sopenharmony_ci /* disable HPD_A and HPD_B */ 5228c2ecf20Sopenharmony_ci hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); 5238c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_POWER, hpd_pwr); 5248c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_MAN_CTRL, hpd_man); 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci /* HPD high */ 5278c2ecf20Sopenharmony_ci case HPD_HIGH_BP: 5288c2ecf20Sopenharmony_ci /* hpd_bp=1 */ 5298c2ecf20Sopenharmony_ci hpd_pwr &= ~HPD_POWER_BP_MASK; 5308c2ecf20Sopenharmony_ci hpd_pwr |= 1 << HPD_POWER_BP_SHIFT; 5318c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_POWER, hpd_pwr); 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci /* HPD low and pulse of at least 100ms */ 5348c2ecf20Sopenharmony_ci case HPD_LOW_OTHER: 5358c2ecf20Sopenharmony_ci /* disable HPD_A and HPD_B */ 5368c2ecf20Sopenharmony_ci hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); 5378c2ecf20Sopenharmony_ci /* hp_other=0 */ 5388c2ecf20Sopenharmony_ci hpd_auto &= ~HPD_AUTO_HP_OTHER; 5398c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); 5408c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_MAN_CTRL, hpd_man); 5418c2ecf20Sopenharmony_ci break; 5428c2ecf20Sopenharmony_ci /* HPD high */ 5438c2ecf20Sopenharmony_ci case HPD_HIGH_OTHER: 5448c2ecf20Sopenharmony_ci hpd_auto |= HPD_AUTO_HP_OTHER; 5458c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); 5468c2ecf20Sopenharmony_ci break; 5478c2ecf20Sopenharmony_ci /* HPD low pulse */ 5488c2ecf20Sopenharmony_ci case HPD_PULSE: 5498c2ecf20Sopenharmony_ci /* disable HPD_A and HPD_B */ 5508c2ecf20Sopenharmony_ci hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); 5518c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_MAN_CTRL, hpd_man); 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic void tda1997x_delayed_work_enable_hpd(struct work_struct *work) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 5618c2ecf20Sopenharmony_ci struct tda1997x_state *state = container_of(dwork, 5628c2ecf20Sopenharmony_ci struct tda1997x_state, 5638c2ecf20Sopenharmony_ci delayed_work_enable_hpd); 5648c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, sd, "%s:\n", __func__); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Set HPD high */ 5698c2ecf20Sopenharmony_ci tda1997x_manual_hpd(sd, HPD_HIGH_OTHER); 5708c2ecf20Sopenharmony_ci tda1997x_manual_hpd(sd, HPD_HIGH_BP); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci state->edid.present = 1; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void tda1997x_disable_edid(struct v4l2_subdev *sd) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s\n", __func__); 5808c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->delayed_work_enable_hpd); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Set HPD low */ 5838c2ecf20Sopenharmony_ci tda1997x_manual_hpd(sd, HPD_LOW_BP); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic void tda1997x_enable_edid(struct v4l2_subdev *sd) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s\n", __func__); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Enable hotplug after 100ms */ 5938c2ecf20Sopenharmony_ci schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 5978c2ecf20Sopenharmony_ci * Signal Control 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci/* 6018c2ecf20Sopenharmony_ci * configure vid_fmt based on mbus_code 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cistatic int 6048c2ecf20Sopenharmony_citda1997x_setup_format(struct tda1997x_state *state, u32 code) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s code=0x%x\n", __func__, code); 6078c2ecf20Sopenharmony_ci switch (code) { 6088c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB121212_1X36: 6098c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 6108c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUV12_1X36: 6118c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUV8_1X24: 6128c2ecf20Sopenharmony_ci state->vid_fmt = OF_FMT_444; 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY12_1X24: 6158c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY10_1X20: 6168c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 6178c2ecf20Sopenharmony_ci state->vid_fmt = OF_FMT_422_SMPT; 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY12_2X12: 6208c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY10_2X10: 6218c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 6228c2ecf20Sopenharmony_ci state->vid_fmt = OF_FMT_422_CCIR; 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci default: 6258c2ecf20Sopenharmony_ci v4l_err(state->client, "incompatible format (0x%x)\n", code); 6268c2ecf20Sopenharmony_ci return -EINVAL; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s code=0x%x fmt=%s\n", __func__, 6298c2ecf20Sopenharmony_ci code, vidfmt_names[state->vid_fmt]); 6308c2ecf20Sopenharmony_ci state->mbus_code = code; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* 6368c2ecf20Sopenharmony_ci * The color conversion matrix will convert between the colorimetry of the 6378c2ecf20Sopenharmony_ci * HDMI input to the desired output format RGB|YUV. RGB output is to be 6388c2ecf20Sopenharmony_ci * full-range and YUV is to be limited range. 6398c2ecf20Sopenharmony_ci * 6408c2ecf20Sopenharmony_ci * RGB full-range uses values from 0 to 255 which is recommended on a monitor 6418c2ecf20Sopenharmony_ci * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is 6428c2ecf20Sopenharmony_ci * typically recommended on a TV. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_cistatic void 6458c2ecf20Sopenharmony_citda1997x_configure_csc(struct v4l2_subdev *sd) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 6488c2ecf20Sopenharmony_ci struct hdmi_avi_infoframe *avi = &state->avi_infoframe; 6498c2ecf20Sopenharmony_ci struct v4l2_hdmi_colorimetry *c = &state->colorimetry; 6508c2ecf20Sopenharmony_ci /* Blanking code values depend on output colorspace (RGB or YUV) */ 6518c2ecf20Sopenharmony_ci struct blanking_codes { 6528c2ecf20Sopenharmony_ci s16 code_gy; 6538c2ecf20Sopenharmony_ci s16 code_bu; 6548c2ecf20Sopenharmony_ci s16 code_rv; 6558c2ecf20Sopenharmony_ci }; 6568c2ecf20Sopenharmony_ci static const struct blanking_codes rgb_blanking = { 64, 64, 64 }; 6578c2ecf20Sopenharmony_ci static const struct blanking_codes yuv_blanking = { 64, 512, 512 }; 6588c2ecf20Sopenharmony_ci const struct blanking_codes *blanking_codes = NULL; 6598c2ecf20Sopenharmony_ci u8 reg; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "input:%s quant:%s output:%s\n", 6628c2ecf20Sopenharmony_ci hdmi_colorspace_names[avi->colorspace], 6638c2ecf20Sopenharmony_ci v4l2_quantization_names[c->quantization], 6648c2ecf20Sopenharmony_ci vidfmt_names[state->vid_fmt]); 6658c2ecf20Sopenharmony_ci state->conv = NULL; 6668c2ecf20Sopenharmony_ci switch (state->vid_fmt) { 6678c2ecf20Sopenharmony_ci /* RGB output */ 6688c2ecf20Sopenharmony_ci case OF_FMT_444: 6698c2ecf20Sopenharmony_ci blanking_codes = &rgb_blanking; 6708c2ecf20Sopenharmony_ci if (c->colorspace == V4L2_COLORSPACE_SRGB) { 6718c2ecf20Sopenharmony_ci if (c->quantization == V4L2_QUANTIZATION_LIM_RANGE) 6728c2ecf20Sopenharmony_ci state->conv = &conv_matrix[RGBLIMITED_RGBFULL]; 6738c2ecf20Sopenharmony_ci } else { 6748c2ecf20Sopenharmony_ci if (c->colorspace == V4L2_COLORSPACE_REC709) 6758c2ecf20Sopenharmony_ci state->conv = &conv_matrix[ITU709_RGBFULL]; 6768c2ecf20Sopenharmony_ci else if (c->colorspace == V4L2_COLORSPACE_SMPTE170M) 6778c2ecf20Sopenharmony_ci state->conv = &conv_matrix[ITU601_RGBFULL]; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* YUV output */ 6828c2ecf20Sopenharmony_ci case OF_FMT_422_SMPT: /* semi-planar */ 6838c2ecf20Sopenharmony_ci case OF_FMT_422_CCIR: /* CCIR656 */ 6848c2ecf20Sopenharmony_ci blanking_codes = &yuv_blanking; 6858c2ecf20Sopenharmony_ci if ((c->colorspace == V4L2_COLORSPACE_SRGB) && 6868c2ecf20Sopenharmony_ci (c->quantization == V4L2_QUANTIZATION_FULL_RANGE)) { 6878c2ecf20Sopenharmony_ci if (state->timings.bt.height <= 576) 6888c2ecf20Sopenharmony_ci state->conv = &conv_matrix[RGBFULL_ITU601]; 6898c2ecf20Sopenharmony_ci else 6908c2ecf20Sopenharmony_ci state->conv = &conv_matrix[RGBFULL_ITU709]; 6918c2ecf20Sopenharmony_ci } else if ((c->colorspace == V4L2_COLORSPACE_SRGB) && 6928c2ecf20Sopenharmony_ci (c->quantization == V4L2_QUANTIZATION_LIM_RANGE)) { 6938c2ecf20Sopenharmony_ci if (state->timings.bt.height <= 576) 6948c2ecf20Sopenharmony_ci state->conv = &conv_matrix[RGBLIMITED_ITU601]; 6958c2ecf20Sopenharmony_ci else 6968c2ecf20Sopenharmony_ci state->conv = &conv_matrix[RGBLIMITED_ITU709]; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (state->conv) { 7028c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s\n", 7038c2ecf20Sopenharmony_ci state->conv->name); 7048c2ecf20Sopenharmony_ci /* enable matrix conversion */ 7058c2ecf20Sopenharmony_ci reg = io_read(sd, REG_VDP_CTRL); 7068c2ecf20Sopenharmony_ci reg &= ~VDP_CTRL_MATRIX_BP; 7078c2ecf20Sopenharmony_ci io_write(sd, REG_VDP_CTRL, reg); 7088c2ecf20Sopenharmony_ci /* offset inputs */ 7098c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 0, state->conv->offint1); 7108c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 2, state->conv->offint2); 7118c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 4, state->conv->offint3); 7128c2ecf20Sopenharmony_ci /* coefficients */ 7138c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 6, state->conv->p11coef); 7148c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 8, state->conv->p12coef); 7158c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 10, state->conv->p13coef); 7168c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 12, state->conv->p21coef); 7178c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 14, state->conv->p22coef); 7188c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 16, state->conv->p23coef); 7198c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 18, state->conv->p31coef); 7208c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 20, state->conv->p32coef); 7218c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 22, state->conv->p33coef); 7228c2ecf20Sopenharmony_ci /* offset outputs */ 7238c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 24, state->conv->offout1); 7248c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 26, state->conv->offout2); 7258c2ecf20Sopenharmony_ci io_write16(sd, REG_VDP_MATRIX + 28, state->conv->offout3); 7268c2ecf20Sopenharmony_ci } else { 7278c2ecf20Sopenharmony_ci /* disable matrix conversion */ 7288c2ecf20Sopenharmony_ci reg = io_read(sd, REG_VDP_CTRL); 7298c2ecf20Sopenharmony_ci reg |= VDP_CTRL_MATRIX_BP; 7308c2ecf20Sopenharmony_ci io_write(sd, REG_VDP_CTRL, reg); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* SetBlankingCodes */ 7348c2ecf20Sopenharmony_ci if (blanking_codes) { 7358c2ecf20Sopenharmony_ci io_write16(sd, REG_BLK_GY, blanking_codes->code_gy); 7368c2ecf20Sopenharmony_ci io_write16(sd, REG_BLK_BU, blanking_codes->code_bu); 7378c2ecf20Sopenharmony_ci io_write16(sd, REG_BLK_RV, blanking_codes->code_rv); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/* Configure frame detection window and VHREF timing generator */ 7428c2ecf20Sopenharmony_cistatic void 7438c2ecf20Sopenharmony_citda1997x_configure_vhref(struct v4l2_subdev *sd) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 7468c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt = &state->timings.bt; 7478c2ecf20Sopenharmony_ci int width, lines; 7488c2ecf20Sopenharmony_ci u16 href_start, href_end; 7498c2ecf20Sopenharmony_ci u16 vref_f1_start, vref_f2_start; 7508c2ecf20Sopenharmony_ci u8 vref_f1_width, vref_f2_width; 7518c2ecf20Sopenharmony_ci u8 field_polarity; 7528c2ecf20Sopenharmony_ci u16 fieldref_f1_start, fieldref_f2_start; 7538c2ecf20Sopenharmony_ci u8 reg; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci href_start = bt->hbackporch + bt->hsync + 1; 7568c2ecf20Sopenharmony_ci href_end = href_start + bt->width; 7578c2ecf20Sopenharmony_ci vref_f1_start = bt->height + bt->vbackporch + bt->vsync + 7588c2ecf20Sopenharmony_ci bt->il_vbackporch + bt->il_vsync + 7598c2ecf20Sopenharmony_ci bt->il_vfrontporch; 7608c2ecf20Sopenharmony_ci vref_f1_width = bt->vbackporch + bt->vsync + bt->vfrontporch; 7618c2ecf20Sopenharmony_ci vref_f2_start = 0; 7628c2ecf20Sopenharmony_ci vref_f2_width = 0; 7638c2ecf20Sopenharmony_ci fieldref_f1_start = 0; 7648c2ecf20Sopenharmony_ci fieldref_f2_start = 0; 7658c2ecf20Sopenharmony_ci if (bt->interlaced) { 7668c2ecf20Sopenharmony_ci vref_f2_start = (bt->height / 2) + 7678c2ecf20Sopenharmony_ci (bt->il_vbackporch + bt->il_vsync - 1); 7688c2ecf20Sopenharmony_ci vref_f2_width = bt->il_vbackporch + bt->il_vsync + 7698c2ecf20Sopenharmony_ci bt->il_vfrontporch; 7708c2ecf20Sopenharmony_ci fieldref_f2_start = vref_f2_start + bt->il_vfrontporch + 7718c2ecf20Sopenharmony_ci fieldref_f1_start; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci field_polarity = 0; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci width = V4L2_DV_BT_FRAME_WIDTH(bt); 7768c2ecf20Sopenharmony_ci lines = V4L2_DV_BT_FRAME_HEIGHT(bt); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * Configure Frame Detection Window: 7808c2ecf20Sopenharmony_ci * horiz area where the VHREF module consider a VSYNC a new frame 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci io_write16(sd, REG_FDW_S, 0x2ef); /* start position */ 7838c2ecf20Sopenharmony_ci io_write16(sd, REG_FDW_E, 0x141); /* end position */ 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* Set Pixel And Line Counters */ 7868c2ecf20Sopenharmony_ci if (state->chip_revision == 0) 7878c2ecf20Sopenharmony_ci io_write16(sd, REG_PXCNT_PR, 4); 7888c2ecf20Sopenharmony_ci else 7898c2ecf20Sopenharmony_ci io_write16(sd, REG_PXCNT_PR, 1); 7908c2ecf20Sopenharmony_ci io_write16(sd, REG_PXCNT_NPIX, width & MASK_VHREF); 7918c2ecf20Sopenharmony_ci io_write16(sd, REG_LCNT_PR, 1); 7928c2ecf20Sopenharmony_ci io_write16(sd, REG_LCNT_NLIN, lines & MASK_VHREF); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* 7958c2ecf20Sopenharmony_ci * Configure the VHRef timing generator responsible for rebuilding all 7968c2ecf20Sopenharmony_ci * horiz and vert synch and ref signals from its input allowing auto 7978c2ecf20Sopenharmony_ci * detection algorithms and forcing predefined modes (480i & 576i) 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_ci reg = VHREF_STD_DET_OFF << VHREF_STD_DET_SHIFT; 8008c2ecf20Sopenharmony_ci io_write(sd, REG_VHREF_CTRL, reg); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* 8038c2ecf20Sopenharmony_ci * Configure the VHRef timing values. In case the VHREF generator has 8048c2ecf20Sopenharmony_ci * been configured in manual mode, this will allow to manually set all 8058c2ecf20Sopenharmony_ci * horiz and vert ref values (non-active pixel areas) of the generator 8068c2ecf20Sopenharmony_ci * and allows setting the frame reference params. 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_ci /* horizontal reference start/end */ 8098c2ecf20Sopenharmony_ci io_write16(sd, REG_HREF_S, href_start & MASK_VHREF); 8108c2ecf20Sopenharmony_ci io_write16(sd, REG_HREF_E, href_end & MASK_VHREF); 8118c2ecf20Sopenharmony_ci /* vertical reference f1 start/end */ 8128c2ecf20Sopenharmony_ci io_write16(sd, REG_VREF_F1_S, vref_f1_start & MASK_VHREF); 8138c2ecf20Sopenharmony_ci io_write(sd, REG_VREF_F1_WIDTH, vref_f1_width); 8148c2ecf20Sopenharmony_ci /* vertical reference f2 start/end */ 8158c2ecf20Sopenharmony_ci io_write16(sd, REG_VREF_F2_S, vref_f2_start & MASK_VHREF); 8168c2ecf20Sopenharmony_ci io_write(sd, REG_VREF_F2_WIDTH, vref_f2_width); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* F1/F2 FREF, field polarity */ 8198c2ecf20Sopenharmony_ci reg = fieldref_f1_start & MASK_VHREF; 8208c2ecf20Sopenharmony_ci reg |= field_polarity << 8; 8218c2ecf20Sopenharmony_ci io_write16(sd, REG_FREF_F1_S, reg); 8228c2ecf20Sopenharmony_ci reg = fieldref_f2_start & MASK_VHREF; 8238c2ecf20Sopenharmony_ci io_write16(sd, REG_FREF_F2_S, reg); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci/* Configure Video Output port signals */ 8278c2ecf20Sopenharmony_cistatic int 8288c2ecf20Sopenharmony_citda1997x_configure_vidout(struct tda1997x_state *state) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 8318c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata = &state->pdata; 8328c2ecf20Sopenharmony_ci u8 prefilter; 8338c2ecf20Sopenharmony_ci u8 reg; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* Configure pixel clock generator: delay, polarity, rate */ 8368c2ecf20Sopenharmony_ci reg = (state->vid_fmt == OF_FMT_422_CCIR) ? 8378c2ecf20Sopenharmony_ci PCLK_SEL_X2 : PCLK_SEL_X1; 8388c2ecf20Sopenharmony_ci reg |= pdata->vidout_delay_pclk << PCLK_DELAY_SHIFT; 8398c2ecf20Sopenharmony_ci reg |= pdata->vidout_inv_pclk << PCLK_INV_SHIFT; 8408c2ecf20Sopenharmony_ci io_write(sd, REG_PCLK, reg); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* Configure pre-filter */ 8438c2ecf20Sopenharmony_ci prefilter = 0; /* filters off */ 8448c2ecf20Sopenharmony_ci /* YUV422 mode requires conversion */ 8458c2ecf20Sopenharmony_ci if ((state->vid_fmt == OF_FMT_422_SMPT) || 8468c2ecf20Sopenharmony_ci (state->vid_fmt == OF_FMT_422_CCIR)) { 8478c2ecf20Sopenharmony_ci /* 2/7 taps for Rv and Bu */ 8488c2ecf20Sopenharmony_ci prefilter = FILTERS_CTRL_2_7TAP << FILTERS_CTRL_BU_SHIFT | 8498c2ecf20Sopenharmony_ci FILTERS_CTRL_2_7TAP << FILTERS_CTRL_RV_SHIFT; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci io_write(sd, REG_FILTERS_CTRL, prefilter); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* Configure video port */ 8548c2ecf20Sopenharmony_ci reg = state->vid_fmt & OF_FMT_MASK; 8558c2ecf20Sopenharmony_ci if (state->vid_fmt == OF_FMT_422_CCIR) 8568c2ecf20Sopenharmony_ci reg |= (OF_BLK | OF_TRC); 8578c2ecf20Sopenharmony_ci reg |= OF_VP_ENABLE; 8588c2ecf20Sopenharmony_ci io_write(sd, REG_OF, reg); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* Configure formatter and conversions */ 8618c2ecf20Sopenharmony_ci reg = io_read(sd, REG_VDP_CTRL); 8628c2ecf20Sopenharmony_ci /* pre-filter is needed unless (REG_FILTERS_CTRL == 0) */ 8638c2ecf20Sopenharmony_ci if (!prefilter) 8648c2ecf20Sopenharmony_ci reg |= VDP_CTRL_PREFILTER_BP; 8658c2ecf20Sopenharmony_ci else 8668c2ecf20Sopenharmony_ci reg &= ~VDP_CTRL_PREFILTER_BP; 8678c2ecf20Sopenharmony_ci /* formatter is needed for YUV422 and for trc/blc codes */ 8688c2ecf20Sopenharmony_ci if (state->vid_fmt == OF_FMT_444) 8698c2ecf20Sopenharmony_ci reg |= VDP_CTRL_FORMATTER_BP; 8708c2ecf20Sopenharmony_ci /* formatter and compdel needed for timing/blanking codes */ 8718c2ecf20Sopenharmony_ci else 8728c2ecf20Sopenharmony_ci reg &= ~(VDP_CTRL_FORMATTER_BP | VDP_CTRL_COMPDEL_BP); 8738c2ecf20Sopenharmony_ci /* activate compdel for small sync delays */ 8748c2ecf20Sopenharmony_ci if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4)) 8758c2ecf20Sopenharmony_ci reg &= ~VDP_CTRL_COMPDEL_BP; 8768c2ecf20Sopenharmony_ci io_write(sd, REG_VDP_CTRL, reg); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* Configure DE output signal: delay, polarity, and source */ 8798c2ecf20Sopenharmony_ci reg = pdata->vidout_delay_de << DE_FREF_DELAY_SHIFT | 8808c2ecf20Sopenharmony_ci pdata->vidout_inv_de << DE_FREF_INV_SHIFT | 8818c2ecf20Sopenharmony_ci pdata->vidout_sel_de << DE_FREF_SEL_SHIFT; 8828c2ecf20Sopenharmony_ci io_write(sd, REG_DE_FREF, reg); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* Configure HS/HREF output signal: delay, polarity, and source */ 8858c2ecf20Sopenharmony_ci if (state->vid_fmt != OF_FMT_422_CCIR) { 8868c2ecf20Sopenharmony_ci reg = pdata->vidout_delay_hs << HS_HREF_DELAY_SHIFT | 8878c2ecf20Sopenharmony_ci pdata->vidout_inv_hs << HS_HREF_INV_SHIFT | 8888c2ecf20Sopenharmony_ci pdata->vidout_sel_hs << HS_HREF_SEL_SHIFT; 8898c2ecf20Sopenharmony_ci } else 8908c2ecf20Sopenharmony_ci reg = HS_HREF_SEL_NONE << HS_HREF_SEL_SHIFT; 8918c2ecf20Sopenharmony_ci io_write(sd, REG_HS_HREF, reg); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* Configure VS/VREF output signal: delay, polarity, and source */ 8948c2ecf20Sopenharmony_ci if (state->vid_fmt != OF_FMT_422_CCIR) { 8958c2ecf20Sopenharmony_ci reg = pdata->vidout_delay_vs << VS_VREF_DELAY_SHIFT | 8968c2ecf20Sopenharmony_ci pdata->vidout_inv_vs << VS_VREF_INV_SHIFT | 8978c2ecf20Sopenharmony_ci pdata->vidout_sel_vs << VS_VREF_SEL_SHIFT; 8988c2ecf20Sopenharmony_ci } else 8998c2ecf20Sopenharmony_ci reg = VS_VREF_SEL_NONE << VS_VREF_SEL_SHIFT; 9008c2ecf20Sopenharmony_ci io_write(sd, REG_VS_VREF, reg); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return 0; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/* Configure Audio output port signals */ 9068c2ecf20Sopenharmony_cistatic int 9078c2ecf20Sopenharmony_citda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 9108c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata = &state->pdata; 9118c2ecf20Sopenharmony_ci bool sp_used_by_fifo = true; 9128c2ecf20Sopenharmony_ci u8 reg; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (!pdata->audout_format) 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* channel assignment (CEA-861-D Table 20) */ 9188c2ecf20Sopenharmony_ci io_write(sd, REG_AUDIO_PATH, channel_assignment); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci /* Audio output configuration */ 9218c2ecf20Sopenharmony_ci reg = 0; 9228c2ecf20Sopenharmony_ci switch (pdata->audout_format) { 9238c2ecf20Sopenharmony_ci case AUDFMT_TYPE_I2S: 9248c2ecf20Sopenharmony_ci reg |= AUDCFG_BUS_I2S << AUDCFG_BUS_SHIFT; 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci case AUDFMT_TYPE_SPDIF: 9278c2ecf20Sopenharmony_ci reg |= AUDCFG_BUS_SPDIF << AUDCFG_BUS_SHIFT; 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci switch (state->audio_type) { 9318c2ecf20Sopenharmony_ci case AUDCFG_TYPE_PCM: 9328c2ecf20Sopenharmony_ci reg |= AUDCFG_TYPE_PCM << AUDCFG_TYPE_SHIFT; 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci case AUDCFG_TYPE_OBA: 9358c2ecf20Sopenharmony_ci reg |= AUDCFG_TYPE_OBA << AUDCFG_TYPE_SHIFT; 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci case AUDCFG_TYPE_DST: 9388c2ecf20Sopenharmony_ci reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT; 9398c2ecf20Sopenharmony_ci sp_used_by_fifo = false; 9408c2ecf20Sopenharmony_ci break; 9418c2ecf20Sopenharmony_ci case AUDCFG_TYPE_HBR: 9428c2ecf20Sopenharmony_ci reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT; 9438c2ecf20Sopenharmony_ci if (pdata->audout_layout == 1) { 9448c2ecf20Sopenharmony_ci /* demuxed via AP0:AP3 */ 9458c2ecf20Sopenharmony_ci reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT; 9468c2ecf20Sopenharmony_ci if (pdata->audout_format == AUDFMT_TYPE_SPDIF) 9478c2ecf20Sopenharmony_ci sp_used_by_fifo = false; 9488c2ecf20Sopenharmony_ci } else { 9498c2ecf20Sopenharmony_ci /* straight via AP0 */ 9508c2ecf20Sopenharmony_ci reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci if (pdata->audout_width == 32) 9558c2ecf20Sopenharmony_ci reg |= AUDCFG_I2SW_32 << AUDCFG_I2SW_SHIFT; 9568c2ecf20Sopenharmony_ci else 9578c2ecf20Sopenharmony_ci reg |= AUDCFG_I2SW_16 << AUDCFG_I2SW_SHIFT; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* automatic hardware mute */ 9608c2ecf20Sopenharmony_ci if (pdata->audio_auto_mute) 9618c2ecf20Sopenharmony_ci reg |= AUDCFG_AUTO_MUTE_EN; 9628c2ecf20Sopenharmony_ci /* clock polarity */ 9638c2ecf20Sopenharmony_ci if (pdata->audout_invert_clk) 9648c2ecf20Sopenharmony_ci reg |= AUDCFG_CLK_INVERT; 9658c2ecf20Sopenharmony_ci io_write(sd, REG_AUDCFG, reg); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* audio layout */ 9688c2ecf20Sopenharmony_ci reg = (pdata->audout_layout) ? AUDIO_LAYOUT_LAYOUT1 : 0; 9698c2ecf20Sopenharmony_ci if (!pdata->audout_layoutauto) 9708c2ecf20Sopenharmony_ci reg |= AUDIO_LAYOUT_MANUAL; 9718c2ecf20Sopenharmony_ci if (sp_used_by_fifo) 9728c2ecf20Sopenharmony_ci reg |= AUDIO_LAYOUT_SP_FLAG; 9738c2ecf20Sopenharmony_ci io_write(sd, REG_AUDIO_LAYOUT, reg); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* FIFO Latency value */ 9768c2ecf20Sopenharmony_ci io_write(sd, REG_FIFO_LATENCY_VAL, 0x80); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* Audio output port config */ 9798c2ecf20Sopenharmony_ci if (sp_used_by_fifo) { 9808c2ecf20Sopenharmony_ci reg = AUDIO_OUT_ENABLE_AP0; 9818c2ecf20Sopenharmony_ci if (channel_assignment >= 0x01) 9828c2ecf20Sopenharmony_ci reg |= AUDIO_OUT_ENABLE_AP1; 9838c2ecf20Sopenharmony_ci if (channel_assignment >= 0x04) 9848c2ecf20Sopenharmony_ci reg |= AUDIO_OUT_ENABLE_AP2; 9858c2ecf20Sopenharmony_ci if (channel_assignment >= 0x0c) 9868c2ecf20Sopenharmony_ci reg |= AUDIO_OUT_ENABLE_AP3; 9878c2ecf20Sopenharmony_ci /* specific cases where AP1 is not used */ 9888c2ecf20Sopenharmony_ci if ((channel_assignment == 0x04) 9898c2ecf20Sopenharmony_ci || (channel_assignment == 0x08) 9908c2ecf20Sopenharmony_ci || (channel_assignment == 0x0c) 9918c2ecf20Sopenharmony_ci || (channel_assignment == 0x10) 9928c2ecf20Sopenharmony_ci || (channel_assignment == 0x14) 9938c2ecf20Sopenharmony_ci || (channel_assignment == 0x18) 9948c2ecf20Sopenharmony_ci || (channel_assignment == 0x1c)) 9958c2ecf20Sopenharmony_ci reg &= ~AUDIO_OUT_ENABLE_AP1; 9968c2ecf20Sopenharmony_ci /* specific cases where AP2 is not used */ 9978c2ecf20Sopenharmony_ci if ((channel_assignment >= 0x14) 9988c2ecf20Sopenharmony_ci && (channel_assignment <= 0x17)) 9998c2ecf20Sopenharmony_ci reg &= ~AUDIO_OUT_ENABLE_AP2; 10008c2ecf20Sopenharmony_ci } else { 10018c2ecf20Sopenharmony_ci reg = AUDIO_OUT_ENABLE_AP3 | 10028c2ecf20Sopenharmony_ci AUDIO_OUT_ENABLE_AP2 | 10038c2ecf20Sopenharmony_ci AUDIO_OUT_ENABLE_AP1 | 10048c2ecf20Sopenharmony_ci AUDIO_OUT_ENABLE_AP0; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci if (pdata->audout_format == AUDFMT_TYPE_I2S) 10078c2ecf20Sopenharmony_ci reg |= (AUDIO_OUT_ENABLE_ACLK | AUDIO_OUT_ENABLE_WS); 10088c2ecf20Sopenharmony_ci io_write(sd, REG_AUDIO_OUT_ENABLE, reg); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* reset test mode to normal audio freq auto selection */ 10118c2ecf20Sopenharmony_ci io_write(sd, REG_TEST_MODE, 0x00); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci return 0; 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/* Soft Reset of specific hdmi info */ 10178c2ecf20Sopenharmony_cistatic int 10188c2ecf20Sopenharmony_citda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci u8 reg; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* reset infoframe engine packets */ 10238c2ecf20Sopenharmony_ci reg = io_read(sd, REG_HDMI_INFO_RST); 10248c2ecf20Sopenharmony_ci io_write(sd, REG_HDMI_INFO_RST, info_rst); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* if infoframe engine has been reset clear INT_FLG_MODE */ 10278c2ecf20Sopenharmony_ci if (reg & RESET_IF) { 10288c2ecf20Sopenharmony_ci reg = io_read(sd, REG_INT_FLG_CLR_MODE); 10298c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_MODE, reg); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* Disable REFTIM to restart start-up-sequencer (SUS) */ 10338c2ecf20Sopenharmony_ci reg = io_read(sd, REG_RATE_CTRL); 10348c2ecf20Sopenharmony_ci reg &= ~RATE_REFTIM_ENABLE; 10358c2ecf20Sopenharmony_ci if (!reset_sus) 10368c2ecf20Sopenharmony_ci reg |= RATE_REFTIM_ENABLE; 10378c2ecf20Sopenharmony_ci reg = io_write(sd, REG_RATE_CTRL, reg); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci return 0; 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic void 10438c2ecf20Sopenharmony_citda1997x_power_mode(struct tda1997x_state *state, bool enable) 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 10468c2ecf20Sopenharmony_ci u8 reg; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (enable) { 10498c2ecf20Sopenharmony_ci /* Automatic control of TMDS */ 10508c2ecf20Sopenharmony_ci io_write(sd, REG_PON_OVR_EN, PON_DIS); 10518c2ecf20Sopenharmony_ci /* Enable current bias unit */ 10528c2ecf20Sopenharmony_ci io_write(sd, REG_CFG1, PON_EN); 10538c2ecf20Sopenharmony_ci /* Enable deep color PLL */ 10548c2ecf20Sopenharmony_ci io_write(sd, REG_DEEP_PLL7_BYP, PON_DIS); 10558c2ecf20Sopenharmony_ci /* Output buffers active */ 10568c2ecf20Sopenharmony_ci reg = io_read(sd, REG_OF); 10578c2ecf20Sopenharmony_ci reg &= ~OF_VP_ENABLE; 10588c2ecf20Sopenharmony_ci io_write(sd, REG_OF, reg); 10598c2ecf20Sopenharmony_ci } else { 10608c2ecf20Sopenharmony_ci /* Power down EDID mode sequence */ 10618c2ecf20Sopenharmony_ci /* Output buffers in HiZ */ 10628c2ecf20Sopenharmony_ci reg = io_read(sd, REG_OF); 10638c2ecf20Sopenharmony_ci reg |= OF_VP_ENABLE; 10648c2ecf20Sopenharmony_ci io_write(sd, REG_OF, reg); 10658c2ecf20Sopenharmony_ci /* Disable deep color PLL */ 10668c2ecf20Sopenharmony_ci io_write(sd, REG_DEEP_PLL7_BYP, PON_EN); 10678c2ecf20Sopenharmony_ci /* Disable current bias unit */ 10688c2ecf20Sopenharmony_ci io_write(sd, REG_CFG1, PON_DIS); 10698c2ecf20Sopenharmony_ci /* Manual control of TMDS */ 10708c2ecf20Sopenharmony_ci io_write(sd, REG_PON_OVR_EN, PON_EN); 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic bool 10758c2ecf20Sopenharmony_citda1997x_detect_tx_5v(struct v4l2_subdev *sd) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci u8 reg = io_read(sd, REG_DETECT_5V); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci return ((reg & DETECT_5V_SEL) ? 1 : 0); 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic bool 10838c2ecf20Sopenharmony_citda1997x_detect_tx_hpd(struct v4l2_subdev *sd) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci u8 reg = io_read(sd, REG_DETECT_5V); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci return ((reg & DETECT_HPD) ? 1 : 0); 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic int 10918c2ecf20Sopenharmony_citda1997x_detect_std(struct tda1997x_state *state, 10928c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 10958c2ecf20Sopenharmony_ci u32 vper; 10968c2ecf20Sopenharmony_ci u16 hper; 10978c2ecf20Sopenharmony_ci u16 hsper; 10988c2ecf20Sopenharmony_ci int i; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci /* 11018c2ecf20Sopenharmony_ci * Read the FMT registers 11028c2ecf20Sopenharmony_ci * REG_V_PER: Period of a frame (or two fields) in MCLK(27MHz) cycles 11038c2ecf20Sopenharmony_ci * REG_H_PER: Period of a line in MCLK(27MHz) cycles 11048c2ecf20Sopenharmony_ci * REG_HS_WIDTH: Period of horiz sync pulse in MCLK(27MHz) cycles 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci vper = io_read24(sd, REG_V_PER) & MASK_VPER; 11078c2ecf20Sopenharmony_ci hper = io_read16(sd, REG_H_PER) & MASK_HPER; 11088c2ecf20Sopenharmony_ci hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; 11098c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper); 11108c2ecf20Sopenharmony_ci if (!vper || !hper || !hsper) 11118c2ecf20Sopenharmony_ci return -ENOLINK; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { 11148c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt; 11158c2ecf20Sopenharmony_ci u32 lines, width, _hper, _hsper; 11168c2ecf20Sopenharmony_ci u32 vmin, vmax, hmin, hmax, hsmin, hsmax; 11178c2ecf20Sopenharmony_ci bool vmatch, hmatch, hsmatch; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci bt = &v4l2_dv_timings_presets[i].bt; 11208c2ecf20Sopenharmony_ci width = V4L2_DV_BT_FRAME_WIDTH(bt); 11218c2ecf20Sopenharmony_ci lines = V4L2_DV_BT_FRAME_HEIGHT(bt); 11228c2ecf20Sopenharmony_ci _hper = (u32)bt->pixelclock / width; 11238c2ecf20Sopenharmony_ci if (bt->interlaced) 11248c2ecf20Sopenharmony_ci lines /= 2; 11258c2ecf20Sopenharmony_ci /* vper +/- 0.7% */ 11268c2ecf20Sopenharmony_ci vmin = ((27000000 / 1000) * 993) / _hper * lines; 11278c2ecf20Sopenharmony_ci vmax = ((27000000 / 1000) * 1007) / _hper * lines; 11288c2ecf20Sopenharmony_ci /* hper +/- 1.0% */ 11298c2ecf20Sopenharmony_ci hmin = ((27000000 / 100) * 99) / _hper; 11308c2ecf20Sopenharmony_ci hmax = ((27000000 / 100) * 101) / _hper; 11318c2ecf20Sopenharmony_ci /* hsper +/- 2 (take care to avoid 32bit overflow) */ 11328c2ecf20Sopenharmony_ci _hsper = 27000 * bt->hsync / ((u32)bt->pixelclock/1000); 11338c2ecf20Sopenharmony_ci hsmin = _hsper - 2; 11348c2ecf20Sopenharmony_ci hsmax = _hsper + 2; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci /* vmatch matches the framerate */ 11378c2ecf20Sopenharmony_ci vmatch = ((vper <= vmax) && (vper >= vmin)) ? 1 : 0; 11388c2ecf20Sopenharmony_ci /* hmatch matches the width */ 11398c2ecf20Sopenharmony_ci hmatch = ((hper <= hmax) && (hper >= hmin)) ? 1 : 0; 11408c2ecf20Sopenharmony_ci /* hsmatch matches the hswidth */ 11418c2ecf20Sopenharmony_ci hsmatch = ((hsper <= hsmax) && (hsper >= hsmin)) ? 1 : 0; 11428c2ecf20Sopenharmony_ci if (hmatch && vmatch && hsmatch) { 11438c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "Detected format: ", 11448c2ecf20Sopenharmony_ci &v4l2_dv_timings_presets[i], 11458c2ecf20Sopenharmony_ci false); 11468c2ecf20Sopenharmony_ci if (timings) 11478c2ecf20Sopenharmony_ci *timings = v4l2_dv_timings_presets[i]; 11488c2ecf20Sopenharmony_ci return 0; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci v4l_err(state->client, "no resolution match for timings: %d/%d/%d\n", 11538c2ecf20Sopenharmony_ci vper, hper, hsper); 11548c2ecf20Sopenharmony_ci return -ERANGE; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci/* some sort of errata workaround for chip revision 0 (N1) */ 11588c2ecf20Sopenharmony_cistatic void tda1997x_reset_n1(struct tda1997x_state *state) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 11618c2ecf20Sopenharmony_ci u8 reg; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* clear HDMI mode flag in BCAPS */ 11648c2ecf20Sopenharmony_ci io_write(sd, REG_CLK_CFG, CLK_CFG_SEL_ACLK_EN | CLK_CFG_SEL_ACLK); 11658c2ecf20Sopenharmony_ci io_write(sd, REG_PON_OVR_EN, PON_EN); 11668c2ecf20Sopenharmony_ci io_write(sd, REG_PON_CBIAS, PON_EN); 11678c2ecf20Sopenharmony_ci io_write(sd, REG_PON_PLL, PON_EN); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci reg = io_read(sd, REG_MODE_REC_CFG1); 11708c2ecf20Sopenharmony_ci reg &= ~0x06; 11718c2ecf20Sopenharmony_ci reg |= 0x02; 11728c2ecf20Sopenharmony_ci io_write(sd, REG_MODE_REC_CFG1, reg); 11738c2ecf20Sopenharmony_ci io_write(sd, REG_CLK_CFG, CLK_CFG_DIS); 11748c2ecf20Sopenharmony_ci io_write(sd, REG_PON_OVR_EN, PON_DIS); 11758c2ecf20Sopenharmony_ci reg = io_read(sd, REG_MODE_REC_CFG1); 11768c2ecf20Sopenharmony_ci reg &= ~0x06; 11778c2ecf20Sopenharmony_ci io_write(sd, REG_MODE_REC_CFG1, reg); 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * Activity detection must only be notified when stable_clk_x AND active_x 11828c2ecf20Sopenharmony_ci * bits are set to 1. If only stable_clk_x bit is set to 1 but not 11838c2ecf20Sopenharmony_ci * active_x, it means that the TMDS clock is not in the defined range 11848c2ecf20Sopenharmony_ci * and activity detection must not be notified. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_cistatic u8 11878c2ecf20Sopenharmony_citda1997x_read_activity_status_regs(struct v4l2_subdev *sd) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci u8 reg, status = 0; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* Read CLK_A_STATUS register */ 11928c2ecf20Sopenharmony_ci reg = io_read(sd, REG_CLK_A_STATUS); 11938c2ecf20Sopenharmony_ci /* ignore if not active */ 11948c2ecf20Sopenharmony_ci if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) 11958c2ecf20Sopenharmony_ci reg &= ~MASK_CLK_STABLE; 11968c2ecf20Sopenharmony_ci status |= ((reg & MASK_CLK_STABLE) >> 2); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci /* Read CLK_B_STATUS register */ 11998c2ecf20Sopenharmony_ci reg = io_read(sd, REG_CLK_B_STATUS); 12008c2ecf20Sopenharmony_ci /* ignore if not active */ 12018c2ecf20Sopenharmony_ci if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) 12028c2ecf20Sopenharmony_ci reg &= ~MASK_CLK_STABLE; 12038c2ecf20Sopenharmony_ci status |= ((reg & MASK_CLK_STABLE) >> 1); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* Read the SUS_STATUS register */ 12068c2ecf20Sopenharmony_ci reg = io_read(sd, REG_SUS_STATUS); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* If state = 5 => TMDS is locked */ 12098c2ecf20Sopenharmony_ci if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) 12108c2ecf20Sopenharmony_ci status |= MASK_SUS_STATE; 12118c2ecf20Sopenharmony_ci else 12128c2ecf20Sopenharmony_ci status &= ~MASK_SUS_STATE; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return status; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void 12188c2ecf20Sopenharmony_ciset_rgb_quantization_range(struct tda1997x_state *state) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct v4l2_hdmi_colorimetry *c = &state->colorimetry; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci state->colorimetry = v4l2_hdmi_rx_colorimetry(&state->avi_infoframe, 12238c2ecf20Sopenharmony_ci NULL, 12248c2ecf20Sopenharmony_ci state->timings.bt.height); 12258c2ecf20Sopenharmony_ci /* If ycbcr_enc is V4L2_YCBCR_ENC_DEFAULT, we receive RGB */ 12268c2ecf20Sopenharmony_ci if (c->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) { 12278c2ecf20Sopenharmony_ci switch (state->rgb_quantization_range) { 12288c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_LIMITED: 12298c2ecf20Sopenharmony_ci c->quantization = V4L2_QUANTIZATION_FULL_RANGE; 12308c2ecf20Sopenharmony_ci break; 12318c2ecf20Sopenharmony_ci case V4L2_DV_RGB_RANGE_FULL: 12328c2ecf20Sopenharmony_ci c->quantization = V4L2_QUANTIZATION_LIM_RANGE; 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, 12378c2ecf20Sopenharmony_ci "colorspace=%d/%d colorimetry=%d range=%s content=%d\n", 12388c2ecf20Sopenharmony_ci state->avi_infoframe.colorspace, c->colorspace, 12398c2ecf20Sopenharmony_ci state->avi_infoframe.colorimetry, 12408c2ecf20Sopenharmony_ci v4l2_quantization_names[c->quantization], 12418c2ecf20Sopenharmony_ci state->avi_infoframe.content_type); 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci/* parse an infoframe and do some sanity checks on it */ 12458c2ecf20Sopenharmony_cistatic unsigned int 12468c2ecf20Sopenharmony_citda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 12498c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 12508c2ecf20Sopenharmony_ci u8 buffer[40] = { 0 }; 12518c2ecf20Sopenharmony_ci u8 reg; 12528c2ecf20Sopenharmony_ci int len, err; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* read data */ 12558c2ecf20Sopenharmony_ci len = io_readn(sd, addr, sizeof(buffer), buffer); 12568c2ecf20Sopenharmony_ci err = hdmi_infoframe_unpack(&frame, buffer, len); 12578c2ecf20Sopenharmony_ci if (err) { 12588c2ecf20Sopenharmony_ci v4l_err(state->client, 12598c2ecf20Sopenharmony_ci "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", 12608c2ecf20Sopenharmony_ci len, addr, buffer[0]); 12618c2ecf20Sopenharmony_ci return err; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); 12648c2ecf20Sopenharmony_ci switch (frame.any.type) { 12658c2ecf20Sopenharmony_ci /* Audio InfoFrame: see HDMI spec 8.2.2 */ 12668c2ecf20Sopenharmony_ci case HDMI_INFOFRAME_TYPE_AUDIO: 12678c2ecf20Sopenharmony_ci /* sample rate */ 12688c2ecf20Sopenharmony_ci switch (frame.audio.sample_frequency) { 12698c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: 12708c2ecf20Sopenharmony_ci state->audio_samplerate = 32000; 12718c2ecf20Sopenharmony_ci break; 12728c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: 12738c2ecf20Sopenharmony_ci state->audio_samplerate = 44100; 12748c2ecf20Sopenharmony_ci break; 12758c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: 12768c2ecf20Sopenharmony_ci state->audio_samplerate = 48000; 12778c2ecf20Sopenharmony_ci break; 12788c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: 12798c2ecf20Sopenharmony_ci state->audio_samplerate = 88200; 12808c2ecf20Sopenharmony_ci break; 12818c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: 12828c2ecf20Sopenharmony_ci state->audio_samplerate = 96000; 12838c2ecf20Sopenharmony_ci break; 12848c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: 12858c2ecf20Sopenharmony_ci state->audio_samplerate = 176400; 12868c2ecf20Sopenharmony_ci break; 12878c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: 12888c2ecf20Sopenharmony_ci state->audio_samplerate = 192000; 12898c2ecf20Sopenharmony_ci break; 12908c2ecf20Sopenharmony_ci default: 12918c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: 12928c2ecf20Sopenharmony_ci break; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* sample size */ 12968c2ecf20Sopenharmony_ci switch (frame.audio.sample_size) { 12978c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_SIZE_16: 12988c2ecf20Sopenharmony_ci state->audio_samplesize = 16; 12998c2ecf20Sopenharmony_ci break; 13008c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_SIZE_20: 13018c2ecf20Sopenharmony_ci state->audio_samplesize = 20; 13028c2ecf20Sopenharmony_ci break; 13038c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_SIZE_24: 13048c2ecf20Sopenharmony_ci state->audio_samplesize = 24; 13058c2ecf20Sopenharmony_ci break; 13068c2ecf20Sopenharmony_ci case HDMI_AUDIO_SAMPLE_SIZE_STREAM: 13078c2ecf20Sopenharmony_ci default: 13088c2ecf20Sopenharmony_ci break; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* Channel Count */ 13128c2ecf20Sopenharmony_ci state->audio_channels = frame.audio.channels; 13138c2ecf20Sopenharmony_ci if (frame.audio.channel_allocation && 13148c2ecf20Sopenharmony_ci frame.audio.channel_allocation != state->audio_ch_alloc) { 13158c2ecf20Sopenharmony_ci /* use the channel assignment from the infoframe */ 13168c2ecf20Sopenharmony_ci state->audio_ch_alloc = frame.audio.channel_allocation; 13178c2ecf20Sopenharmony_ci tda1997x_configure_audout(sd, state->audio_ch_alloc); 13188c2ecf20Sopenharmony_ci /* reset the audio FIFO */ 13198c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci break; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */ 13248c2ecf20Sopenharmony_ci case HDMI_INFOFRAME_TYPE_AVI: 13258c2ecf20Sopenharmony_ci state->avi_infoframe = frame.avi; 13268c2ecf20Sopenharmony_ci set_rgb_quantization_range(state); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* configure upsampler: 0=bypass 1=repeatchroma 2=interpolate */ 13298c2ecf20Sopenharmony_ci reg = io_read(sd, REG_PIX_REPEAT); 13308c2ecf20Sopenharmony_ci reg &= ~PIX_REPEAT_MASK_UP_SEL; 13318c2ecf20Sopenharmony_ci if (frame.avi.colorspace == HDMI_COLORSPACE_YUV422) 13328c2ecf20Sopenharmony_ci reg |= (PIX_REPEAT_CHROMA << PIX_REPEAT_SHIFT); 13338c2ecf20Sopenharmony_ci io_write(sd, REG_PIX_REPEAT, reg); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci /* ConfigurePixelRepeater: repeat n-times each pixel */ 13368c2ecf20Sopenharmony_ci reg = io_read(sd, REG_PIX_REPEAT); 13378c2ecf20Sopenharmony_ci reg &= ~PIX_REPEAT_MASK_REP; 13388c2ecf20Sopenharmony_ci reg |= frame.avi.pixel_repeat; 13398c2ecf20Sopenharmony_ci io_write(sd, REG_PIX_REPEAT, reg); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* configure the receiver with the new colorspace */ 13428c2ecf20Sopenharmony_ci tda1997x_configure_csc(sd); 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci default: 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci return 0; 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 13538c2ecf20Sopenharmony_ci u8 reg, source; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci source = io_read(sd, REG_INT_FLG_CLR_SUS); 13568c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_SUS, source); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (source & MASK_MPT) { 13598c2ecf20Sopenharmony_ci /* reset MTP in use flag if set */ 13608c2ecf20Sopenharmony_ci if (state->mptrw_in_progress) 13618c2ecf20Sopenharmony_ci state->mptrw_in_progress = 0; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci if (source & MASK_SUS_END) { 13658c2ecf20Sopenharmony_ci /* reset audio FIFO */ 13668c2ecf20Sopenharmony_ci reg = io_read(sd, REG_HDMI_INFO_RST); 13678c2ecf20Sopenharmony_ci reg |= MASK_SR_FIFO_FIFO_CTRL; 13688c2ecf20Sopenharmony_ci io_write(sd, REG_HDMI_INFO_RST, reg); 13698c2ecf20Sopenharmony_ci reg &= ~MASK_SR_FIFO_FIFO_CTRL; 13708c2ecf20Sopenharmony_ci io_write(sd, REG_HDMI_INFO_RST, reg); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci /* reset HDMI flags */ 13738c2ecf20Sopenharmony_ci state->hdmi_status = 0; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* filter FMT interrupt based on SUS state */ 13778c2ecf20Sopenharmony_ci reg = io_read(sd, REG_SUS_STATUS); 13788c2ecf20Sopenharmony_ci if (((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) 13798c2ecf20Sopenharmony_ci || (source & MASK_MPT)) { 13808c2ecf20Sopenharmony_ci source &= ~MASK_FMT; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (source & (MASK_FMT | MASK_SUS_END)) { 13848c2ecf20Sopenharmony_ci reg = io_read(sd, REG_SUS_STATUS); 13858c2ecf20Sopenharmony_ci if ((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) { 13868c2ecf20Sopenharmony_ci v4l_err(state->client, "BAD SUS STATUS\n"); 13878c2ecf20Sopenharmony_ci return; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci if (debug) 13908c2ecf20Sopenharmony_ci tda1997x_detect_std(state, NULL); 13918c2ecf20Sopenharmony_ci /* notify user of change in resolution */ 13928c2ecf20Sopenharmony_ci v4l2_subdev_notify_event(&state->sd, &tda1997x_ev_fmt); 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 13998c2ecf20Sopenharmony_ci u8 source; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci source = io_read(sd, REG_INT_FLG_CLR_DDC); 14028c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_DDC, source); 14038c2ecf20Sopenharmony_ci if (source & MASK_EDID_MTP) { 14048c2ecf20Sopenharmony_ci /* reset MTP in use flag if set */ 14058c2ecf20Sopenharmony_ci if (state->mptrw_in_progress) 14068c2ecf20Sopenharmony_ci state->mptrw_in_progress = 0; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci /* Detection of +5V */ 14108c2ecf20Sopenharmony_ci if (source & MASK_DET_5V) { 14118c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, 14128c2ecf20Sopenharmony_ci tda1997x_detect_tx_5v(sd)); 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci} 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_cistatic void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags) 14178c2ecf20Sopenharmony_ci{ 14188c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 14198c2ecf20Sopenharmony_ci u8 reg, source; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci u8 irq_status; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci source = io_read(sd, REG_INT_FLG_CLR_RATE); 14248c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_RATE, source); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* read status regs */ 14278c2ecf20Sopenharmony_ci irq_status = tda1997x_read_activity_status_regs(sd); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* 14308c2ecf20Sopenharmony_ci * read clock status reg until INT_FLG_CLR_RATE is still 0 14318c2ecf20Sopenharmony_ci * after the read to make sure its the last one 14328c2ecf20Sopenharmony_ci */ 14338c2ecf20Sopenharmony_ci reg = source; 14348c2ecf20Sopenharmony_ci while (reg != 0) { 14358c2ecf20Sopenharmony_ci irq_status = tda1997x_read_activity_status_regs(sd); 14368c2ecf20Sopenharmony_ci reg = io_read(sd, REG_INT_FLG_CLR_RATE); 14378c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_RATE, reg); 14388c2ecf20Sopenharmony_ci source |= reg; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* we only pay attention to stability change events */ 14428c2ecf20Sopenharmony_ci if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) { 14438c2ecf20Sopenharmony_ci int input = (source & MASK_RATE_A_ST)?0:1; 14448c2ecf20Sopenharmony_ci u8 mask = 1<<input; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci /* state change */ 14478c2ecf20Sopenharmony_ci if ((irq_status & mask) != (state->activity_status & mask)) { 14488c2ecf20Sopenharmony_ci /* activity lost */ 14498c2ecf20Sopenharmony_ci if ((irq_status & mask) == 0) { 14508c2ecf20Sopenharmony_ci v4l_info(state->client, 14518c2ecf20Sopenharmony_ci "HDMI-%c: Digital Activity Lost\n", 14528c2ecf20Sopenharmony_ci input+'A'); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* bypass up/down sampler and pixel repeater */ 14558c2ecf20Sopenharmony_ci reg = io_read(sd, REG_PIX_REPEAT); 14568c2ecf20Sopenharmony_ci reg &= ~PIX_REPEAT_MASK_UP_SEL; 14578c2ecf20Sopenharmony_ci reg &= ~PIX_REPEAT_MASK_REP; 14588c2ecf20Sopenharmony_ci io_write(sd, REG_PIX_REPEAT, reg); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (state->chip_revision == 0) 14618c2ecf20Sopenharmony_ci tda1997x_reset_n1(state); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci state->input_detect[input] = 0; 14648c2ecf20Sopenharmony_ci v4l2_subdev_notify_event(sd, &tda1997x_ev_fmt); 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* activity detected */ 14688c2ecf20Sopenharmony_ci else { 14698c2ecf20Sopenharmony_ci v4l_info(state->client, 14708c2ecf20Sopenharmony_ci "HDMI-%c: Digital Activity Detected\n", 14718c2ecf20Sopenharmony_ci input+'A'); 14728c2ecf20Sopenharmony_ci state->input_detect[input] = 1; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* hold onto current state */ 14768c2ecf20Sopenharmony_ci state->activity_status = (irq_status & mask); 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 14848c2ecf20Sopenharmony_ci u8 source; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci source = io_read(sd, REG_INT_FLG_CLR_INFO); 14878c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_INFO, source); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci /* Audio infoframe */ 14908c2ecf20Sopenharmony_ci if (source & MASK_AUD_IF) { 14918c2ecf20Sopenharmony_ci tda1997x_parse_infoframe(state, AUD_IF); 14928c2ecf20Sopenharmony_ci source &= ~MASK_AUD_IF; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci /* Source Product Descriptor infoframe change */ 14968c2ecf20Sopenharmony_ci if (source & MASK_SPD_IF) { 14978c2ecf20Sopenharmony_ci tda1997x_parse_infoframe(state, SPD_IF); 14988c2ecf20Sopenharmony_ci source &= ~MASK_SPD_IF; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci /* Auxiliary Video Information infoframe */ 15028c2ecf20Sopenharmony_ci if (source & MASK_AVI_IF) { 15038c2ecf20Sopenharmony_ci tda1997x_parse_infoframe(state, AVI_IF); 15048c2ecf20Sopenharmony_ci source &= ~MASK_AVI_IF; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 15118c2ecf20Sopenharmony_ci u8 reg, source; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci source = io_read(sd, REG_INT_FLG_CLR_AUDIO); 15148c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_AUDIO, source); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* reset audio FIFO on FIFO pointer error or audio mute */ 15178c2ecf20Sopenharmony_ci if (source & MASK_ERROR_FIFO_PT || 15188c2ecf20Sopenharmony_ci source & MASK_MUTE_FLG) { 15198c2ecf20Sopenharmony_ci /* audio reset audio FIFO */ 15208c2ecf20Sopenharmony_ci reg = io_read(sd, REG_SUS_STATUS); 15218c2ecf20Sopenharmony_ci if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) { 15228c2ecf20Sopenharmony_ci reg = io_read(sd, REG_HDMI_INFO_RST); 15238c2ecf20Sopenharmony_ci reg |= MASK_SR_FIFO_FIFO_CTRL; 15248c2ecf20Sopenharmony_ci io_write(sd, REG_HDMI_INFO_RST, reg); 15258c2ecf20Sopenharmony_ci reg &= ~MASK_SR_FIFO_FIFO_CTRL; 15268c2ecf20Sopenharmony_ci io_write(sd, REG_HDMI_INFO_RST, reg); 15278c2ecf20Sopenharmony_ci /* reset channel status IT if present */ 15288c2ecf20Sopenharmony_ci source &= ~(MASK_CH_STATE); 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci if (source & MASK_AUDIO_FREQ_FLG) { 15328c2ecf20Sopenharmony_ci static const int freq[] = { 15338c2ecf20Sopenharmony_ci 0, 32000, 44100, 48000, 88200, 96000, 176400, 192000 15348c2ecf20Sopenharmony_ci }; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci reg = io_read(sd, REG_AUDIO_FREQ); 15378c2ecf20Sopenharmony_ci state->audio_samplerate = freq[reg & 7]; 15388c2ecf20Sopenharmony_ci v4l_info(state->client, "Audio Frequency Change: %dHz\n", 15398c2ecf20Sopenharmony_ci state->audio_samplerate); 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci if (source & MASK_AUDIO_FLG) { 15428c2ecf20Sopenharmony_ci reg = io_read(sd, REG_AUDIO_FLAGS); 15438c2ecf20Sopenharmony_ci if (reg & BIT(AUDCFG_TYPE_DST)) 15448c2ecf20Sopenharmony_ci state->audio_type = AUDCFG_TYPE_DST; 15458c2ecf20Sopenharmony_ci if (reg & BIT(AUDCFG_TYPE_OBA)) 15468c2ecf20Sopenharmony_ci state->audio_type = AUDCFG_TYPE_OBA; 15478c2ecf20Sopenharmony_ci if (reg & BIT(AUDCFG_TYPE_HBR)) 15488c2ecf20Sopenharmony_ci state->audio_type = AUDCFG_TYPE_HBR; 15498c2ecf20Sopenharmony_ci if (reg & BIT(AUDCFG_TYPE_PCM)) 15508c2ecf20Sopenharmony_ci state->audio_type = AUDCFG_TYPE_PCM; 15518c2ecf20Sopenharmony_ci v4l_info(state->client, "Audio Type: %s\n", 15528c2ecf20Sopenharmony_ci audtype_names[state->audio_type]); 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci} 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_cistatic void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 15598c2ecf20Sopenharmony_ci u8 reg, source; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci source = io_read(sd, REG_INT_FLG_CLR_HDCP); 15628c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_HDCP, source); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* reset MTP in use flag if set */ 15658c2ecf20Sopenharmony_ci if (source & MASK_HDCP_MTP) 15668c2ecf20Sopenharmony_ci state->mptrw_in_progress = 0; 15678c2ecf20Sopenharmony_ci if (source & MASK_STATE_C5) { 15688c2ecf20Sopenharmony_ci /* REPEATER: mask AUDIO and IF irqs to avoid IF during auth */ 15698c2ecf20Sopenharmony_ci reg = io_read(sd, REG_INT_MASK_TOP); 15708c2ecf20Sopenharmony_ci reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO); 15718c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_TOP, reg); 15728c2ecf20Sopenharmony_ci *flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO); 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic irqreturn_t tda1997x_isr_thread(int irq, void *d) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct tda1997x_state *state = d; 15798c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 15808c2ecf20Sopenharmony_ci u8 flags; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 15838c2ecf20Sopenharmony_ci do { 15848c2ecf20Sopenharmony_ci /* read interrupt flags */ 15858c2ecf20Sopenharmony_ci flags = io_read(sd, REG_INT_FLG_CLR_TOP); 15868c2ecf20Sopenharmony_ci if (flags == 0) 15878c2ecf20Sopenharmony_ci break; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci /* SUS interrupt source (Input activity events) */ 15908c2ecf20Sopenharmony_ci if (flags & INTERRUPT_SUS) 15918c2ecf20Sopenharmony_ci tda1997x_irq_sus(state, &flags); 15928c2ecf20Sopenharmony_ci /* DDC interrupt source (Display Data Channel) */ 15938c2ecf20Sopenharmony_ci else if (flags & INTERRUPT_DDC) 15948c2ecf20Sopenharmony_ci tda1997x_irq_ddc(state, &flags); 15958c2ecf20Sopenharmony_ci /* RATE interrupt source (Digital Input activity) */ 15968c2ecf20Sopenharmony_ci else if (flags & INTERRUPT_RATE) 15978c2ecf20Sopenharmony_ci tda1997x_irq_rate(state, &flags); 15988c2ecf20Sopenharmony_ci /* Infoframe change interrupt */ 15998c2ecf20Sopenharmony_ci else if (flags & INTERRUPT_INFO) 16008c2ecf20Sopenharmony_ci tda1997x_irq_info(state, &flags); 16018c2ecf20Sopenharmony_ci /* Audio interrupt source: 16028c2ecf20Sopenharmony_ci * freq change, DST,OBA,HBR,ASP flags, mute, FIFO err 16038c2ecf20Sopenharmony_ci */ 16048c2ecf20Sopenharmony_ci else if (flags & INTERRUPT_AUDIO) 16058c2ecf20Sopenharmony_ci tda1997x_irq_audio(state, &flags); 16068c2ecf20Sopenharmony_ci /* HDCP interrupt source (content protection) */ 16078c2ecf20Sopenharmony_ci if (flags & INTERRUPT_HDCP) 16088c2ecf20Sopenharmony_ci tda1997x_irq_hdcp(state, &flags); 16098c2ecf20Sopenharmony_ci } while (flags != 0); 16108c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci return IRQ_HANDLED; 16138c2ecf20Sopenharmony_ci} 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 16168c2ecf20Sopenharmony_ci * v4l2_subdev_video_ops 16178c2ecf20Sopenharmony_ci */ 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic int 16208c2ecf20Sopenharmony_citda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) 16218c2ecf20Sopenharmony_ci{ 16228c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 16238c2ecf20Sopenharmony_ci u32 vper; 16248c2ecf20Sopenharmony_ci u16 hper; 16258c2ecf20Sopenharmony_ci u16 hsper; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 16288c2ecf20Sopenharmony_ci vper = io_read24(sd, REG_V_PER) & MASK_VPER; 16298c2ecf20Sopenharmony_ci hper = io_read16(sd, REG_H_PER) & MASK_HPER; 16308c2ecf20Sopenharmony_ci hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; 16318c2ecf20Sopenharmony_ci /* 16328c2ecf20Sopenharmony_ci * The tda1997x supports A/B inputs but only a single output. 16338c2ecf20Sopenharmony_ci * The irq handler monitors for timing changes on both inputs and 16348c2ecf20Sopenharmony_ci * sets the input_detect array to 0|1 depending on signal presence. 16358c2ecf20Sopenharmony_ci * I believe selection of A vs B is automatic. 16368c2ecf20Sopenharmony_ci * 16378c2ecf20Sopenharmony_ci * The vper/hper/hsper registers provide the frame period, line period 16388c2ecf20Sopenharmony_ci * and horiz sync period (units of MCLK clock cycles (27MHz)) and 16398c2ecf20Sopenharmony_ci * testing shows these values to be random if no signal is present 16408c2ecf20Sopenharmony_ci * or locked. 16418c2ecf20Sopenharmony_ci */ 16428c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "inputs:%d/%d timings:%d/%d/%d\n", 16438c2ecf20Sopenharmony_ci state->input_detect[0], state->input_detect[1], 16448c2ecf20Sopenharmony_ci vper, hper, hsper); 16458c2ecf20Sopenharmony_ci if (!state->input_detect[0] && !state->input_detect[1]) 16468c2ecf20Sopenharmony_ci *status = V4L2_IN_ST_NO_SIGNAL; 16478c2ecf20Sopenharmony_ci else if (!vper || !hper || !hsper) 16488c2ecf20Sopenharmony_ci *status = V4L2_IN_ST_NO_SYNC; 16498c2ecf20Sopenharmony_ci else 16508c2ecf20Sopenharmony_ci *status = 0; 16518c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci return 0; 16548c2ecf20Sopenharmony_ci}; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_cistatic int tda1997x_s_dv_timings(struct v4l2_subdev *sd, 16578c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 16588c2ecf20Sopenharmony_ci{ 16598c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s\n", __func__); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) 16648c2ecf20Sopenharmony_ci return 0; /* no changes */ 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, &tda1997x_dv_timings_cap, 16678c2ecf20Sopenharmony_ci NULL, NULL)) 16688c2ecf20Sopenharmony_ci return -ERANGE; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 16718c2ecf20Sopenharmony_ci state->timings = *timings; 16728c2ecf20Sopenharmony_ci /* setup frame detection window and VHREF timing generator */ 16738c2ecf20Sopenharmony_ci tda1997x_configure_vhref(sd); 16748c2ecf20Sopenharmony_ci /* configure colorspace conversion */ 16758c2ecf20Sopenharmony_ci tda1997x_configure_csc(sd); 16768c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_cistatic int tda1997x_g_dv_timings(struct v4l2_subdev *sd, 16828c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 16838c2ecf20Sopenharmony_ci{ 16848c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s\n", __func__); 16878c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 16888c2ecf20Sopenharmony_ci *timings = state->timings; 16898c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci return 0; 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic int tda1997x_query_dv_timings(struct v4l2_subdev *sd, 16958c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 16988c2ecf20Sopenharmony_ci int ret; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s\n", __func__); 17018c2ecf20Sopenharmony_ci memset(timings, 0, sizeof(struct v4l2_dv_timings)); 17028c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 17038c2ecf20Sopenharmony_ci ret = tda1997x_detect_std(state, timings); 17048c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci return ret; 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops tda1997x_video_ops = { 17108c2ecf20Sopenharmony_ci .g_input_status = tda1997x_g_input_status, 17118c2ecf20Sopenharmony_ci .s_dv_timings = tda1997x_s_dv_timings, 17128c2ecf20Sopenharmony_ci .g_dv_timings = tda1997x_g_dv_timings, 17138c2ecf20Sopenharmony_ci .query_dv_timings = tda1997x_query_dv_timings, 17148c2ecf20Sopenharmony_ci}; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 17188c2ecf20Sopenharmony_ci * v4l2_subdev_pad_ops 17198c2ecf20Sopenharmony_ci */ 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int tda1997x_init_cfg(struct v4l2_subdev *sd, 17228c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 17258c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, 0); 17288c2ecf20Sopenharmony_ci mf->code = state->mbus_codes[0]; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return 0; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic int tda1997x_enum_mbus_code(struct v4l2_subdev *sd, 17348c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17358c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s %d\n", __func__, code->index); 17408c2ecf20Sopenharmony_ci if (code->index >= ARRAY_SIZE(state->mbus_codes)) 17418c2ecf20Sopenharmony_ci return -EINVAL; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci if (!state->mbus_codes[code->index]) 17448c2ecf20Sopenharmony_ci return -EINVAL; 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci code->code = state->mbus_codes[code->index]; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci return 0; 17498c2ecf20Sopenharmony_ci} 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_cistatic void tda1997x_fill_format(struct tda1997x_state *state, 17528c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format) 17538c2ecf20Sopenharmony_ci{ 17548c2ecf20Sopenharmony_ci const struct v4l2_bt_timings *bt; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci memset(format, 0, sizeof(*format)); 17578c2ecf20Sopenharmony_ci bt = &state->timings.bt; 17588c2ecf20Sopenharmony_ci format->width = bt->width; 17598c2ecf20Sopenharmony_ci format->height = bt->height; 17608c2ecf20Sopenharmony_ci format->colorspace = state->colorimetry.colorspace; 17618c2ecf20Sopenharmony_ci format->field = (bt->interlaced) ? 17628c2ecf20Sopenharmony_ci V4L2_FIELD_SEQ_TB : V4L2_FIELD_NONE; 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_cistatic int tda1997x_get_format(struct v4l2_subdev *sd, 17668c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17678c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 17688c2ecf20Sopenharmony_ci{ 17698c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s pad=%d which=%d\n", 17728c2ecf20Sopenharmony_ci __func__, format->pad, format->which); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci tda1997x_fill_format(state, &format->format); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 17778c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 17808c2ecf20Sopenharmony_ci format->format.code = fmt->code; 17818c2ecf20Sopenharmony_ci } else 17828c2ecf20Sopenharmony_ci format->format.code = state->mbus_code; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci return 0; 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic int tda1997x_set_format(struct v4l2_subdev *sd, 17888c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17898c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 17928c2ecf20Sopenharmony_ci u32 code = 0; 17938c2ecf20Sopenharmony_ci int i; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s pad=%d which=%d fmt=0x%x\n", 17968c2ecf20Sopenharmony_ci __func__, format->pad, format->which, format->format.code); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state->mbus_codes); i++) { 17998c2ecf20Sopenharmony_ci if (format->format.code == state->mbus_codes[i]) { 18008c2ecf20Sopenharmony_ci code = state->mbus_codes[i]; 18018c2ecf20Sopenharmony_ci break; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci if (!code) 18058c2ecf20Sopenharmony_ci code = state->mbus_codes[0]; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci tda1997x_fill_format(state, &format->format); 18088c2ecf20Sopenharmony_ci format->format.code = code; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 18118c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 18148c2ecf20Sopenharmony_ci *fmt = format->format; 18158c2ecf20Sopenharmony_ci } else { 18168c2ecf20Sopenharmony_ci int ret = tda1997x_setup_format(state, format->format.code); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (ret) 18198c2ecf20Sopenharmony_ci return ret; 18208c2ecf20Sopenharmony_ci /* mbus_code has changed - re-configure csc/vidout */ 18218c2ecf20Sopenharmony_ci tda1997x_configure_csc(sd); 18228c2ecf20Sopenharmony_ci tda1997x_configure_vidout(state); 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci return 0; 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_cistatic int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); 18338c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (edid->start_block == 0 && edid->blocks == 0) { 18368c2ecf20Sopenharmony_ci edid->blocks = state->edid.blocks; 18378c2ecf20Sopenharmony_ci return 0; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci if (!state->edid.present) 18418c2ecf20Sopenharmony_ci return -ENODATA; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (edid->start_block >= state->edid.blocks) 18448c2ecf20Sopenharmony_ci return -EINVAL; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (edid->start_block + edid->blocks > state->edid.blocks) 18478c2ecf20Sopenharmony_ci edid->blocks = state->edid.blocks - edid->start_block; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci memcpy(edid->edid, state->edid.edid + edid->start_block * 128, 18508c2ecf20Sopenharmony_ci edid->blocks * 128); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci return 0; 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_cistatic int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) 18568c2ecf20Sopenharmony_ci{ 18578c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 18588c2ecf20Sopenharmony_ci int i; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); 18618c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci if (edid->start_block != 0) 18648c2ecf20Sopenharmony_ci return -EINVAL; 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci if (edid->blocks == 0) { 18678c2ecf20Sopenharmony_ci state->edid.blocks = 0; 18688c2ecf20Sopenharmony_ci state->edid.present = 0; 18698c2ecf20Sopenharmony_ci tda1997x_disable_edid(sd); 18708c2ecf20Sopenharmony_ci return 0; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (edid->blocks > 2) { 18748c2ecf20Sopenharmony_ci edid->blocks = 2; 18758c2ecf20Sopenharmony_ci return -E2BIG; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci tda1997x_disable_edid(sd); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci /* write base EDID */ 18818c2ecf20Sopenharmony_ci for (i = 0; i < 128; i++) 18828c2ecf20Sopenharmony_ci io_write(sd, REG_EDID_IN_BYTE0 + i, edid->edid[i]); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci /* write CEA Extension */ 18858c2ecf20Sopenharmony_ci for (i = 0; i < 128; i++) 18868c2ecf20Sopenharmony_ci io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* store state */ 18898c2ecf20Sopenharmony_ci memcpy(state->edid.edid, edid->edid, 256); 18908c2ecf20Sopenharmony_ci state->edid.blocks = edid->blocks; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci tda1997x_enable_edid(sd); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci return 0; 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_cistatic int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd, 18988c2ecf20Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci *cap = tda1997x_dv_timings_cap; 19018c2ecf20Sopenharmony_ci return 0; 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_cistatic int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, 19058c2ecf20Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci return v4l2_enum_dv_timings_cap(timings, &tda1997x_dv_timings_cap, 19088c2ecf20Sopenharmony_ci NULL, NULL); 19098c2ecf20Sopenharmony_ci} 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { 19128c2ecf20Sopenharmony_ci .init_cfg = tda1997x_init_cfg, 19138c2ecf20Sopenharmony_ci .enum_mbus_code = tda1997x_enum_mbus_code, 19148c2ecf20Sopenharmony_ci .get_fmt = tda1997x_get_format, 19158c2ecf20Sopenharmony_ci .set_fmt = tda1997x_set_format, 19168c2ecf20Sopenharmony_ci .get_edid = tda1997x_get_edid, 19178c2ecf20Sopenharmony_ci .set_edid = tda1997x_set_edid, 19188c2ecf20Sopenharmony_ci .dv_timings_cap = tda1997x_get_dv_timings_cap, 19198c2ecf20Sopenharmony_ci .enum_dv_timings = tda1997x_enum_dv_timings, 19208c2ecf20Sopenharmony_ci}; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 19238c2ecf20Sopenharmony_ci * v4l2_subdev_core_ops 19248c2ecf20Sopenharmony_ci */ 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_cistatic int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 19298c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 19308c2ecf20Sopenharmony_ci u8 buffer[40] = { 0 }; 19318c2ecf20Sopenharmony_ci int len, err; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* read data */ 19348c2ecf20Sopenharmony_ci len = io_readn(sd, addr, sizeof(buffer), buffer); 19358c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); 19368c2ecf20Sopenharmony_ci err = hdmi_infoframe_unpack(&frame, buffer, len); 19378c2ecf20Sopenharmony_ci if (err) { 19388c2ecf20Sopenharmony_ci v4l_err(state->client, 19398c2ecf20Sopenharmony_ci "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", 19408c2ecf20Sopenharmony_ci len, addr, buffer[0]); 19418c2ecf20Sopenharmony_ci return err; 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci return 0; 19468c2ecf20Sopenharmony_ci} 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_cistatic int tda1997x_log_status(struct v4l2_subdev *sd) 19498c2ecf20Sopenharmony_ci{ 19508c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 19518c2ecf20Sopenharmony_ci struct v4l2_dv_timings timings; 19528c2ecf20Sopenharmony_ci struct hdmi_avi_infoframe *avi = &state->avi_infoframe; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Chip status-----\n"); 19558c2ecf20Sopenharmony_ci v4l2_info(sd, "Chip: %s N%d\n", state->info->name, 19568c2ecf20Sopenharmony_ci state->chip_revision + 1); 19578c2ecf20Sopenharmony_ci v4l2_info(sd, "EDID Enabled: %s\n", state->edid.present ? "yes" : "no"); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Signal status-----\n"); 19608c2ecf20Sopenharmony_ci v4l2_info(sd, "Cable detected (+5V power): %s\n", 19618c2ecf20Sopenharmony_ci tda1997x_detect_tx_5v(sd) ? "yes" : "no"); 19628c2ecf20Sopenharmony_ci v4l2_info(sd, "HPD detected: %s\n", 19638c2ecf20Sopenharmony_ci tda1997x_detect_tx_hpd(sd) ? "yes" : "no"); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Video Timings-----\n"); 19668c2ecf20Sopenharmony_ci switch (tda1997x_detect_std(state, &timings)) { 19678c2ecf20Sopenharmony_ci case -ENOLINK: 19688c2ecf20Sopenharmony_ci v4l2_info(sd, "No video detected\n"); 19698c2ecf20Sopenharmony_ci break; 19708c2ecf20Sopenharmony_ci case -ERANGE: 19718c2ecf20Sopenharmony_ci v4l2_info(sd, "Invalid signal detected\n"); 19728c2ecf20Sopenharmony_ci break; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci v4l2_print_dv_timings(sd->name, "Configured format: ", 19758c2ecf20Sopenharmony_ci &state->timings, true); 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Color space-----\n"); 19788c2ecf20Sopenharmony_ci v4l2_info(sd, "Input color space: %s %s %s", 19798c2ecf20Sopenharmony_ci hdmi_colorspace_names[avi->colorspace], 19808c2ecf20Sopenharmony_ci (avi->colorspace == HDMI_COLORSPACE_RGB) ? "" : 19818c2ecf20Sopenharmony_ci hdmi_colorimetry_names[avi->colorimetry], 19828c2ecf20Sopenharmony_ci v4l2_quantization_names[state->colorimetry.quantization]); 19838c2ecf20Sopenharmony_ci v4l2_info(sd, "Output color space: %s", 19848c2ecf20Sopenharmony_ci vidfmt_names[state->vid_fmt]); 19858c2ecf20Sopenharmony_ci v4l2_info(sd, "Color space conversion: %s", state->conv ? 19868c2ecf20Sopenharmony_ci state->conv->name : "None"); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Audio-----\n"); 19898c2ecf20Sopenharmony_ci if (state->audio_channels) { 19908c2ecf20Sopenharmony_ci v4l2_info(sd, "audio: %dch %dHz\n", state->audio_channels, 19918c2ecf20Sopenharmony_ci state->audio_samplerate); 19928c2ecf20Sopenharmony_ci } else { 19938c2ecf20Sopenharmony_ci v4l2_info(sd, "audio: none\n"); 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci v4l2_info(sd, "-----Infoframes-----\n"); 19978c2ecf20Sopenharmony_ci tda1997x_log_infoframe(sd, AUD_IF); 19988c2ecf20Sopenharmony_ci tda1997x_log_infoframe(sd, SPD_IF); 19998c2ecf20Sopenharmony_ci tda1997x_log_infoframe(sd, AVI_IF); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci return 0; 20028c2ecf20Sopenharmony_ci} 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_cistatic int tda1997x_subscribe_event(struct v4l2_subdev *sd, 20058c2ecf20Sopenharmony_ci struct v4l2_fh *fh, 20068c2ecf20Sopenharmony_ci struct v4l2_event_subscription *sub) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci switch (sub->type) { 20098c2ecf20Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 20108c2ecf20Sopenharmony_ci return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); 20118c2ecf20Sopenharmony_ci case V4L2_EVENT_CTRL: 20128c2ecf20Sopenharmony_ci return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); 20138c2ecf20Sopenharmony_ci default: 20148c2ecf20Sopenharmony_ci return -EINVAL; 20158c2ecf20Sopenharmony_ci } 20168c2ecf20Sopenharmony_ci} 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops tda1997x_core_ops = { 20198c2ecf20Sopenharmony_ci .log_status = tda1997x_log_status, 20208c2ecf20Sopenharmony_ci .subscribe_event = tda1997x_subscribe_event, 20218c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 20228c2ecf20Sopenharmony_ci}; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 20258c2ecf20Sopenharmony_ci * v4l2_subdev_ops 20268c2ecf20Sopenharmony_ci */ 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops tda1997x_subdev_ops = { 20298c2ecf20Sopenharmony_ci .core = &tda1997x_core_ops, 20308c2ecf20Sopenharmony_ci .video = &tda1997x_video_ops, 20318c2ecf20Sopenharmony_ci .pad = &tda1997x_pad_ops, 20328c2ecf20Sopenharmony_ci}; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 20358c2ecf20Sopenharmony_ci * v4l2_controls 20368c2ecf20Sopenharmony_ci */ 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_cistatic int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 20418c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci switch (ctrl->id) { 20448c2ecf20Sopenharmony_ci /* allow overriding the default RGB quantization range */ 20458c2ecf20Sopenharmony_ci case V4L2_CID_DV_RX_RGB_RANGE: 20468c2ecf20Sopenharmony_ci state->rgb_quantization_range = ctrl->val; 20478c2ecf20Sopenharmony_ci set_rgb_quantization_range(state); 20488c2ecf20Sopenharmony_ci tda1997x_configure_csc(sd); 20498c2ecf20Sopenharmony_ci return 0; 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci return -EINVAL; 20538c2ecf20Sopenharmony_ci}; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 20568c2ecf20Sopenharmony_ci{ 20578c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 20588c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) { 20618c2ecf20Sopenharmony_ci ctrl->val = state->avi_infoframe.content_type; 20628c2ecf20Sopenharmony_ci return 0; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci return -EINVAL; 20658c2ecf20Sopenharmony_ci}; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tda1997x_ctrl_ops = { 20688c2ecf20Sopenharmony_ci .s_ctrl = tda1997x_s_ctrl, 20698c2ecf20Sopenharmony_ci .g_volatile_ctrl = tda1997x_g_volatile_ctrl, 20708c2ecf20Sopenharmony_ci}; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_cistatic int tda1997x_core_init(struct v4l2_subdev *sd) 20738c2ecf20Sopenharmony_ci{ 20748c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 20758c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata = &state->pdata; 20768c2ecf20Sopenharmony_ci u8 reg; 20778c2ecf20Sopenharmony_ci int i; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci /* disable HPD */ 20808c2ecf20Sopenharmony_ci io_write(sd, REG_HPD_AUTO_CTRL, HPD_AUTO_HPD_UNSEL); 20818c2ecf20Sopenharmony_ci if (state->chip_revision == 0) { 20828c2ecf20Sopenharmony_ci io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP); 20838c2ecf20Sopenharmony_ci io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); 20848c2ecf20Sopenharmony_ci } 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci /* reset infoframe at end of start-up-sequencer */ 20878c2ecf20Sopenharmony_ci io_write(sd, REG_SUS_SET_RGB2, 0x06); 20888c2ecf20Sopenharmony_ci io_write(sd, REG_SUS_SET_RGB3, 0x06); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci /* Enable TMDS pull-ups */ 20918c2ecf20Sopenharmony_ci io_write(sd, REG_RT_MAN_CTRL, RT_MAN_CTRL_RT | 20928c2ecf20Sopenharmony_ci RT_MAN_CTRL_RT_B | RT_MAN_CTRL_RT_A); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci /* enable sync measurement timing */ 20958c2ecf20Sopenharmony_ci tda1997x_cec_write(sd, REG_PWR_CONTROL & 0xff, 0x04); 20968c2ecf20Sopenharmony_ci /* adjust CEC clock divider */ 20978c2ecf20Sopenharmony_ci tda1997x_cec_write(sd, REG_OSC_DIVIDER & 0xff, 0x03); 20988c2ecf20Sopenharmony_ci tda1997x_cec_write(sd, REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0); 20998c2ecf20Sopenharmony_ci io_write(sd, REG_TIMER_D, 0x54); 21008c2ecf20Sopenharmony_ci /* enable power switch */ 21018c2ecf20Sopenharmony_ci reg = tda1997x_cec_read(sd, REG_CONTROL & 0xff); 21028c2ecf20Sopenharmony_ci reg |= 0x20; 21038c2ecf20Sopenharmony_ci tda1997x_cec_write(sd, REG_CONTROL & 0xff, reg); 21048c2ecf20Sopenharmony_ci mdelay(50); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci /* read the chip version */ 21078c2ecf20Sopenharmony_ci reg = io_read(sd, REG_VERSION); 21088c2ecf20Sopenharmony_ci /* get the chip configuration */ 21098c2ecf20Sopenharmony_ci reg = io_read(sd, REG_CMTP_REG10); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci /* enable interrupts we care about */ 21128c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_TOP, 21138c2ecf20Sopenharmony_ci INTERRUPT_HDCP | INTERRUPT_AUDIO | INTERRUPT_INFO | 21148c2ecf20Sopenharmony_ci INTERRUPT_RATE | INTERRUPT_SUS); 21158c2ecf20Sopenharmony_ci /* config_mtp,fmt,sus_end,sus_st */ 21168c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_SUS, MASK_MPT | MASK_FMT | MASK_SUS_END); 21178c2ecf20Sopenharmony_ci /* rate stability change for inputs A/B */ 21188c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_RATE, MASK_RATE_B_ST | MASK_RATE_A_ST); 21198c2ecf20Sopenharmony_ci /* aud,spd,avi*/ 21208c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_INFO, 21218c2ecf20Sopenharmony_ci MASK_AUD_IF | MASK_SPD_IF | MASK_AVI_IF); 21228c2ecf20Sopenharmony_ci /* audio_freq,audio_flg,mute_flg,fifo_err */ 21238c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_AUDIO, 21248c2ecf20Sopenharmony_ci MASK_AUDIO_FREQ_FLG | MASK_AUDIO_FLG | MASK_MUTE_FLG | 21258c2ecf20Sopenharmony_ci MASK_ERROR_FIFO_PT); 21268c2ecf20Sopenharmony_ci /* HDCP C5 state reached */ 21278c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_HDCP, MASK_STATE_C5); 21288c2ecf20Sopenharmony_ci /* 5V detect and HDP pulse end */ 21298c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_DDC, MASK_DET_5V); 21308c2ecf20Sopenharmony_ci /* don't care about AFE/MODE */ 21318c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_AFE, 0); 21328c2ecf20Sopenharmony_ci io_write(sd, REG_INT_MASK_MODE, 0); 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci /* clear all interrupts */ 21358c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_TOP, 0xff); 21368c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_SUS, 0xff); 21378c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_DDC, 0xff); 21388c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_RATE, 0xff); 21398c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_MODE, 0xff); 21408c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_INFO, 0xff); 21418c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_AUDIO, 0xff); 21428c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_HDCP, 0xff); 21438c2ecf20Sopenharmony_ci io_write(sd, REG_INT_FLG_CLR_AFE, 0xff); 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* init TMDS equalizer */ 21468c2ecf20Sopenharmony_ci if (state->chip_revision == 0) 21478c2ecf20Sopenharmony_ci io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); 21488c2ecf20Sopenharmony_ci io_write24(sd, REG_CLK_MIN_RATE, CLK_MIN_RATE); 21498c2ecf20Sopenharmony_ci io_write24(sd, REG_CLK_MAX_RATE, CLK_MAX_RATE); 21508c2ecf20Sopenharmony_ci if (state->chip_revision == 0) 21518c2ecf20Sopenharmony_ci io_write(sd, REG_WDL_CFG, WDL_CFG_VAL); 21528c2ecf20Sopenharmony_ci /* DC filter */ 21538c2ecf20Sopenharmony_ci io_write(sd, REG_DEEP_COLOR_CTRL, DC_FILTER_VAL); 21548c2ecf20Sopenharmony_ci /* disable test pattern */ 21558c2ecf20Sopenharmony_ci io_write(sd, REG_SVC_MODE, 0x00); 21568c2ecf20Sopenharmony_ci /* update HDMI INFO CTRL */ 21578c2ecf20Sopenharmony_ci io_write(sd, REG_INFO_CTRL, 0xff); 21588c2ecf20Sopenharmony_ci /* write HDMI INFO EXCEED value */ 21598c2ecf20Sopenharmony_ci io_write(sd, REG_INFO_EXCEED, 3); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci if (state->chip_revision == 0) 21628c2ecf20Sopenharmony_ci tda1997x_reset_n1(state); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci /* 21658c2ecf20Sopenharmony_ci * No HDCP acknowledge when HDCP is disabled 21668c2ecf20Sopenharmony_ci * and reset SUS to force format detection 21678c2ecf20Sopenharmony_ci */ 21688c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, NACK_HDCP, true); 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci /* Set HPD low */ 21718c2ecf20Sopenharmony_ci tda1997x_manual_hpd(sd, HPD_LOW_BP); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* Configure receiver capabilities */ 21748c2ecf20Sopenharmony_ci io_write(sd, REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci /* Configure HDMI: Auto HDCP mode, packet controlled mute */ 21778c2ecf20Sopenharmony_ci reg = HDMI_CTRL_MUTE_AUTO << HDMI_CTRL_MUTE_SHIFT; 21788c2ecf20Sopenharmony_ci reg |= HDMI_CTRL_HDCP_AUTO << HDMI_CTRL_HDCP_SHIFT; 21798c2ecf20Sopenharmony_ci io_write(sd, REG_HDMI_CTRL, reg); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* reset start-up-sequencer to force format detection */ 21828c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, 0, true); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci /* disable matrix conversion */ 21858c2ecf20Sopenharmony_ci reg = io_read(sd, REG_VDP_CTRL); 21868c2ecf20Sopenharmony_ci reg |= VDP_CTRL_MATRIX_BP; 21878c2ecf20Sopenharmony_ci io_write(sd, REG_VDP_CTRL, reg); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci /* set video output mode */ 21908c2ecf20Sopenharmony_ci tda1997x_configure_vidout(state); 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci /* configure video output port */ 21938c2ecf20Sopenharmony_ci for (i = 0; i < 9; i++) { 21948c2ecf20Sopenharmony_ci v4l_dbg(1, debug, state->client, "vidout_cfg[%d]=0x%02x\n", i, 21958c2ecf20Sopenharmony_ci pdata->vidout_port_cfg[i]); 21968c2ecf20Sopenharmony_ci io_write(sd, REG_VP35_32_CTRL + i, pdata->vidout_port_cfg[i]); 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci /* configure audio output port */ 22008c2ecf20Sopenharmony_ci tda1997x_configure_audout(sd, 0); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* configure audio clock freq */ 22038c2ecf20Sopenharmony_ci switch (pdata->audout_mclk_fs) { 22048c2ecf20Sopenharmony_ci case 512: 22058c2ecf20Sopenharmony_ci reg = AUDIO_CLOCK_SEL_512FS; 22068c2ecf20Sopenharmony_ci break; 22078c2ecf20Sopenharmony_ci case 256: 22088c2ecf20Sopenharmony_ci reg = AUDIO_CLOCK_SEL_256FS; 22098c2ecf20Sopenharmony_ci break; 22108c2ecf20Sopenharmony_ci case 128: 22118c2ecf20Sopenharmony_ci reg = AUDIO_CLOCK_SEL_128FS; 22128c2ecf20Sopenharmony_ci break; 22138c2ecf20Sopenharmony_ci case 64: 22148c2ecf20Sopenharmony_ci reg = AUDIO_CLOCK_SEL_64FS; 22158c2ecf20Sopenharmony_ci break; 22168c2ecf20Sopenharmony_ci case 32: 22178c2ecf20Sopenharmony_ci reg = AUDIO_CLOCK_SEL_32FS; 22188c2ecf20Sopenharmony_ci break; 22198c2ecf20Sopenharmony_ci default: 22208c2ecf20Sopenharmony_ci reg = AUDIO_CLOCK_SEL_16FS; 22218c2ecf20Sopenharmony_ci break; 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci io_write(sd, REG_AUDIO_CLOCK, reg); 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci /* reset advanced infoframes (ISRC1/ISRC2/ACP) */ 22268c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, RESET_AI, false); 22278c2ecf20Sopenharmony_ci /* reset infoframe */ 22288c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, RESET_IF, false); 22298c2ecf20Sopenharmony_ci /* reset audio infoframes */ 22308c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); 22318c2ecf20Sopenharmony_ci /* reset gamut */ 22328c2ecf20Sopenharmony_ci tda1997x_hdmi_info_reset(sd, RESET_GAMUT, false); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci /* get initial HDMI status */ 22358c2ecf20Sopenharmony_ci state->hdmi_status = io_read(sd, REG_HDMI_FLAGS); 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci io_write(sd, REG_EDID_ENABLE, EDID_ENABLE_A_EN | EDID_ENABLE_B_EN); 22388c2ecf20Sopenharmony_ci return 0; 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_cistatic int tda1997x_set_power(struct tda1997x_state *state, bool on) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci int ret = 0; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci if (on) { 22468c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(TDA1997X_NUM_SUPPLIES, 22478c2ecf20Sopenharmony_ci state->supplies); 22488c2ecf20Sopenharmony_ci msleep(300); 22498c2ecf20Sopenharmony_ci } else { 22508c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, 22518c2ecf20Sopenharmony_ci state->supplies); 22528c2ecf20Sopenharmony_ci } 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci return ret; 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_cistatic const struct i2c_device_id tda1997x_i2c_id[] = { 22588c2ecf20Sopenharmony_ci {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, 22598c2ecf20Sopenharmony_ci {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, 22608c2ecf20Sopenharmony_ci { }, 22618c2ecf20Sopenharmony_ci}; 22628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_cistatic const struct of_device_id tda1997x_of_id[] __maybe_unused = { 22658c2ecf20Sopenharmony_ci { .compatible = "nxp,tda19971", .data = &tda1997x_chip_info[TDA19971] }, 22668c2ecf20Sopenharmony_ci { .compatible = "nxp,tda19973", .data = &tda1997x_chip_info[TDA19973] }, 22678c2ecf20Sopenharmony_ci { }, 22688c2ecf20Sopenharmony_ci}; 22698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tda1997x_of_id); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_cistatic int tda1997x_parse_dt(struct tda1997x_state *state) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata = &state->pdata; 22748c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; 22758c2ecf20Sopenharmony_ci struct device_node *ep; 22768c2ecf20Sopenharmony_ci struct device_node *np; 22778c2ecf20Sopenharmony_ci unsigned int flags; 22788c2ecf20Sopenharmony_ci const char *str; 22798c2ecf20Sopenharmony_ci int ret; 22808c2ecf20Sopenharmony_ci u32 v; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci /* 22838c2ecf20Sopenharmony_ci * setup default values: 22848c2ecf20Sopenharmony_ci * - HREF: active high from start to end of row 22858c2ecf20Sopenharmony_ci * - VS: Vertical Sync active high at beginning of frame 22868c2ecf20Sopenharmony_ci * - DE: Active high when data valid 22878c2ecf20Sopenharmony_ci * - A_CLK: 128*Fs 22888c2ecf20Sopenharmony_ci */ 22898c2ecf20Sopenharmony_ci pdata->vidout_sel_hs = HS_HREF_SEL_HREF_VHREF; 22908c2ecf20Sopenharmony_ci pdata->vidout_sel_vs = VS_VREF_SEL_VREF_HDMI; 22918c2ecf20Sopenharmony_ci pdata->vidout_sel_de = DE_FREF_SEL_DE_VHREF; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci np = state->client->dev.of_node; 22948c2ecf20Sopenharmony_ci ep = of_graph_get_next_endpoint(np, NULL); 22958c2ecf20Sopenharmony_ci if (!ep) 22968c2ecf20Sopenharmony_ci return -EINVAL; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); 22998c2ecf20Sopenharmony_ci if (ret) { 23008c2ecf20Sopenharmony_ci of_node_put(ep); 23018c2ecf20Sopenharmony_ci return ret; 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ci of_node_put(ep); 23048c2ecf20Sopenharmony_ci pdata->vidout_bus_type = bus_cfg.bus_type; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci /* polarity of HS/VS/DE */ 23078c2ecf20Sopenharmony_ci flags = bus_cfg.bus.parallel.flags; 23088c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) 23098c2ecf20Sopenharmony_ci pdata->vidout_inv_hs = 1; 23108c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) 23118c2ecf20Sopenharmony_ci pdata->vidout_inv_vs = 1; 23128c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_DATA_ACTIVE_LOW) 23138c2ecf20Sopenharmony_ci pdata->vidout_inv_de = 1; 23148c2ecf20Sopenharmony_ci pdata->vidout_bus_width = bus_cfg.bus.parallel.bus_width; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci /* video output port config */ 23178c2ecf20Sopenharmony_ci ret = of_property_count_u32_elems(np, "nxp,vidout-portcfg"); 23188c2ecf20Sopenharmony_ci if (ret > 0) { 23198c2ecf20Sopenharmony_ci u32 reg, val, i; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci for (i = 0; i < ret / 2 && i < 9; i++) { 23228c2ecf20Sopenharmony_ci of_property_read_u32_index(np, "nxp,vidout-portcfg", 23238c2ecf20Sopenharmony_ci i * 2, ®); 23248c2ecf20Sopenharmony_ci of_property_read_u32_index(np, "nxp,vidout-portcfg", 23258c2ecf20Sopenharmony_ci i * 2 + 1, &val); 23268c2ecf20Sopenharmony_ci if (reg < 9) 23278c2ecf20Sopenharmony_ci pdata->vidout_port_cfg[reg] = val; 23288c2ecf20Sopenharmony_ci } 23298c2ecf20Sopenharmony_ci } else { 23308c2ecf20Sopenharmony_ci v4l_err(state->client, "nxp,vidout-portcfg missing\n"); 23318c2ecf20Sopenharmony_ci return -EINVAL; 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci /* default to channel layout dictated by packet header */ 23358c2ecf20Sopenharmony_ci pdata->audout_layoutauto = true; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci pdata->audout_format = AUDFMT_TYPE_DISABLED; 23388c2ecf20Sopenharmony_ci if (!of_property_read_string(np, "nxp,audout-format", &str)) { 23398c2ecf20Sopenharmony_ci if (strcmp(str, "i2s") == 0) 23408c2ecf20Sopenharmony_ci pdata->audout_format = AUDFMT_TYPE_I2S; 23418c2ecf20Sopenharmony_ci else if (strcmp(str, "spdif") == 0) 23428c2ecf20Sopenharmony_ci pdata->audout_format = AUDFMT_TYPE_SPDIF; 23438c2ecf20Sopenharmony_ci else { 23448c2ecf20Sopenharmony_ci v4l_err(state->client, "nxp,audout-format invalid\n"); 23458c2ecf20Sopenharmony_ci return -EINVAL; 23468c2ecf20Sopenharmony_ci } 23478c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "nxp,audout-layout", &v)) { 23488c2ecf20Sopenharmony_ci switch (v) { 23498c2ecf20Sopenharmony_ci case 0: 23508c2ecf20Sopenharmony_ci case 1: 23518c2ecf20Sopenharmony_ci break; 23528c2ecf20Sopenharmony_ci default: 23538c2ecf20Sopenharmony_ci v4l_err(state->client, 23548c2ecf20Sopenharmony_ci "nxp,audout-layout invalid\n"); 23558c2ecf20Sopenharmony_ci return -EINVAL; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci pdata->audout_layout = v; 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "nxp,audout-width", &v)) { 23608c2ecf20Sopenharmony_ci switch (v) { 23618c2ecf20Sopenharmony_ci case 16: 23628c2ecf20Sopenharmony_ci case 32: 23638c2ecf20Sopenharmony_ci break; 23648c2ecf20Sopenharmony_ci default: 23658c2ecf20Sopenharmony_ci v4l_err(state->client, 23668c2ecf20Sopenharmony_ci "nxp,audout-width invalid\n"); 23678c2ecf20Sopenharmony_ci return -EINVAL; 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci pdata->audout_width = v; 23708c2ecf20Sopenharmony_ci } 23718c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "nxp,audout-mclk-fs", &v)) { 23728c2ecf20Sopenharmony_ci switch (v) { 23738c2ecf20Sopenharmony_ci case 512: 23748c2ecf20Sopenharmony_ci case 256: 23758c2ecf20Sopenharmony_ci case 128: 23768c2ecf20Sopenharmony_ci case 64: 23778c2ecf20Sopenharmony_ci case 32: 23788c2ecf20Sopenharmony_ci case 16: 23798c2ecf20Sopenharmony_ci break; 23808c2ecf20Sopenharmony_ci default: 23818c2ecf20Sopenharmony_ci v4l_err(state->client, 23828c2ecf20Sopenharmony_ci "nxp,audout-mclk-fs invalid\n"); 23838c2ecf20Sopenharmony_ci return -EINVAL; 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci pdata->audout_mclk_fs = v; 23868c2ecf20Sopenharmony_ci } 23878c2ecf20Sopenharmony_ci } 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci return 0; 23908c2ecf20Sopenharmony_ci} 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_cistatic int tda1997x_get_regulators(struct tda1997x_state *state) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci int i; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci for (i = 0; i < TDA1997X_NUM_SUPPLIES; i++) 23978c2ecf20Sopenharmony_ci state->supplies[i].supply = tda1997x_supply_name[i]; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci return devm_regulator_bulk_get(&state->client->dev, 24008c2ecf20Sopenharmony_ci TDA1997X_NUM_SUPPLIES, 24018c2ecf20Sopenharmony_ci state->supplies); 24028c2ecf20Sopenharmony_ci} 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_cistatic int tda1997x_identify_module(struct tda1997x_state *state) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sd; 24078c2ecf20Sopenharmony_ci enum tda1997x_type type; 24088c2ecf20Sopenharmony_ci u8 reg; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci /* Read chip configuration*/ 24118c2ecf20Sopenharmony_ci reg = io_read(sd, REG_CMTP_REG10); 24128c2ecf20Sopenharmony_ci state->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */ 24138c2ecf20Sopenharmony_ci state->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */ 24148c2ecf20Sopenharmony_ci state->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */ 24158c2ecf20Sopenharmony_ci state->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */ 24168c2ecf20Sopenharmony_ci switch ((reg >> 4) & 0x03) { 24178c2ecf20Sopenharmony_ci case 0x00: 24188c2ecf20Sopenharmony_ci type = TDA19971; 24198c2ecf20Sopenharmony_ci break; 24208c2ecf20Sopenharmony_ci case 0x02: 24218c2ecf20Sopenharmony_ci case 0x03: 24228c2ecf20Sopenharmony_ci type = TDA19973; 24238c2ecf20Sopenharmony_ci break; 24248c2ecf20Sopenharmony_ci default: 24258c2ecf20Sopenharmony_ci dev_err(&state->client->dev, "unsupported chip ID\n"); 24268c2ecf20Sopenharmony_ci return -EIO; 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci if (state->info->type != type) { 24298c2ecf20Sopenharmony_ci dev_err(&state->client->dev, "chip id mismatch\n"); 24308c2ecf20Sopenharmony_ci return -EIO; 24318c2ecf20Sopenharmony_ci } 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci /* read chip revision */ 24348c2ecf20Sopenharmony_ci state->chip_revision = io_read(sd, REG_CMTP_REG11); 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci return 0; 24378c2ecf20Sopenharmony_ci} 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_cistatic const struct media_entity_operations tda1997x_media_ops = { 24408c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 24418c2ecf20Sopenharmony_ci}; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 24458c2ecf20Sopenharmony_ci * HDMI Audio Codec 24468c2ecf20Sopenharmony_ci */ 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci/* refine sample-rate based on HDMI source */ 24498c2ecf20Sopenharmony_cistatic int tda1997x_pcm_startup(struct snd_pcm_substream *substream, 24508c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci struct tda1997x_state *state = snd_soc_dai_get_drvdata(dai); 24538c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 24548c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rtd = substream->runtime; 24558c2ecf20Sopenharmony_ci int rate, err; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci rate = state->audio_samplerate; 24588c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE, 24598c2ecf20Sopenharmony_ci rate, rate); 24608c2ecf20Sopenharmony_ci if (err < 0) { 24618c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to constrain samplerate to %dHz\n", 24628c2ecf20Sopenharmony_ci rate); 24638c2ecf20Sopenharmony_ci return err; 24648c2ecf20Sopenharmony_ci } 24658c2ecf20Sopenharmony_ci dev_info(component->dev, "set samplerate constraint to %dHz\n", rate); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci return 0; 24688c2ecf20Sopenharmony_ci} 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tda1997x_dai_ops = { 24718c2ecf20Sopenharmony_ci .startup = tda1997x_pcm_startup, 24728c2ecf20Sopenharmony_ci}; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tda1997x_audio_dai = { 24758c2ecf20Sopenharmony_ci .name = "tda1997x", 24768c2ecf20Sopenharmony_ci .capture = { 24778c2ecf20Sopenharmony_ci .stream_name = "Capture", 24788c2ecf20Sopenharmony_ci .channels_min = 2, 24798c2ecf20Sopenharmony_ci .channels_max = 8, 24808c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 24818c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | 24828c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | 24838c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_192000, 24848c2ecf20Sopenharmony_ci }, 24858c2ecf20Sopenharmony_ci .ops = &tda1997x_dai_ops, 24868c2ecf20Sopenharmony_ci}; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_cistatic int tda1997x_codec_probe(struct snd_soc_component *component) 24898c2ecf20Sopenharmony_ci{ 24908c2ecf20Sopenharmony_ci return 0; 24918c2ecf20Sopenharmony_ci} 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_cistatic void tda1997x_codec_remove(struct snd_soc_component *component) 24948c2ecf20Sopenharmony_ci{ 24958c2ecf20Sopenharmony_ci} 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_cistatic struct snd_soc_component_driver tda1997x_codec_driver = { 24988c2ecf20Sopenharmony_ci .probe = tda1997x_codec_probe, 24998c2ecf20Sopenharmony_ci .remove = tda1997x_codec_remove, 25008c2ecf20Sopenharmony_ci .idle_bias_on = 1, 25018c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 25028c2ecf20Sopenharmony_ci .endianness = 1, 25038c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 25048c2ecf20Sopenharmony_ci}; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_cistatic int tda1997x_probe(struct i2c_client *client, 25078c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 25088c2ecf20Sopenharmony_ci{ 25098c2ecf20Sopenharmony_ci struct tda1997x_state *state; 25108c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata; 25118c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 25128c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 25138c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl; 25148c2ecf20Sopenharmony_ci static const struct v4l2_dv_timings cea1920x1080 = 25158c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080P60; 25168c2ecf20Sopenharmony_ci u32 *mbus_codes; 25178c2ecf20Sopenharmony_ci int i, ret; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci /* Check if the adapter supports the needed features */ 25208c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 25218c2ecf20Sopenharmony_ci return -EIO; 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct tda1997x_state), GFP_KERNEL); 25248c2ecf20Sopenharmony_ci if (!state) 25258c2ecf20Sopenharmony_ci return -ENOMEM; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci state->client = client; 25288c2ecf20Sopenharmony_ci pdata = &state->pdata; 25298c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { 25308c2ecf20Sopenharmony_ci const struct of_device_id *oid; 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci oid = of_match_node(tda1997x_of_id, client->dev.of_node); 25338c2ecf20Sopenharmony_ci state->info = oid->data; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci ret = tda1997x_parse_dt(state); 25368c2ecf20Sopenharmony_ci if (ret < 0) { 25378c2ecf20Sopenharmony_ci v4l_err(client, "DT parsing error\n"); 25388c2ecf20Sopenharmony_ci goto err_free_state; 25398c2ecf20Sopenharmony_ci } 25408c2ecf20Sopenharmony_ci } else if (client->dev.platform_data) { 25418c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata = 25428c2ecf20Sopenharmony_ci client->dev.platform_data; 25438c2ecf20Sopenharmony_ci state->info = 25448c2ecf20Sopenharmony_ci (const struct tda1997x_chip_info *)id->driver_data; 25458c2ecf20Sopenharmony_ci state->pdata = *pdata; 25468c2ecf20Sopenharmony_ci } else { 25478c2ecf20Sopenharmony_ci v4l_err(client, "No platform data\n"); 25488c2ecf20Sopenharmony_ci ret = -ENODEV; 25498c2ecf20Sopenharmony_ci goto err_free_state; 25508c2ecf20Sopenharmony_ci } 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci ret = tda1997x_get_regulators(state); 25538c2ecf20Sopenharmony_ci if (ret) 25548c2ecf20Sopenharmony_ci goto err_free_state; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci ret = tda1997x_set_power(state, 1); 25578c2ecf20Sopenharmony_ci if (ret) 25588c2ecf20Sopenharmony_ci goto err_free_state; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci mutex_init(&state->page_lock); 25618c2ecf20Sopenharmony_ci mutex_init(&state->lock); 25628c2ecf20Sopenharmony_ci state->page = 0xff; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&state->delayed_work_enable_hpd, 25658c2ecf20Sopenharmony_ci tda1997x_delayed_work_enable_hpd); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci /* set video format based on chip and bus width */ 25688c2ecf20Sopenharmony_ci ret = tda1997x_identify_module(state); 25698c2ecf20Sopenharmony_ci if (ret) 25708c2ecf20Sopenharmony_ci goto err_free_mutex; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci /* initialize subdev */ 25738c2ecf20Sopenharmony_ci sd = &state->sd; 25748c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); 25758c2ecf20Sopenharmony_ci snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", 25768c2ecf20Sopenharmony_ci id->name, i2c_adapter_id(client->adapter), 25778c2ecf20Sopenharmony_ci client->addr); 25788c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 25798c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_DV_DECODER; 25808c2ecf20Sopenharmony_ci sd->entity.ops = &tda1997x_media_ops; 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci /* set allowed mbus modes based on chip, bus-type, and bus-width */ 25838c2ecf20Sopenharmony_ci i = 0; 25848c2ecf20Sopenharmony_ci mbus_codes = state->mbus_codes; 25858c2ecf20Sopenharmony_ci switch (state->info->type) { 25868c2ecf20Sopenharmony_ci case TDA19973: 25878c2ecf20Sopenharmony_ci switch (pdata->vidout_bus_type) { 25888c2ecf20Sopenharmony_ci case V4L2_MBUS_PARALLEL: 25898c2ecf20Sopenharmony_ci switch (pdata->vidout_bus_width) { 25908c2ecf20Sopenharmony_ci case 36: 25918c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_RGB121212_1X36; 25928c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_YUV12_1X36; 25938c2ecf20Sopenharmony_ci fallthrough; 25948c2ecf20Sopenharmony_ci case 24: 25958c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; 25968c2ecf20Sopenharmony_ci break; 25978c2ecf20Sopenharmony_ci } 25988c2ecf20Sopenharmony_ci break; 25998c2ecf20Sopenharmony_ci case V4L2_MBUS_BT656: 26008c2ecf20Sopenharmony_ci switch (pdata->vidout_bus_width) { 26018c2ecf20Sopenharmony_ci case 36: 26028c2ecf20Sopenharmony_ci case 24: 26038c2ecf20Sopenharmony_ci case 12: 26048c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; 26058c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; 26068c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; 26078c2ecf20Sopenharmony_ci break; 26088c2ecf20Sopenharmony_ci } 26098c2ecf20Sopenharmony_ci break; 26108c2ecf20Sopenharmony_ci default: 26118c2ecf20Sopenharmony_ci break; 26128c2ecf20Sopenharmony_ci } 26138c2ecf20Sopenharmony_ci break; 26148c2ecf20Sopenharmony_ci case TDA19971: 26158c2ecf20Sopenharmony_ci switch (pdata->vidout_bus_type) { 26168c2ecf20Sopenharmony_ci case V4L2_MBUS_PARALLEL: 26178c2ecf20Sopenharmony_ci switch (pdata->vidout_bus_width) { 26188c2ecf20Sopenharmony_ci case 24: 26198c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_RGB888_1X24; 26208c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_YUV8_1X24; 26218c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; 26228c2ecf20Sopenharmony_ci fallthrough; 26238c2ecf20Sopenharmony_ci case 20: 26248c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_1X20; 26258c2ecf20Sopenharmony_ci fallthrough; 26268c2ecf20Sopenharmony_ci case 16: 26278c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_1X16; 26288c2ecf20Sopenharmony_ci break; 26298c2ecf20Sopenharmony_ci } 26308c2ecf20Sopenharmony_ci break; 26318c2ecf20Sopenharmony_ci case V4L2_MBUS_BT656: 26328c2ecf20Sopenharmony_ci switch (pdata->vidout_bus_width) { 26338c2ecf20Sopenharmony_ci case 24: 26348c2ecf20Sopenharmony_ci case 20: 26358c2ecf20Sopenharmony_ci case 16: 26368c2ecf20Sopenharmony_ci case 12: 26378c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; 26388c2ecf20Sopenharmony_ci fallthrough; 26398c2ecf20Sopenharmony_ci case 10: 26408c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; 26418c2ecf20Sopenharmony_ci fallthrough; 26428c2ecf20Sopenharmony_ci case 8: 26438c2ecf20Sopenharmony_ci mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; 26448c2ecf20Sopenharmony_ci break; 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci break; 26478c2ecf20Sopenharmony_ci default: 26488c2ecf20Sopenharmony_ci break; 26498c2ecf20Sopenharmony_ci } 26508c2ecf20Sopenharmony_ci break; 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci if (WARN_ON(i > ARRAY_SIZE(state->mbus_codes))) { 26538c2ecf20Sopenharmony_ci ret = -EINVAL; 26548c2ecf20Sopenharmony_ci goto err_free_mutex; 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci /* default format */ 26588c2ecf20Sopenharmony_ci tda1997x_setup_format(state, state->mbus_codes[0]); 26598c2ecf20Sopenharmony_ci state->timings = cea1920x1080; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci /* 26628c2ecf20Sopenharmony_ci * default to SRGB full range quantization 26638c2ecf20Sopenharmony_ci * (in case we don't get an infoframe such as DVI signal 26648c2ecf20Sopenharmony_ci */ 26658c2ecf20Sopenharmony_ci state->colorimetry.colorspace = V4L2_COLORSPACE_SRGB; 26668c2ecf20Sopenharmony_ci state->colorimetry.quantization = V4L2_QUANTIZATION_FULL_RANGE; 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci /* disable/reset HDCP to get correct I2C access to Rx HDMI */ 26698c2ecf20Sopenharmony_ci io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci /* 26728c2ecf20Sopenharmony_ci * if N2 version, reset compdel_bp as it may generate some small pixel 26738c2ecf20Sopenharmony_ci * shifts in case of embedded sync/or delay lower than 4 26748c2ecf20Sopenharmony_ci */ 26758c2ecf20Sopenharmony_ci if (state->chip_revision != 0) { 26768c2ecf20Sopenharmony_ci io_write(sd, REG_MAN_SUS_HDMI_SEL, 0x00); 26778c2ecf20Sopenharmony_ci io_write(sd, REG_VDP_CTRL, 0x1f); 26788c2ecf20Sopenharmony_ci } 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci v4l_info(client, "NXP %s N%d detected\n", state->info->name, 26818c2ecf20Sopenharmony_ci state->chip_revision + 1); 26828c2ecf20Sopenharmony_ci v4l_info(client, "video: %dbit %s %d formats available\n", 26838c2ecf20Sopenharmony_ci pdata->vidout_bus_width, 26848c2ecf20Sopenharmony_ci (pdata->vidout_bus_type == V4L2_MBUS_PARALLEL) ? 26858c2ecf20Sopenharmony_ci "parallel" : "BT656", 26868c2ecf20Sopenharmony_ci i); 26878c2ecf20Sopenharmony_ci if (pdata->audout_format) { 26888c2ecf20Sopenharmony_ci v4l_info(client, "audio: %dch %s layout%d sysclk=%d*fs\n", 26898c2ecf20Sopenharmony_ci pdata->audout_layout ? 2 : 8, 26908c2ecf20Sopenharmony_ci audfmt_names[pdata->audout_format], 26918c2ecf20Sopenharmony_ci pdata->audout_layout, 26928c2ecf20Sopenharmony_ci pdata->audout_mclk_fs); 26938c2ecf20Sopenharmony_ci } 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03); 26968c2ecf20Sopenharmony_ci state->client_cec = devm_i2c_new_dummy_device(&client->dev, 26978c2ecf20Sopenharmony_ci client->adapter, ret); 26988c2ecf20Sopenharmony_ci if (IS_ERR(state->client_cec)) { 26998c2ecf20Sopenharmony_ci ret = PTR_ERR(state->client_cec); 27008c2ecf20Sopenharmony_ci goto err_free_mutex; 27018c2ecf20Sopenharmony_ci } 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci v4l_info(client, "CEC slave address 0x%02x\n", ret); 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci ret = tda1997x_core_init(sd); 27068c2ecf20Sopenharmony_ci if (ret) 27078c2ecf20Sopenharmony_ci goto err_free_mutex; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci /* control handlers */ 27108c2ecf20Sopenharmony_ci hdl = &state->hdl; 27118c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 3); 27128c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops, 27138c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_IT_CONTENT_TYPE, 27148c2ecf20Sopenharmony_ci V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0, 27158c2ecf20Sopenharmony_ci V4L2_DV_IT_CONTENT_TYPE_NO_ITC); 27168c2ecf20Sopenharmony_ci if (ctrl) 27178c2ecf20Sopenharmony_ci ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 27188c2ecf20Sopenharmony_ci /* custom controls */ 27198c2ecf20Sopenharmony_ci state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, 27208c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); 27218c2ecf20Sopenharmony_ci state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, 27228c2ecf20Sopenharmony_ci &tda1997x_ctrl_ops, 27238c2ecf20Sopenharmony_ci V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, 27248c2ecf20Sopenharmony_ci V4L2_DV_RGB_RANGE_AUTO); 27258c2ecf20Sopenharmony_ci state->sd.ctrl_handler = hdl; 27268c2ecf20Sopenharmony_ci if (hdl->error) { 27278c2ecf20Sopenharmony_ci ret = hdl->error; 27288c2ecf20Sopenharmony_ci goto err_free_handler; 27298c2ecf20Sopenharmony_ci } 27308c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(hdl); 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci /* initialize source pads */ 27338c2ecf20Sopenharmony_ci state->pads[TDA1997X_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 27348c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, TDA1997X_NUM_PADS, 27358c2ecf20Sopenharmony_ci state->pads); 27368c2ecf20Sopenharmony_ci if (ret) { 27378c2ecf20Sopenharmony_ci v4l_err(client, "failed entity_init: %d", ret); 27388c2ecf20Sopenharmony_ci goto err_free_handler; 27398c2ecf20Sopenharmony_ci } 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(sd); 27428c2ecf20Sopenharmony_ci if (ret) 27438c2ecf20Sopenharmony_ci goto err_free_media; 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_ci /* register audio DAI */ 27468c2ecf20Sopenharmony_ci if (pdata->audout_format) { 27478c2ecf20Sopenharmony_ci u64 formats; 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci if (pdata->audout_width == 32) 27508c2ecf20Sopenharmony_ci formats = SNDRV_PCM_FMTBIT_S32_LE; 27518c2ecf20Sopenharmony_ci else 27528c2ecf20Sopenharmony_ci formats = SNDRV_PCM_FMTBIT_S16_LE; 27538c2ecf20Sopenharmony_ci tda1997x_audio_dai.capture.formats = formats; 27548c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&state->client->dev, 27558c2ecf20Sopenharmony_ci &tda1997x_codec_driver, 27568c2ecf20Sopenharmony_ci &tda1997x_audio_dai, 1); 27578c2ecf20Sopenharmony_ci if (ret) { 27588c2ecf20Sopenharmony_ci dev_err(&client->dev, "register audio codec failed\n"); 27598c2ecf20Sopenharmony_ci goto err_free_media; 27608c2ecf20Sopenharmony_ci } 27618c2ecf20Sopenharmony_ci dev_set_drvdata(&state->client->dev, state); 27628c2ecf20Sopenharmony_ci v4l_info(state->client, "registered audio codec\n"); 27638c2ecf20Sopenharmony_ci } 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci /* request irq */ 27668c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 27678c2ecf20Sopenharmony_ci NULL, tda1997x_isr_thread, 27688c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 27698c2ecf20Sopenharmony_ci KBUILD_MODNAME, state); 27708c2ecf20Sopenharmony_ci if (ret) { 27718c2ecf20Sopenharmony_ci v4l_err(client, "irq%d reg failed: %d\n", client->irq, ret); 27728c2ecf20Sopenharmony_ci goto err_free_media; 27738c2ecf20Sopenharmony_ci } 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci return 0; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_cierr_free_media: 27788c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 27798c2ecf20Sopenharmony_cierr_free_handler: 27808c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 27818c2ecf20Sopenharmony_cierr_free_mutex: 27828c2ecf20Sopenharmony_ci cancel_delayed_work(&state->delayed_work_enable_hpd); 27838c2ecf20Sopenharmony_ci mutex_destroy(&state->page_lock); 27848c2ecf20Sopenharmony_ci mutex_destroy(&state->lock); 27858c2ecf20Sopenharmony_cierr_free_state: 27868c2ecf20Sopenharmony_ci kfree(state); 27878c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s failed: %d\n", __func__, ret); 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci return ret; 27908c2ecf20Sopenharmony_ci} 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_cistatic int tda1997x_remove(struct i2c_client *client) 27938c2ecf20Sopenharmony_ci{ 27948c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 27958c2ecf20Sopenharmony_ci struct tda1997x_state *state = to_state(sd); 27968c2ecf20Sopenharmony_ci struct tda1997x_platform_data *pdata = &state->pdata; 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci if (pdata->audout_format) { 27998c2ecf20Sopenharmony_ci mutex_destroy(&state->audio_lock); 28008c2ecf20Sopenharmony_ci } 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci disable_irq(state->client->irq); 28038c2ecf20Sopenharmony_ci tda1997x_power_mode(state, 0); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 28068c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 28078c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 28088c2ecf20Sopenharmony_ci regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies); 28098c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&state->delayed_work_enable_hpd); 28108c2ecf20Sopenharmony_ci mutex_destroy(&state->page_lock); 28118c2ecf20Sopenharmony_ci mutex_destroy(&state->lock); 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci kfree(state); 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_ci return 0; 28168c2ecf20Sopenharmony_ci} 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_cistatic struct i2c_driver tda1997x_i2c_driver = { 28198c2ecf20Sopenharmony_ci .driver = { 28208c2ecf20Sopenharmony_ci .name = "tda1997x", 28218c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(tda1997x_of_id), 28228c2ecf20Sopenharmony_ci }, 28238c2ecf20Sopenharmony_ci .probe = tda1997x_probe, 28248c2ecf20Sopenharmony_ci .remove = tda1997x_remove, 28258c2ecf20Sopenharmony_ci .id_table = tda1997x_i2c_id, 28268c2ecf20Sopenharmony_ci}; 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_cimodule_i2c_driver(tda1997x_i2c_driver); 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>"); 28318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TDA1997X HDMI Receiver driver"); 28328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2833