18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "dp_reg.h" 118c2ecf20Sopenharmony_ci#include "dp_link.h" 128c2ecf20Sopenharmony_ci#include "dp_panel.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define DP_TEST_REQUEST_MASK 0x7F 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cienum audio_sample_rate { 178c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_32_KHZ = 0x00, 188c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_44_1_KHZ = 0x01, 198c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_48_KHZ = 0x02, 208c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_88_2_KHZ = 0x03, 218c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_96_KHZ = 0x04, 228c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_176_4_KHZ = 0x05, 238c2ecf20Sopenharmony_ci AUDIO_SAMPLE_RATE_192_KHZ = 0x06, 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cienum audio_pattern_type { 278c2ecf20Sopenharmony_ci AUDIO_TEST_PATTERN_OPERATOR_DEFINED = 0x00, 288c2ecf20Sopenharmony_ci AUDIO_TEST_PATTERN_SAWTOOTH = 0x01, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct dp_link_request { 328c2ecf20Sopenharmony_ci u32 test_requested; 338c2ecf20Sopenharmony_ci u32 test_link_rate; 348c2ecf20Sopenharmony_ci u32 test_lane_count; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct dp_link_private { 388c2ecf20Sopenharmony_ci u32 prev_sink_count; 398c2ecf20Sopenharmony_ci struct device *dev; 408c2ecf20Sopenharmony_ci struct drm_dp_aux *aux; 418c2ecf20Sopenharmony_ci struct dp_link dp_link; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci struct dp_link_request request; 448c2ecf20Sopenharmony_ci struct mutex psm_mutex; 458c2ecf20Sopenharmony_ci u8 link_status[DP_LINK_STATUS_SIZE]; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int dp_aux_link_power_up(struct drm_dp_aux *aux, 498c2ecf20Sopenharmony_ci struct dp_link_info *link) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci u8 value; 528c2ecf20Sopenharmony_ci int err; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (link->revision < 0x11) 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); 588c2ecf20Sopenharmony_ci if (err < 0) 598c2ecf20Sopenharmony_ci return err; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci value &= ~DP_SET_POWER_MASK; 628c2ecf20Sopenharmony_ci value |= DP_SET_POWER_D0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); 658c2ecf20Sopenharmony_ci if (err < 0) 668c2ecf20Sopenharmony_ci return err; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int dp_aux_link_power_down(struct drm_dp_aux *aux, 748c2ecf20Sopenharmony_ci struct dp_link_info *link) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u8 value; 778c2ecf20Sopenharmony_ci int err; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (link->revision < 0x11) 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); 838c2ecf20Sopenharmony_ci if (err < 0) 848c2ecf20Sopenharmony_ci return err; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci value &= ~DP_SET_POWER_MASK; 878c2ecf20Sopenharmony_ci value |= DP_SET_POWER_D3; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); 908c2ecf20Sopenharmony_ci if (err < 0) 918c2ecf20Sopenharmony_ci return err; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int dp_link_get_period(struct dp_link_private *link, int const addr) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int ret = 0; 998c2ecf20Sopenharmony_ci u8 data; 1008c2ecf20Sopenharmony_ci u32 const max_audio_period = 0xA; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* TEST_AUDIO_PERIOD_CH_XX */ 1038c2ecf20Sopenharmony_ci if (drm_dp_dpcd_readb(link->aux, addr, &data) < 0) { 1048c2ecf20Sopenharmony_ci DRM_ERROR("failed to read test_audio_period (0x%x)\n", addr); 1058c2ecf20Sopenharmony_ci ret = -EINVAL; 1068c2ecf20Sopenharmony_ci goto exit; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Period - Bits 3:0 */ 1108c2ecf20Sopenharmony_ci data = data & 0xF; 1118c2ecf20Sopenharmony_ci if ((int)data > max_audio_period) { 1128c2ecf20Sopenharmony_ci DRM_ERROR("invalid test_audio_period_ch_1 = 0x%x\n", data); 1138c2ecf20Sopenharmony_ci ret = -EINVAL; 1148c2ecf20Sopenharmony_ci goto exit; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = data; 1188c2ecf20Sopenharmony_ciexit: 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int dp_link_parse_audio_channel_period(struct dp_link_private *link) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int ret = 0; 1258c2ecf20Sopenharmony_ci struct dp_link_test_audio *req = &link->dp_link.test_audio; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH1); 1288c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1298c2ecf20Sopenharmony_ci goto exit; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci req->test_audio_period_ch_1 = ret; 1328c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_1 = 0x%x\n", ret); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH2); 1358c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1368c2ecf20Sopenharmony_ci goto exit; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci req->test_audio_period_ch_2 = ret; 1398c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_2 = 0x%x\n", ret); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */ 1428c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH3); 1438c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1448c2ecf20Sopenharmony_ci goto exit; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci req->test_audio_period_ch_3 = ret; 1478c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_3 = 0x%x\n", ret); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH4); 1508c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1518c2ecf20Sopenharmony_ci goto exit; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci req->test_audio_period_ch_4 = ret; 1548c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_4 = 0x%x\n", ret); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH5); 1578c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1588c2ecf20Sopenharmony_ci goto exit; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci req->test_audio_period_ch_5 = ret; 1618c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_5 = 0x%x\n", ret); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH6); 1648c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1658c2ecf20Sopenharmony_ci goto exit; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci req->test_audio_period_ch_6 = ret; 1688c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_6 = 0x%x\n", ret); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH7); 1718c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1728c2ecf20Sopenharmony_ci goto exit; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci req->test_audio_period_ch_7 = ret; 1758c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_7 = 0x%x\n", ret); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH8); 1788c2ecf20Sopenharmony_ci if (ret == -EINVAL) 1798c2ecf20Sopenharmony_ci goto exit; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci req->test_audio_period_ch_8 = ret; 1828c2ecf20Sopenharmony_ci DRM_DEBUG_DP("test_audio_period_ch_8 = 0x%x\n", ret); 1838c2ecf20Sopenharmony_ciexit: 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int dp_link_parse_audio_pattern_type(struct dp_link_private *link) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci int ret = 0; 1908c2ecf20Sopenharmony_ci u8 data; 1918c2ecf20Sopenharmony_ci ssize_t rlen; 1928c2ecf20Sopenharmony_ci int const max_audio_pattern_type = 0x1; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, 1958c2ecf20Sopenharmony_ci DP_TEST_AUDIO_PATTERN_TYPE, &data); 1968c2ecf20Sopenharmony_ci if (rlen < 0) { 1978c2ecf20Sopenharmony_ci DRM_ERROR("failed to read link audio mode. rlen=%zd\n", rlen); 1988c2ecf20Sopenharmony_ci return rlen; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Audio Pattern Type - Bits 7:0 */ 2028c2ecf20Sopenharmony_ci if ((int)data > max_audio_pattern_type) { 2038c2ecf20Sopenharmony_ci DRM_ERROR("invalid audio pattern type = 0x%x\n", data); 2048c2ecf20Sopenharmony_ci ret = -EINVAL; 2058c2ecf20Sopenharmony_ci goto exit; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci link->dp_link.test_audio.test_audio_pattern_type = data; 2098c2ecf20Sopenharmony_ci DRM_DEBUG_DP("audio pattern type = 0x%x\n", data); 2108c2ecf20Sopenharmony_ciexit: 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int dp_link_parse_audio_mode(struct dp_link_private *link) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int ret = 0; 2178c2ecf20Sopenharmony_ci u8 data; 2188c2ecf20Sopenharmony_ci ssize_t rlen; 2198c2ecf20Sopenharmony_ci int const max_audio_sampling_rate = 0x6; 2208c2ecf20Sopenharmony_ci int const max_audio_channel_count = 0x8; 2218c2ecf20Sopenharmony_ci int sampling_rate = 0x0; 2228c2ecf20Sopenharmony_ci int channel_count = 0x0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_AUDIO_MODE, &data); 2258c2ecf20Sopenharmony_ci if (rlen < 0) { 2268c2ecf20Sopenharmony_ci DRM_ERROR("failed to read link audio mode. rlen=%zd\n", rlen); 2278c2ecf20Sopenharmony_ci return rlen; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Sampling Rate - Bits 3:0 */ 2318c2ecf20Sopenharmony_ci sampling_rate = data & 0xF; 2328c2ecf20Sopenharmony_ci if (sampling_rate > max_audio_sampling_rate) { 2338c2ecf20Sopenharmony_ci DRM_ERROR("sampling rate (0x%x) greater than max (0x%x)\n", 2348c2ecf20Sopenharmony_ci sampling_rate, max_audio_sampling_rate); 2358c2ecf20Sopenharmony_ci ret = -EINVAL; 2368c2ecf20Sopenharmony_ci goto exit; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Channel Count - Bits 7:4 */ 2408c2ecf20Sopenharmony_ci channel_count = ((data & 0xF0) >> 4) + 1; 2418c2ecf20Sopenharmony_ci if (channel_count > max_audio_channel_count) { 2428c2ecf20Sopenharmony_ci DRM_ERROR("channel_count (0x%x) greater than max (0x%x)\n", 2438c2ecf20Sopenharmony_ci channel_count, max_audio_channel_count); 2448c2ecf20Sopenharmony_ci ret = -EINVAL; 2458c2ecf20Sopenharmony_ci goto exit; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci link->dp_link.test_audio.test_audio_sampling_rate = sampling_rate; 2498c2ecf20Sopenharmony_ci link->dp_link.test_audio.test_audio_channel_count = channel_count; 2508c2ecf20Sopenharmony_ci DRM_DEBUG_DP("sampling_rate = 0x%x, channel_count = 0x%x\n", 2518c2ecf20Sopenharmony_ci sampling_rate, channel_count); 2528c2ecf20Sopenharmony_ciexit: 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int dp_link_parse_audio_pattern_params(struct dp_link_private *link) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int ret = 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = dp_link_parse_audio_mode(link); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci goto exit; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = dp_link_parse_audio_pattern_type(link); 2658c2ecf20Sopenharmony_ci if (ret) 2668c2ecf20Sopenharmony_ci goto exit; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret = dp_link_parse_audio_channel_period(link); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciexit: 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic bool dp_link_is_video_pattern_valid(u32 pattern) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci switch (pattern) { 2778c2ecf20Sopenharmony_ci case DP_NO_TEST_PATTERN: 2788c2ecf20Sopenharmony_ci case DP_COLOR_RAMP: 2798c2ecf20Sopenharmony_ci case DP_BLACK_AND_WHITE_VERTICAL_LINES: 2808c2ecf20Sopenharmony_ci case DP_COLOR_SQUARE: 2818c2ecf20Sopenharmony_ci return true; 2828c2ecf20Sopenharmony_ci default: 2838c2ecf20Sopenharmony_ci return false; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/** 2888c2ecf20Sopenharmony_ci * dp_link_is_bit_depth_valid() - validates the bit depth requested 2898c2ecf20Sopenharmony_ci * @tbd: bit depth requested by the sink 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * Returns true if the requested bit depth is supported. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic bool dp_link_is_bit_depth_valid(u32 tbd) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci /* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */ 2968c2ecf20Sopenharmony_ci switch (tbd) { 2978c2ecf20Sopenharmony_ci case DP_TEST_BIT_DEPTH_6: 2988c2ecf20Sopenharmony_ci case DP_TEST_BIT_DEPTH_8: 2998c2ecf20Sopenharmony_ci case DP_TEST_BIT_DEPTH_10: 3008c2ecf20Sopenharmony_ci return true; 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci return false; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int dp_link_parse_timing_params1(struct dp_link_private *link, 3078c2ecf20Sopenharmony_ci int addr, int len, u32 *val) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci u8 bp[2]; 3108c2ecf20Sopenharmony_ci int rlen; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (len != 2) 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Read the requested video link pattern (Byte 0x221). */ 3168c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_read(link->aux, addr, bp, len); 3178c2ecf20Sopenharmony_ci if (rlen < len) { 3188c2ecf20Sopenharmony_ci DRM_ERROR("failed to read 0x%x\n", addr); 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci *val = bp[1] | (bp[0] << 8); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int dp_link_parse_timing_params2(struct dp_link_private *link, 3288c2ecf20Sopenharmony_ci int addr, int len, 3298c2ecf20Sopenharmony_ci u32 *val1, u32 *val2) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci u8 bp[2]; 3328c2ecf20Sopenharmony_ci int rlen; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (len != 2) 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Read the requested video link pattern (Byte 0x221). */ 3388c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_read(link->aux, addr, bp, len); 3398c2ecf20Sopenharmony_ci if (rlen < len) { 3408c2ecf20Sopenharmony_ci DRM_ERROR("failed to read 0x%x\n", addr); 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci *val1 = (bp[0] & BIT(7)) >> 7; 3458c2ecf20Sopenharmony_ci *val2 = bp[1] | ((bp[0] & 0x7F) << 8); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int dp_link_parse_timing_params3(struct dp_link_private *link, 3518c2ecf20Sopenharmony_ci int addr, u32 *val) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci u8 bp; 3548c2ecf20Sopenharmony_ci u32 len = 1; 3558c2ecf20Sopenharmony_ci int rlen; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_read(link->aux, addr, &bp, len); 3588c2ecf20Sopenharmony_ci if (rlen < 1) { 3598c2ecf20Sopenharmony_ci DRM_ERROR("failed to read 0x%x\n", addr); 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci *val = bp; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/** 3688c2ecf20Sopenharmony_ci * dp_parse_video_pattern_params() - parses video pattern parameters from DPCD 3698c2ecf20Sopenharmony_ci * @link: Display Port Driver data 3708c2ecf20Sopenharmony_ci * 3718c2ecf20Sopenharmony_ci * Returns 0 if it successfully parses the video link pattern and the link 3728c2ecf20Sopenharmony_ci * bit depth requested by the sink and, and if the values parsed are valid. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_cistatic int dp_link_parse_video_pattern_params(struct dp_link_private *link) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci int ret = 0; 3778c2ecf20Sopenharmony_ci ssize_t rlen; 3788c2ecf20Sopenharmony_ci u8 bp; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_PATTERN, &bp); 3818c2ecf20Sopenharmony_ci if (rlen < 0) { 3828c2ecf20Sopenharmony_ci DRM_ERROR("failed to read link video pattern. rlen=%zd\n", 3838c2ecf20Sopenharmony_ci rlen); 3848c2ecf20Sopenharmony_ci return rlen; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!dp_link_is_video_pattern_valid(bp)) { 3888c2ecf20Sopenharmony_ci DRM_ERROR("invalid link video pattern = 0x%x\n", bp); 3898c2ecf20Sopenharmony_ci ret = -EINVAL; 3908c2ecf20Sopenharmony_ci return ret; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci link->dp_link.test_video.test_video_pattern = bp; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Read the requested color bit depth and dynamic range (Byte 0x232) */ 3968c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_MISC0, &bp); 3978c2ecf20Sopenharmony_ci if (rlen < 0) { 3988c2ecf20Sopenharmony_ci DRM_ERROR("failed to read link bit depth. rlen=%zd\n", rlen); 3998c2ecf20Sopenharmony_ci return rlen; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Dynamic Range */ 4038c2ecf20Sopenharmony_ci link->dp_link.test_video.test_dyn_range = 4048c2ecf20Sopenharmony_ci (bp & DP_TEST_DYNAMIC_RANGE_CEA); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Color bit depth */ 4078c2ecf20Sopenharmony_ci bp &= DP_TEST_BIT_DEPTH_MASK; 4088c2ecf20Sopenharmony_ci if (!dp_link_is_bit_depth_valid(bp)) { 4098c2ecf20Sopenharmony_ci DRM_ERROR("invalid link bit depth = 0x%x\n", bp); 4108c2ecf20Sopenharmony_ci ret = -EINVAL; 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci link->dp_link.test_video.test_bit_depth = bp; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* resolution timing params */ 4178c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params1(link, DP_TEST_H_TOTAL_HI, 2, 4188c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_h_total); 4198c2ecf20Sopenharmony_ci if (ret) { 4208c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_htotal(DP_TEST_H_TOTAL_HI)\n"); 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params1(link, DP_TEST_V_TOTAL_HI, 2, 4258c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_v_total); 4268c2ecf20Sopenharmony_ci if (ret) { 4278c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_v_total(DP_TEST_V_TOTAL_HI)\n"); 4288c2ecf20Sopenharmony_ci return ret; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params1(link, DP_TEST_H_START_HI, 2, 4328c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_h_start); 4338c2ecf20Sopenharmony_ci if (ret) { 4348c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_h_start(DP_TEST_H_START_HI)\n"); 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params1(link, DP_TEST_V_START_HI, 2, 4398c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_v_start); 4408c2ecf20Sopenharmony_ci if (ret) { 4418c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_v_start(DP_TEST_V_START_HI)\n"); 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params2(link, DP_TEST_HSYNC_HI, 2, 4468c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_hsync_pol, 4478c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_hsync_width); 4488c2ecf20Sopenharmony_ci if (ret) { 4498c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse (DP_TEST_HSYNC_HI)\n"); 4508c2ecf20Sopenharmony_ci return ret; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params2(link, DP_TEST_VSYNC_HI, 2, 4548c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_vsync_pol, 4558c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_vsync_width); 4568c2ecf20Sopenharmony_ci if (ret) { 4578c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse (DP_TEST_VSYNC_HI)\n"); 4588c2ecf20Sopenharmony_ci return ret; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params1(link, DP_TEST_H_WIDTH_HI, 2, 4628c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_h_width); 4638c2ecf20Sopenharmony_ci if (ret) { 4648c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_h_width(DP_TEST_H_WIDTH_HI)\n"); 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params1(link, DP_TEST_V_HEIGHT_HI, 2, 4698c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_v_height); 4708c2ecf20Sopenharmony_ci if (ret) { 4718c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_v_height\n"); 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params3(link, DP_TEST_MISC1, 4768c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_rr_d); 4778c2ecf20Sopenharmony_ci link->dp_link.test_video.test_rr_d &= DP_TEST_REFRESH_DENOMINATOR; 4788c2ecf20Sopenharmony_ci if (ret) { 4798c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_rr_d (DP_TEST_MISC1)\n"); 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = dp_link_parse_timing_params3(link, DP_TEST_REFRESH_RATE_NUMERATOR, 4848c2ecf20Sopenharmony_ci &link->dp_link.test_video.test_rr_n); 4858c2ecf20Sopenharmony_ci if (ret) { 4868c2ecf20Sopenharmony_ci DRM_ERROR("failed to parse test_rr_n\n"); 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci DRM_DEBUG_DP("link video pattern = 0x%x\n" 4918c2ecf20Sopenharmony_ci "link dynamic range = 0x%x\n" 4928c2ecf20Sopenharmony_ci "link bit depth = 0x%x\n" 4938c2ecf20Sopenharmony_ci "TEST_H_TOTAL = %d, TEST_V_TOTAL = %d\n" 4948c2ecf20Sopenharmony_ci "TEST_H_START = %d, TEST_V_START = %d\n" 4958c2ecf20Sopenharmony_ci "TEST_HSYNC_POL = %d\n" 4968c2ecf20Sopenharmony_ci "TEST_HSYNC_WIDTH = %d\n" 4978c2ecf20Sopenharmony_ci "TEST_VSYNC_POL = %d\n" 4988c2ecf20Sopenharmony_ci "TEST_VSYNC_WIDTH = %d\n" 4998c2ecf20Sopenharmony_ci "TEST_H_WIDTH = %d\n" 5008c2ecf20Sopenharmony_ci "TEST_V_HEIGHT = %d\n" 5018c2ecf20Sopenharmony_ci "TEST_REFRESH_DENOMINATOR = %d\n" 5028c2ecf20Sopenharmony_ci "TEST_REFRESH_NUMERATOR = %d\n", 5038c2ecf20Sopenharmony_ci link->dp_link.test_video.test_video_pattern, 5048c2ecf20Sopenharmony_ci link->dp_link.test_video.test_dyn_range, 5058c2ecf20Sopenharmony_ci link->dp_link.test_video.test_bit_depth, 5068c2ecf20Sopenharmony_ci link->dp_link.test_video.test_h_total, 5078c2ecf20Sopenharmony_ci link->dp_link.test_video.test_v_total, 5088c2ecf20Sopenharmony_ci link->dp_link.test_video.test_h_start, 5098c2ecf20Sopenharmony_ci link->dp_link.test_video.test_v_start, 5108c2ecf20Sopenharmony_ci link->dp_link.test_video.test_hsync_pol, 5118c2ecf20Sopenharmony_ci link->dp_link.test_video.test_hsync_width, 5128c2ecf20Sopenharmony_ci link->dp_link.test_video.test_vsync_pol, 5138c2ecf20Sopenharmony_ci link->dp_link.test_video.test_vsync_width, 5148c2ecf20Sopenharmony_ci link->dp_link.test_video.test_h_width, 5158c2ecf20Sopenharmony_ci link->dp_link.test_video.test_v_height, 5168c2ecf20Sopenharmony_ci link->dp_link.test_video.test_rr_d, 5178c2ecf20Sopenharmony_ci link->dp_link.test_video.test_rr_n); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/** 5238c2ecf20Sopenharmony_ci * dp_link_parse_link_training_params() - parses link training parameters from 5248c2ecf20Sopenharmony_ci * DPCD 5258c2ecf20Sopenharmony_ci * @link: Display Port Driver data 5268c2ecf20Sopenharmony_ci * 5278c2ecf20Sopenharmony_ci * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane 5288c2ecf20Sopenharmony_ci * count (Byte 0x220), and if these values parse are valid. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_cistatic int dp_link_parse_link_training_params(struct dp_link_private *link) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci u8 bp; 5338c2ecf20Sopenharmony_ci ssize_t rlen; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_LINK_RATE, &bp); 5368c2ecf20Sopenharmony_ci if (rlen < 0) { 5378c2ecf20Sopenharmony_ci DRM_ERROR("failed to read link rate. rlen=%zd\n", rlen); 5388c2ecf20Sopenharmony_ci return rlen; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (!is_link_rate_valid(bp)) { 5428c2ecf20Sopenharmony_ci DRM_ERROR("invalid link rate = 0x%x\n", bp); 5438c2ecf20Sopenharmony_ci return -EINVAL; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci link->request.test_link_rate = bp; 5478c2ecf20Sopenharmony_ci DRM_DEBUG_DP("link rate = 0x%x\n", link->request.test_link_rate); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_LANE_COUNT, &bp); 5508c2ecf20Sopenharmony_ci if (rlen < 0) { 5518c2ecf20Sopenharmony_ci DRM_ERROR("failed to read lane count. rlen=%zd\n", rlen); 5528c2ecf20Sopenharmony_ci return rlen; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci bp &= DP_MAX_LANE_COUNT_MASK; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (!is_lane_count_valid(bp)) { 5578c2ecf20Sopenharmony_ci DRM_ERROR("invalid lane count = 0x%x\n", bp); 5588c2ecf20Sopenharmony_ci return -EINVAL; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci link->request.test_lane_count = bp; 5628c2ecf20Sopenharmony_ci DRM_DEBUG_DP("lane count = 0x%x\n", link->request.test_lane_count); 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci/** 5678c2ecf20Sopenharmony_ci * dp_parse_phy_test_params() - parses the phy link parameters 5688c2ecf20Sopenharmony_ci * @link: Display Port Driver data 5698c2ecf20Sopenharmony_ci * 5708c2ecf20Sopenharmony_ci * Parses the DPCD (Byte 0x248) for the DP PHY link pattern that is being 5718c2ecf20Sopenharmony_ci * requested. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_cistatic int dp_link_parse_phy_test_params(struct dp_link_private *link) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci u8 data; 5768c2ecf20Sopenharmony_ci ssize_t rlen; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_PHY_TEST_PATTERN, 5798c2ecf20Sopenharmony_ci &data); 5808c2ecf20Sopenharmony_ci if (rlen < 0) { 5818c2ecf20Sopenharmony_ci DRM_ERROR("failed to read phy link pattern. rlen=%zd\n", rlen); 5828c2ecf20Sopenharmony_ci return rlen; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci link->dp_link.phy_params.phy_test_pattern_sel = data & 0x07; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci DRM_DEBUG_DP("phy_test_pattern_sel = 0x%x\n", data); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci switch (data) { 5908c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_SEL_MASK: 5918c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_NONE: 5928c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_D10_2: 5938c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_ERROR_COUNT: 5948c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_PRBS7: 5958c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_80BIT_CUSTOM: 5968c2ecf20Sopenharmony_ci case DP_PHY_TEST_PATTERN_CP2520: 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci default: 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/** 6048c2ecf20Sopenharmony_ci * dp_link_is_video_audio_test_requested() - checks for audio/video link request 6058c2ecf20Sopenharmony_ci * @link: link requested by the sink 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * Returns true if the requested link is a permitted audio/video link. 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_cistatic bool dp_link_is_video_audio_test_requested(u32 link) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci u8 video_audio_test = (DP_TEST_LINK_VIDEO_PATTERN | 6128c2ecf20Sopenharmony_ci DP_TEST_LINK_AUDIO_PATTERN | 6138c2ecf20Sopenharmony_ci DP_TEST_LINK_AUDIO_DISABLED_VIDEO); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return ((link & video_audio_test) && 6168c2ecf20Sopenharmony_ci !(link & ~video_audio_test)); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/** 6208c2ecf20Sopenharmony_ci * dp_link_parse_request() - parses link request parameters from sink 6218c2ecf20Sopenharmony_ci * @link: Display Port Driver data 6228c2ecf20Sopenharmony_ci * 6238c2ecf20Sopenharmony_ci * Parses the DPCD to check if an automated link is requested (Byte 0x201), 6248c2ecf20Sopenharmony_ci * and what type of link automation is being requested (Byte 0x218). 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_cistatic int dp_link_parse_request(struct dp_link_private *link) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci int ret = 0; 6298c2ecf20Sopenharmony_ci u8 data; 6308c2ecf20Sopenharmony_ci ssize_t rlen; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /** 6338c2ecf20Sopenharmony_ci * Read the device service IRQ vector (Byte 0x201) to determine 6348c2ecf20Sopenharmony_ci * whether an automated link has been requested by the sink. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, 6378c2ecf20Sopenharmony_ci DP_DEVICE_SERVICE_IRQ_VECTOR, &data); 6388c2ecf20Sopenharmony_ci if (rlen < 0) { 6398c2ecf20Sopenharmony_ci DRM_ERROR("aux read failed. rlen=%zd\n", rlen); 6408c2ecf20Sopenharmony_ci return rlen; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci DRM_DEBUG_DP("device service irq vector = 0x%x\n", data); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!(data & DP_AUTOMATED_TEST_REQUEST)) { 6468c2ecf20Sopenharmony_ci DRM_DEBUG_DP("no test requested\n"); 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /** 6518c2ecf20Sopenharmony_ci * Read the link request byte (Byte 0x218) to determine what type 6528c2ecf20Sopenharmony_ci * of automated link has been requested by the sink. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_REQUEST, &data); 6558c2ecf20Sopenharmony_ci if (rlen < 0) { 6568c2ecf20Sopenharmony_ci DRM_ERROR("aux read failed. rlen=%zd\n", rlen); 6578c2ecf20Sopenharmony_ci return rlen; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (!data || (data == DP_TEST_LINK_FAUX_PATTERN)) { 6618c2ecf20Sopenharmony_ci DRM_DEBUG_DP("link 0x%x not supported\n", data); 6628c2ecf20Sopenharmony_ci goto end; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Test:(0x%x) requested\n", data); 6668c2ecf20Sopenharmony_ci link->request.test_requested = data; 6678c2ecf20Sopenharmony_ci if (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) { 6688c2ecf20Sopenharmony_ci ret = dp_link_parse_phy_test_params(link); 6698c2ecf20Sopenharmony_ci if (ret) 6708c2ecf20Sopenharmony_ci goto end; 6718c2ecf20Sopenharmony_ci ret = dp_link_parse_link_training_params(link); 6728c2ecf20Sopenharmony_ci if (ret) 6738c2ecf20Sopenharmony_ci goto end; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (link->request.test_requested == DP_TEST_LINK_TRAINING) { 6778c2ecf20Sopenharmony_ci ret = dp_link_parse_link_training_params(link); 6788c2ecf20Sopenharmony_ci if (ret) 6798c2ecf20Sopenharmony_ci goto end; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (dp_link_is_video_audio_test_requested( 6838c2ecf20Sopenharmony_ci link->request.test_requested)) { 6848c2ecf20Sopenharmony_ci ret = dp_link_parse_video_pattern_params(link); 6858c2ecf20Sopenharmony_ci if (ret) 6868c2ecf20Sopenharmony_ci goto end; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ret = dp_link_parse_audio_pattern_params(link); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ciend: 6918c2ecf20Sopenharmony_ci /* 6928c2ecf20Sopenharmony_ci * Send a DP_TEST_ACK if all link parameters are valid, otherwise send 6938c2ecf20Sopenharmony_ci * a DP_TEST_NAK. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci if (ret) { 6968c2ecf20Sopenharmony_ci link->dp_link.test_response = DP_TEST_NAK; 6978c2ecf20Sopenharmony_ci } else { 6988c2ecf20Sopenharmony_ci if (link->request.test_requested != DP_TEST_LINK_EDID_READ) 6998c2ecf20Sopenharmony_ci link->dp_link.test_response = DP_TEST_ACK; 7008c2ecf20Sopenharmony_ci else 7018c2ecf20Sopenharmony_ci link->dp_link.test_response = 7028c2ecf20Sopenharmony_ci DP_TEST_EDID_CHECKSUM_WRITE; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci return ret; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci/** 7098c2ecf20Sopenharmony_ci * dp_link_parse_sink_count() - parses the sink count 7108c2ecf20Sopenharmony_ci * @dp_link: pointer to link module data 7118c2ecf20Sopenharmony_ci * 7128c2ecf20Sopenharmony_ci * Parses the DPCD to check if there is an update to the sink count 7138c2ecf20Sopenharmony_ci * (Byte 0x200), and whether all the sink devices connected have Content 7148c2ecf20Sopenharmony_ci * Protection enabled. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_cistatic int dp_link_parse_sink_count(struct dp_link *dp_link) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci ssize_t rlen; 7198c2ecf20Sopenharmony_ci bool cp_ready; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci struct dp_link_private *link = container_of(dp_link, 7228c2ecf20Sopenharmony_ci struct dp_link_private, dp_link); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci rlen = drm_dp_dpcd_readb(link->aux, DP_SINK_COUNT, 7258c2ecf20Sopenharmony_ci &link->dp_link.sink_count); 7268c2ecf20Sopenharmony_ci if (rlen < 0) { 7278c2ecf20Sopenharmony_ci DRM_ERROR("sink count read failed. rlen=%zd\n", rlen); 7288c2ecf20Sopenharmony_ci return rlen; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci cp_ready = link->dp_link.sink_count & DP_SINK_CP_READY; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci link->dp_link.sink_count = 7348c2ecf20Sopenharmony_ci DP_GET_SINK_COUNT(link->dp_link.sink_count); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci DRM_DEBUG_DP("sink_count = 0x%x, cp_ready = 0x%x\n", 7378c2ecf20Sopenharmony_ci link->dp_link.sink_count, cp_ready); 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void dp_link_parse_sink_status_field(struct dp_link_private *link) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci int len = 0; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci link->prev_sink_count = link->dp_link.sink_count; 7468c2ecf20Sopenharmony_ci dp_link_parse_sink_count(&link->dp_link); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci len = drm_dp_dpcd_read_link_status(link->aux, 7498c2ecf20Sopenharmony_ci link->link_status); 7508c2ecf20Sopenharmony_ci if (len < DP_LINK_STATUS_SIZE) 7518c2ecf20Sopenharmony_ci DRM_ERROR("DP link status read failed\n"); 7528c2ecf20Sopenharmony_ci dp_link_parse_request(link); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/** 7568c2ecf20Sopenharmony_ci * dp_link_process_link_training_request() - processes new training requests 7578c2ecf20Sopenharmony_ci * @link: Display Port link data 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci * This function will handle new link training requests that are initiated by 7608c2ecf20Sopenharmony_ci * the sink. In particular, it will update the requested lane count and link 7618c2ecf20Sopenharmony_ci * rate, and then trigger the link retraining procedure. 7628c2ecf20Sopenharmony_ci * 7638c2ecf20Sopenharmony_ci * The function will return 0 if a link training request has been processed, 7648c2ecf20Sopenharmony_ci * otherwise it will return -EINVAL. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_cistatic int dp_link_process_link_training_request(struct dp_link_private *link) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci if (link->request.test_requested != DP_TEST_LINK_TRAINING) 7698c2ecf20Sopenharmony_ci return -EINVAL; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Test:0x%x link rate = 0x%x, lane count = 0x%x\n", 7728c2ecf20Sopenharmony_ci DP_TEST_LINK_TRAINING, 7738c2ecf20Sopenharmony_ci link->request.test_link_rate, 7748c2ecf20Sopenharmony_ci link->request.test_lane_count); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci link->dp_link.link_params.num_lanes = link->request.test_lane_count; 7778c2ecf20Sopenharmony_ci link->dp_link.link_params.rate = 7788c2ecf20Sopenharmony_ci drm_dp_bw_code_to_link_rate(link->request.test_link_rate); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci return 0; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cibool dp_link_send_test_response(struct dp_link *dp_link) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct dp_link_private *link = NULL; 7868c2ecf20Sopenharmony_ci int ret = 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (!dp_link) { 7898c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 7908c2ecf20Sopenharmony_ci return false; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci link = container_of(dp_link, struct dp_link_private, dp_link); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_writeb(link->aux, DP_TEST_RESPONSE, 7968c2ecf20Sopenharmony_ci dp_link->test_response); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return ret == 1; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ciint dp_link_psm_config(struct dp_link *dp_link, 8028c2ecf20Sopenharmony_ci struct dp_link_info *link_info, bool enable) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct dp_link_private *link = NULL; 8058c2ecf20Sopenharmony_ci int ret = 0; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (!dp_link) { 8088c2ecf20Sopenharmony_ci DRM_ERROR("invalid params\n"); 8098c2ecf20Sopenharmony_ci return -EINVAL; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci link = container_of(dp_link, struct dp_link_private, dp_link); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci mutex_lock(&link->psm_mutex); 8158c2ecf20Sopenharmony_ci if (enable) 8168c2ecf20Sopenharmony_ci ret = dp_aux_link_power_down(link->aux, link_info); 8178c2ecf20Sopenharmony_ci else 8188c2ecf20Sopenharmony_ci ret = dp_aux_link_power_up(link->aux, link_info); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (ret) 8218c2ecf20Sopenharmony_ci DRM_ERROR("Failed to %s low power mode\n", enable ? 8228c2ecf20Sopenharmony_ci "enter" : "exit"); 8238c2ecf20Sopenharmony_ci else 8248c2ecf20Sopenharmony_ci dp_link->psm_enabled = enable; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci mutex_unlock(&link->psm_mutex); 8278c2ecf20Sopenharmony_ci return ret; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cibool dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct dp_link_private *link = NULL; 8338c2ecf20Sopenharmony_ci int ret = 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (!dp_link) { 8368c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 8378c2ecf20Sopenharmony_ci return false; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci link = container_of(dp_link, struct dp_link_private, dp_link); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_writeb(link->aux, DP_TEST_EDID_CHECKSUM, 8438c2ecf20Sopenharmony_ci checksum); 8448c2ecf20Sopenharmony_ci return ret == 1; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int dp_link_parse_vx_px(struct dp_link_private *link) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci int ret = 0; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci DRM_DEBUG_DP("vx: 0=%d, 1=%d, 2=%d, 3=%d\n", 8528c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_voltage(link->link_status, 0), 8538c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_voltage(link->link_status, 1), 8548c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_voltage(link->link_status, 2), 8558c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_voltage(link->link_status, 3)); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci DRM_DEBUG_DP("px: 0=%d, 1=%d, 2=%d, 3=%d\n", 8588c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_pre_emphasis(link->link_status, 0), 8598c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_pre_emphasis(link->link_status, 1), 8608c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_pre_emphasis(link->link_status, 2), 8618c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_pre_emphasis(link->link_status, 3)); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci /** 8648c2ecf20Sopenharmony_ci * Update the voltage and pre-emphasis levels as per DPCD request 8658c2ecf20Sopenharmony_ci * vector. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Current: v_level = 0x%x, p_level = 0x%x\n", 8688c2ecf20Sopenharmony_ci link->dp_link.phy_params.v_level, 8698c2ecf20Sopenharmony_ci link->dp_link.phy_params.p_level); 8708c2ecf20Sopenharmony_ci link->dp_link.phy_params.v_level = 8718c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_voltage(link->link_status, 0); 8728c2ecf20Sopenharmony_ci link->dp_link.phy_params.p_level = 8738c2ecf20Sopenharmony_ci drm_dp_get_adjust_request_pre_emphasis(link->link_status, 0); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci link->dp_link.phy_params.p_level >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Requested: v_level = 0x%x, p_level = 0x%x\n", 8788c2ecf20Sopenharmony_ci link->dp_link.phy_params.v_level, 8798c2ecf20Sopenharmony_ci link->dp_link.phy_params.p_level); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return ret; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/** 8858c2ecf20Sopenharmony_ci * dp_link_process_phy_test_pattern_request() - process new phy link requests 8868c2ecf20Sopenharmony_ci * @link: Display Port Driver data 8878c2ecf20Sopenharmony_ci * 8888c2ecf20Sopenharmony_ci * This function will handle new phy link pattern requests that are initiated 8898c2ecf20Sopenharmony_ci * by the sink. The function will return 0 if a phy link pattern has been 8908c2ecf20Sopenharmony_ci * processed, otherwise it will return -EINVAL. 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_cistatic int dp_link_process_phy_test_pattern_request( 8938c2ecf20Sopenharmony_ci struct dp_link_private *link) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci int ret = 0; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (!(link->request.test_requested & DP_TEST_LINK_PHY_TEST_PATTERN)) { 8988c2ecf20Sopenharmony_ci DRM_DEBUG_DP("no phy test\n"); 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!is_link_rate_valid(link->request.test_link_rate) || 9038c2ecf20Sopenharmony_ci !is_lane_count_valid(link->request.test_lane_count)) { 9048c2ecf20Sopenharmony_ci DRM_ERROR("Invalid: link rate = 0x%x,lane count = 0x%x\n", 9058c2ecf20Sopenharmony_ci link->request.test_link_rate, 9068c2ecf20Sopenharmony_ci link->request.test_lane_count); 9078c2ecf20Sopenharmony_ci return -EINVAL; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Current: rate = 0x%x, lane count = 0x%x\n", 9118c2ecf20Sopenharmony_ci link->dp_link.link_params.rate, 9128c2ecf20Sopenharmony_ci link->dp_link.link_params.num_lanes); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Requested: rate = 0x%x, lane count = 0x%x\n", 9158c2ecf20Sopenharmony_ci link->request.test_link_rate, 9168c2ecf20Sopenharmony_ci link->request.test_lane_count); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci link->dp_link.link_params.num_lanes = link->request.test_lane_count; 9198c2ecf20Sopenharmony_ci link->dp_link.link_params.rate = 9208c2ecf20Sopenharmony_ci drm_dp_bw_code_to_link_rate(link->request.test_link_rate); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci ret = dp_link_parse_vx_px(link); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (ret) 9258c2ecf20Sopenharmony_ci DRM_ERROR("parse_vx_px failed. ret=%d\n", ret); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci return link_status[r - DP_LANE0_1_STATUS]; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/** 9368c2ecf20Sopenharmony_ci * dp_link_process_link_status_update() - processes link status updates 9378c2ecf20Sopenharmony_ci * @link: Display Port link module data 9388c2ecf20Sopenharmony_ci * 9398c2ecf20Sopenharmony_ci * This function will check for changes in the link status, e.g. clock 9408c2ecf20Sopenharmony_ci * recovery done on all lanes, and trigger link training if there is a 9418c2ecf20Sopenharmony_ci * failure/error on the link. 9428c2ecf20Sopenharmony_ci * 9438c2ecf20Sopenharmony_ci * The function will return 0 if the a link status update has been processed, 9448c2ecf20Sopenharmony_ci * otherwise it will return -EINVAL. 9458c2ecf20Sopenharmony_ci */ 9468c2ecf20Sopenharmony_cistatic int dp_link_process_link_status_update(struct dp_link_private *link) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci bool channel_eq_done = drm_dp_channel_eq_ok(link->link_status, 9498c2ecf20Sopenharmony_ci link->dp_link.link_params.num_lanes); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci bool clock_recovery_done = drm_dp_clock_recovery_ok(link->link_status, 9528c2ecf20Sopenharmony_ci link->dp_link.link_params.num_lanes); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci DRM_DEBUG_DP("channel_eq_done = %d, clock_recovery_done = %d\n", 9558c2ecf20Sopenharmony_ci channel_eq_done, clock_recovery_done); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (channel_eq_done && clock_recovery_done) 9588c2ecf20Sopenharmony_ci return -EINVAL; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/** 9658c2ecf20Sopenharmony_ci * dp_link_process_downstream_port_status_change() - process port status changes 9668c2ecf20Sopenharmony_ci * @link: Display Port Driver data 9678c2ecf20Sopenharmony_ci * 9688c2ecf20Sopenharmony_ci * This function will handle downstream port updates that are initiated by 9698c2ecf20Sopenharmony_ci * the sink. If the downstream port status has changed, the EDID is read via 9708c2ecf20Sopenharmony_ci * AUX. 9718c2ecf20Sopenharmony_ci * 9728c2ecf20Sopenharmony_ci * The function will return 0 if a downstream port update has been 9738c2ecf20Sopenharmony_ci * processed, otherwise it will return -EINVAL. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_cistatic int dp_link_process_ds_port_status_change(struct dp_link_private *link) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci if (get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) & 9788c2ecf20Sopenharmony_ci DP_DOWNSTREAM_PORT_STATUS_CHANGED) 9798c2ecf20Sopenharmony_ci goto reset; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (link->prev_sink_count == link->dp_link.sink_count) 9828c2ecf20Sopenharmony_ci return -EINVAL; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cireset: 9858c2ecf20Sopenharmony_ci /* reset prev_sink_count */ 9868c2ecf20Sopenharmony_ci link->prev_sink_count = link->dp_link.sink_count; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic bool dp_link_is_video_pattern_requested(struct dp_link_private *link) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci return (link->request.test_requested & DP_TEST_LINK_VIDEO_PATTERN) 9948c2ecf20Sopenharmony_ci && !(link->request.test_requested & 9958c2ecf20Sopenharmony_ci DP_TEST_LINK_AUDIO_DISABLED_VIDEO); 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic bool dp_link_is_audio_pattern_requested(struct dp_link_private *link) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci return (link->request.test_requested & DP_TEST_LINK_AUDIO_PATTERN); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void dp_link_reset_data(struct dp_link_private *link) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci link->request = (const struct dp_link_request){ 0 }; 10068c2ecf20Sopenharmony_ci link->dp_link.test_video = (const struct dp_link_test_video){ 0 }; 10078c2ecf20Sopenharmony_ci link->dp_link.test_video.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN; 10088c2ecf20Sopenharmony_ci link->dp_link.test_audio = (const struct dp_link_test_audio){ 0 }; 10098c2ecf20Sopenharmony_ci link->dp_link.phy_params.phy_test_pattern_sel = 0; 10108c2ecf20Sopenharmony_ci link->dp_link.sink_request = 0; 10118c2ecf20Sopenharmony_ci link->dp_link.test_response = 0; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/** 10158c2ecf20Sopenharmony_ci * dp_link_process_request() - handle HPD IRQ transition to HIGH 10168c2ecf20Sopenharmony_ci * @dp_link: pointer to link module data 10178c2ecf20Sopenharmony_ci * 10188c2ecf20Sopenharmony_ci * This function will handle the HPD IRQ state transitions from LOW to HIGH 10198c2ecf20Sopenharmony_ci * (including cases when there are back to back HPD IRQ HIGH) indicating 10208c2ecf20Sopenharmony_ci * the start of a new link training request or sink status update. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ciint dp_link_process_request(struct dp_link *dp_link) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci int ret = 0; 10258c2ecf20Sopenharmony_ci struct dp_link_private *link; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (!dp_link) { 10288c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 10298c2ecf20Sopenharmony_ci return -EINVAL; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci link = container_of(dp_link, struct dp_link_private, dp_link); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci dp_link_reset_data(link); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci dp_link_parse_sink_status_field(link); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (link->request.test_requested == DP_TEST_LINK_EDID_READ) { 10398c2ecf20Sopenharmony_ci dp_link->sink_request |= DP_TEST_LINK_EDID_READ; 10408c2ecf20Sopenharmony_ci return ret; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ret = dp_link_process_ds_port_status_change(link); 10448c2ecf20Sopenharmony_ci if (!ret) { 10458c2ecf20Sopenharmony_ci dp_link->sink_request |= DS_PORT_STATUS_CHANGED; 10468c2ecf20Sopenharmony_ci return ret; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci ret = dp_link_process_link_training_request(link); 10508c2ecf20Sopenharmony_ci if (!ret) { 10518c2ecf20Sopenharmony_ci dp_link->sink_request |= DP_TEST_LINK_TRAINING; 10528c2ecf20Sopenharmony_ci return ret; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ret = dp_link_process_phy_test_pattern_request(link); 10568c2ecf20Sopenharmony_ci if (!ret) { 10578c2ecf20Sopenharmony_ci dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN; 10588c2ecf20Sopenharmony_ci return ret; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci ret = dp_link_process_link_status_update(link); 10628c2ecf20Sopenharmony_ci if (!ret) { 10638c2ecf20Sopenharmony_ci dp_link->sink_request |= DP_LINK_STATUS_UPDATED; 10648c2ecf20Sopenharmony_ci return ret; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (dp_link_is_video_pattern_requested(link)) { 10688c2ecf20Sopenharmony_ci ret = 0; 10698c2ecf20Sopenharmony_ci dp_link->sink_request |= DP_TEST_LINK_VIDEO_PATTERN; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (dp_link_is_audio_pattern_requested(link)) { 10738c2ecf20Sopenharmony_ci dp_link->sink_request |= DP_TEST_LINK_AUDIO_PATTERN; 10748c2ecf20Sopenharmony_ci return -EINVAL; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci return ret; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ciint dp_link_get_colorimetry_config(struct dp_link *dp_link) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci u32 cc = DP_MISC0_COLORIMERY_CFG_LEGACY_RGB; 10838c2ecf20Sopenharmony_ci struct dp_link_private *link; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (!dp_link) { 10868c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 10878c2ecf20Sopenharmony_ci return -EINVAL; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci link = container_of(dp_link, struct dp_link_private, dp_link); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* 10938c2ecf20Sopenharmony_ci * Unless a video pattern CTS test is ongoing, use RGB_VESA 10948c2ecf20Sopenharmony_ci * Only RGB_VESA and RGB_CEA supported for now 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_ci if (dp_link_is_video_pattern_requested(link)) { 10978c2ecf20Sopenharmony_ci if (link->dp_link.test_video.test_dyn_range & 10988c2ecf20Sopenharmony_ci DP_TEST_DYNAMIC_RANGE_CEA) 10998c2ecf20Sopenharmony_ci cc = DP_MISC0_COLORIMERY_CFG_CEA_RGB; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci return cc; 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ciint dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci int i; 11088c2ecf20Sopenharmony_ci int v_max = 0, p_max = 0; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (!dp_link) { 11118c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 11128c2ecf20Sopenharmony_ci return -EINVAL; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* use the max level across lanes */ 11168c2ecf20Sopenharmony_ci for (i = 0; i < dp_link->link_params.num_lanes; i++) { 11178c2ecf20Sopenharmony_ci u8 data_v = drm_dp_get_adjust_request_voltage(link_status, i); 11188c2ecf20Sopenharmony_ci u8 data_p = drm_dp_get_adjust_request_pre_emphasis(link_status, 11198c2ecf20Sopenharmony_ci i); 11208c2ecf20Sopenharmony_ci DRM_DEBUG_DP("lane=%d req_vol_swing=%d req_pre_emphasis=%d\n", 11218c2ecf20Sopenharmony_ci i, data_v, data_p); 11228c2ecf20Sopenharmony_ci if (v_max < data_v) 11238c2ecf20Sopenharmony_ci v_max = data_v; 11248c2ecf20Sopenharmony_ci if (p_max < data_p) 11258c2ecf20Sopenharmony_ci p_max = data_p; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci dp_link->phy_params.v_level = v_max >> DP_TRAIN_VOLTAGE_SWING_SHIFT; 11298c2ecf20Sopenharmony_ci dp_link->phy_params.p_level = p_max >> DP_TRAIN_PRE_EMPHASIS_SHIFT; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /** 11328c2ecf20Sopenharmony_ci * Adjust the voltage swing and pre-emphasis level combination to within 11338c2ecf20Sopenharmony_ci * the allowable range. 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_ci if (dp_link->phy_params.v_level > DP_TRAIN_VOLTAGE_SWING_MAX) { 11368c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Requested vSwingLevel=%d, change to %d\n", 11378c2ecf20Sopenharmony_ci dp_link->phy_params.v_level, 11388c2ecf20Sopenharmony_ci DP_TRAIN_VOLTAGE_SWING_MAX); 11398c2ecf20Sopenharmony_ci dp_link->phy_params.v_level = DP_TRAIN_VOLTAGE_SWING_MAX; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (dp_link->phy_params.p_level > DP_TRAIN_PRE_EMPHASIS_MAX) { 11438c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Requested preEmphasisLevel=%d, change to %d\n", 11448c2ecf20Sopenharmony_ci dp_link->phy_params.p_level, 11458c2ecf20Sopenharmony_ci DP_TRAIN_PRE_EMPHASIS_MAX); 11468c2ecf20Sopenharmony_ci dp_link->phy_params.p_level = DP_TRAIN_PRE_EMPHASIS_MAX; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if ((dp_link->phy_params.p_level > DP_TRAIN_PRE_EMPHASIS_LVL_1) 11508c2ecf20Sopenharmony_ci && (dp_link->phy_params.v_level == 11518c2ecf20Sopenharmony_ci DP_TRAIN_VOLTAGE_SWING_LVL_2)) { 11528c2ecf20Sopenharmony_ci DRM_DEBUG_DP("Requested preEmphasisLevel=%d, change to %d\n", 11538c2ecf20Sopenharmony_ci dp_link->phy_params.p_level, 11548c2ecf20Sopenharmony_ci DP_TRAIN_PRE_EMPHASIS_LVL_1); 11558c2ecf20Sopenharmony_ci dp_link->phy_params.p_level = DP_TRAIN_PRE_EMPHASIS_LVL_1; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci DRM_DEBUG_DP("adjusted: v_level=%d, p_level=%d\n", 11598c2ecf20Sopenharmony_ci dp_link->phy_params.v_level, dp_link->phy_params.p_level); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_civoid dp_link_reset_phy_params_vx_px(struct dp_link *dp_link) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci dp_link->phy_params.v_level = 0; 11678c2ecf20Sopenharmony_ci dp_link->phy_params.p_level = 0; 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ciu32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci u32 tbd; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* 11758c2ecf20Sopenharmony_ci * Few simplistic rules and assumptions made here: 11768c2ecf20Sopenharmony_ci * 1. Test bit depth is bit depth per color component 11778c2ecf20Sopenharmony_ci * 2. Assume 3 color components 11788c2ecf20Sopenharmony_ci */ 11798c2ecf20Sopenharmony_ci switch (bpp) { 11808c2ecf20Sopenharmony_ci case 18: 11818c2ecf20Sopenharmony_ci tbd = DP_TEST_BIT_DEPTH_6; 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci case 24: 11848c2ecf20Sopenharmony_ci tbd = DP_TEST_BIT_DEPTH_8; 11858c2ecf20Sopenharmony_ci break; 11868c2ecf20Sopenharmony_ci case 30: 11878c2ecf20Sopenharmony_ci tbd = DP_TEST_BIT_DEPTH_10; 11888c2ecf20Sopenharmony_ci break; 11898c2ecf20Sopenharmony_ci default: 11908c2ecf20Sopenharmony_ci tbd = DP_TEST_BIT_DEPTH_UNKNOWN; 11918c2ecf20Sopenharmony_ci break; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (tbd != DP_TEST_BIT_DEPTH_UNKNOWN) 11958c2ecf20Sopenharmony_ci tbd = (tbd >> DP_TEST_BIT_DEPTH_SHIFT); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci return tbd; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistruct dp_link *dp_link_get(struct device *dev, struct drm_dp_aux *aux) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci struct dp_link_private *link; 12038c2ecf20Sopenharmony_ci struct dp_link *dp_link; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (!dev || !aux) { 12068c2ecf20Sopenharmony_ci DRM_ERROR("invalid input\n"); 12078c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL); 12118c2ecf20Sopenharmony_ci if (!link) 12128c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci link->dev = dev; 12158c2ecf20Sopenharmony_ci link->aux = aux; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci mutex_init(&link->psm_mutex); 12188c2ecf20Sopenharmony_ci dp_link = &link->dp_link; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci return dp_link; 12218c2ecf20Sopenharmony_ci} 1222