162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright © 2009 Keith Packard 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its 562306a36Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that 662306a36Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright 762306a36Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and 862306a36Sopenharmony_ci * that the name of the copyright holders not be used in advertising or 962306a36Sopenharmony_ci * publicity pertaining to distribution of the software without specific, 1062306a36Sopenharmony_ci * written prior permission. The copyright holders make no representations 1162306a36Sopenharmony_ci * about the suitability of this software for any purpose. It is provided "as 1262306a36Sopenharmony_ci * is" without express or implied warranty. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1562306a36Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 1662306a36Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 1762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 1862306a36Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 1962306a36Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 2062306a36Sopenharmony_ci * OF THIS SOFTWARE. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/backlight.h> 2462306a36Sopenharmony_ci#include <linux/delay.h> 2562306a36Sopenharmony_ci#include <linux/errno.h> 2662306a36Sopenharmony_ci#include <linux/i2c.h> 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci#include <linux/sched.h> 3162306a36Sopenharmony_ci#include <linux/seq_file.h> 3262306a36Sopenharmony_ci#include <linux/string_helpers.h> 3362306a36Sopenharmony_ci#include <linux/dynamic_debug.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <drm/display/drm_dp_helper.h> 3662306a36Sopenharmony_ci#include <drm/display/drm_dp_mst_helper.h> 3762306a36Sopenharmony_ci#include <drm/drm_edid.h> 3862306a36Sopenharmony_ci#include <drm/drm_print.h> 3962306a36Sopenharmony_ci#include <drm/drm_vblank.h> 4062306a36Sopenharmony_ci#include <drm/drm_panel.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "drm_dp_helper_internal.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciDECLARE_DYNDBG_CLASSMAP(drm_debug_classes, DD_CLASS_TYPE_DISJOINT_BITS, 0, 4562306a36Sopenharmony_ci "DRM_UT_CORE", 4662306a36Sopenharmony_ci "DRM_UT_DRIVER", 4762306a36Sopenharmony_ci "DRM_UT_KMS", 4862306a36Sopenharmony_ci "DRM_UT_PRIME", 4962306a36Sopenharmony_ci "DRM_UT_ATOMIC", 5062306a36Sopenharmony_ci "DRM_UT_VBL", 5162306a36Sopenharmony_ci "DRM_UT_STATE", 5262306a36Sopenharmony_ci "DRM_UT_LEASE", 5362306a36Sopenharmony_ci "DRM_UT_DP", 5462306a36Sopenharmony_ci "DRM_UT_DRMRES"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct dp_aux_backlight { 5762306a36Sopenharmony_ci struct backlight_device *base; 5862306a36Sopenharmony_ci struct drm_dp_aux *aux; 5962306a36Sopenharmony_ci struct drm_edp_backlight_info info; 6062306a36Sopenharmony_ci bool enabled; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * DOC: dp helpers 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * These functions contain some common logic and helpers at various abstraction 6762306a36Sopenharmony_ci * levels to deal with Display Port sink devices and related things like DP aux 6862306a36Sopenharmony_ci * channel transfers, EDID reading over DP aux channels, decoding certain DPCD 6962306a36Sopenharmony_ci * blocks, ... 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Helpers for DP link training */ 7362306a36Sopenharmony_cistatic u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci return link_status[r - DP_LANE0_1_STATUS]; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], 7962306a36Sopenharmony_ci int lane) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int i = DP_LANE0_1_STATUS + (lane >> 1); 8262306a36Sopenharmony_ci int s = (lane & 1) * 4; 8362306a36Sopenharmony_ci u8 l = dp_link_status(link_status, i); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return (l >> s) & 0xf; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cibool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], 8962306a36Sopenharmony_ci int lane_count) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci u8 lane_align; 9262306a36Sopenharmony_ci u8 lane_status; 9362306a36Sopenharmony_ci int lane; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci lane_align = dp_link_status(link_status, 9662306a36Sopenharmony_ci DP_LANE_ALIGN_STATUS_UPDATED); 9762306a36Sopenharmony_ci if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) 9862306a36Sopenharmony_ci return false; 9962306a36Sopenharmony_ci for (lane = 0; lane < lane_count; lane++) { 10062306a36Sopenharmony_ci lane_status = dp_get_lane_status(link_status, lane); 10162306a36Sopenharmony_ci if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) 10262306a36Sopenharmony_ci return false; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci return true; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_channel_eq_ok); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cibool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], 10962306a36Sopenharmony_ci int lane_count) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int lane; 11262306a36Sopenharmony_ci u8 lane_status; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (lane = 0; lane < lane_count; lane++) { 11562306a36Sopenharmony_ci lane_status = dp_get_lane_status(link_status, lane); 11662306a36Sopenharmony_ci if ((lane_status & DP_LANE_CR_DONE) == 0) 11762306a36Sopenharmony_ci return false; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return true; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_clock_recovery_ok); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciu8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], 12462306a36Sopenharmony_ci int lane) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 12762306a36Sopenharmony_ci int s = ((lane & 1) ? 12862306a36Sopenharmony_ci DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 12962306a36Sopenharmony_ci DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); 13062306a36Sopenharmony_ci u8 l = dp_link_status(link_status, i); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciu8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], 13762306a36Sopenharmony_ci int lane) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 14062306a36Sopenharmony_ci int s = ((lane & 1) ? 14162306a36Sopenharmony_ci DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : 14262306a36Sopenharmony_ci DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); 14362306a36Sopenharmony_ci u8 l = dp_link_status(link_status, i); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* DP 2.0 128b/132b */ 15062306a36Sopenharmony_ciu8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE], 15162306a36Sopenharmony_ci int lane) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 15462306a36Sopenharmony_ci int s = ((lane & 1) ? 15562306a36Sopenharmony_ci DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT : 15662306a36Sopenharmony_ci DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT); 15762306a36Sopenharmony_ci u8 l = dp_link_status(link_status, i); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return (l >> s) & 0xf; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_get_adjust_tx_ffe_preset); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* DP 2.0 errata for 128b/132b */ 16462306a36Sopenharmony_cibool drm_dp_128b132b_lane_channel_eq_done(const u8 link_status[DP_LINK_STATUS_SIZE], 16562306a36Sopenharmony_ci int lane_count) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u8 lane_align, lane_status; 16862306a36Sopenharmony_ci int lane; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci lane_align = dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); 17162306a36Sopenharmony_ci if (!(lane_align & DP_INTERLANE_ALIGN_DONE)) 17262306a36Sopenharmony_ci return false; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (lane = 0; lane < lane_count; lane++) { 17562306a36Sopenharmony_ci lane_status = dp_get_lane_status(link_status, lane); 17662306a36Sopenharmony_ci if (!(lane_status & DP_LANE_CHANNEL_EQ_DONE)) 17762306a36Sopenharmony_ci return false; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci return true; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_128b132b_lane_channel_eq_done); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* DP 2.0 errata for 128b/132b */ 18462306a36Sopenharmony_cibool drm_dp_128b132b_lane_symbol_locked(const u8 link_status[DP_LINK_STATUS_SIZE], 18562306a36Sopenharmony_ci int lane_count) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci u8 lane_status; 18862306a36Sopenharmony_ci int lane; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci for (lane = 0; lane < lane_count; lane++) { 19162306a36Sopenharmony_ci lane_status = dp_get_lane_status(link_status, lane); 19262306a36Sopenharmony_ci if (!(lane_status & DP_LANE_SYMBOL_LOCKED)) 19362306a36Sopenharmony_ci return false; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci return true; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_128b132b_lane_symbol_locked); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* DP 2.0 errata for 128b/132b */ 20062306a36Sopenharmony_cibool drm_dp_128b132b_eq_interlane_align_done(const u8 link_status[DP_LINK_STATUS_SIZE]) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci u8 status = dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return status & DP_128B132B_DPRX_EQ_INTERLANE_ALIGN_DONE; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_128b132b_eq_interlane_align_done); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* DP 2.0 errata for 128b/132b */ 20962306a36Sopenharmony_cibool drm_dp_128b132b_cds_interlane_align_done(const u8 link_status[DP_LINK_STATUS_SIZE]) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci u8 status = dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return status & DP_128B132B_DPRX_CDS_INTERLANE_ALIGN_DONE; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_128b132b_cds_interlane_align_done); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* DP 2.0 errata for 128b/132b */ 21862306a36Sopenharmony_cibool drm_dp_128b132b_link_training_failed(const u8 link_status[DP_LINK_STATUS_SIZE]) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci u8 status = dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return status & DP_128B132B_LT_FAILED; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_128b132b_link_training_failed); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci if (rd_interval > 4) 22962306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n", 23062306a36Sopenharmony_ci aux->name, rd_interval); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (rd_interval == 0) 23362306a36Sopenharmony_ci return 100; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return rd_interval * 4 * USEC_PER_MSEC; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int __8b10b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci if (rd_interval > 4) 24162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n", 24262306a36Sopenharmony_ci aux->name, rd_interval); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (rd_interval == 0) 24562306a36Sopenharmony_ci return 400; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return rd_interval * 4 * USEC_PER_MSEC; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int __128b132b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci switch (rd_interval) { 25362306a36Sopenharmony_ci default: 25462306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x\n", 25562306a36Sopenharmony_ci aux->name, rd_interval); 25662306a36Sopenharmony_ci fallthrough; 25762306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US: 25862306a36Sopenharmony_ci return 400; 25962306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS: 26062306a36Sopenharmony_ci return 4000; 26162306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS: 26262306a36Sopenharmony_ci return 8000; 26362306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS: 26462306a36Sopenharmony_ci return 12000; 26562306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS: 26662306a36Sopenharmony_ci return 16000; 26762306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS: 26862306a36Sopenharmony_ci return 32000; 26962306a36Sopenharmony_ci case DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS: 27062306a36Sopenharmony_ci return 64000; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* 27562306a36Sopenharmony_ci * The link training delays are different for: 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * - Clock recovery vs. channel equalization 27862306a36Sopenharmony_ci * - DPRX vs. LTTPR 27962306a36Sopenharmony_ci * - 128b/132b vs. 8b/10b 28062306a36Sopenharmony_ci * - DPCD rev 1.3 vs. later 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * Get the correct delay in us, reading DPCD if necessary. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic int __read_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], 28562306a36Sopenharmony_ci enum drm_dp_phy dp_phy, bool uhbr, bool cr) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int (*parse)(const struct drm_dp_aux *aux, u8 rd_interval); 28862306a36Sopenharmony_ci unsigned int offset; 28962306a36Sopenharmony_ci u8 rd_interval, mask; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (dp_phy == DP_PHY_DPRX) { 29262306a36Sopenharmony_ci if (uhbr) { 29362306a36Sopenharmony_ci if (cr) 29462306a36Sopenharmony_ci return 100; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL; 29762306a36Sopenharmony_ci mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; 29862306a36Sopenharmony_ci parse = __128b132b_channel_eq_delay_us; 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci if (cr && dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) 30162306a36Sopenharmony_ci return 100; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci offset = DP_TRAINING_AUX_RD_INTERVAL; 30462306a36Sopenharmony_ci mask = DP_TRAINING_AUX_RD_MASK; 30562306a36Sopenharmony_ci if (cr) 30662306a36Sopenharmony_ci parse = __8b10b_clock_recovery_delay_us; 30762306a36Sopenharmony_ci else 30862306a36Sopenharmony_ci parse = __8b10b_channel_eq_delay_us; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci if (uhbr) { 31262306a36Sopenharmony_ci offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy); 31362306a36Sopenharmony_ci mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; 31462306a36Sopenharmony_ci parse = __128b132b_channel_eq_delay_us; 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci if (cr) 31762306a36Sopenharmony_ci return 100; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci offset = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy); 32062306a36Sopenharmony_ci mask = DP_TRAINING_AUX_RD_MASK; 32162306a36Sopenharmony_ci parse = __8b10b_channel_eq_delay_us; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (offset < DP_RECEIVER_CAP_SIZE) { 32662306a36Sopenharmony_ci rd_interval = dpcd[offset]; 32762306a36Sopenharmony_ci } else { 32862306a36Sopenharmony_ci if (drm_dp_dpcd_readb(aux, offset, &rd_interval) != 1) { 32962306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: failed rd interval read\n", 33062306a36Sopenharmony_ci aux->name); 33162306a36Sopenharmony_ci /* arbitrary default delay */ 33262306a36Sopenharmony_ci return 400; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return parse(aux, rd_interval & mask); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ciint drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], 34062306a36Sopenharmony_ci enum drm_dp_phy dp_phy, bool uhbr) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci return __read_delay(aux, dpcd, dp_phy, uhbr, true); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_clock_recovery_delay); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciint drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], 34762306a36Sopenharmony_ci enum drm_dp_phy dp_phy, bool uhbr) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci return __read_delay(aux, dpcd, dp_phy, uhbr, false); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_channel_eq_delay); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* Per DP 2.0 Errata */ 35462306a36Sopenharmony_ciint drm_dp_128b132b_read_aux_rd_interval(struct drm_dp_aux *aux) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci int unit; 35762306a36Sopenharmony_ci u8 val; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (drm_dp_dpcd_readb(aux, DP_128B132B_TRAINING_AUX_RD_INTERVAL, &val) != 1) { 36062306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: failed rd interval read\n", 36162306a36Sopenharmony_ci aux->name); 36262306a36Sopenharmony_ci /* default to max */ 36362306a36Sopenharmony_ci val = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci unit = (val & DP_128B132B_TRAINING_AUX_RD_INTERVAL_1MS_UNIT) ? 1 : 2; 36762306a36Sopenharmony_ci val &= DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return (val + 1) * unit * 1000; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_128b132b_read_aux_rd_interval); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_civoid drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, 37462306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE]) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci u8 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & 37762306a36Sopenharmony_ci DP_TRAINING_AUX_RD_MASK; 37862306a36Sopenharmony_ci int delay_us; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) 38162306a36Sopenharmony_ci delay_us = 100; 38262306a36Sopenharmony_ci else 38362306a36Sopenharmony_ci delay_us = __8b10b_clock_recovery_delay_us(aux, rd_interval); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci usleep_range(delay_us, delay_us * 2); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux, 39062306a36Sopenharmony_ci u8 rd_interval) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int delay_us = __8b10b_channel_eq_delay_us(aux, rd_interval); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci usleep_range(delay_us, delay_us * 2); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_civoid drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux, 39862306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE]) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci __drm_dp_link_train_channel_eq_delay(aux, 40162306a36Sopenharmony_ci dpcd[DP_TRAINING_AUX_RD_INTERVAL] & 40262306a36Sopenharmony_ci DP_TRAINING_AUX_RD_MASK); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/** 40762306a36Sopenharmony_ci * drm_dp_phy_name() - Get the name of the given DP PHY 40862306a36Sopenharmony_ci * @dp_phy: The DP PHY identifier 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * Given the @dp_phy, get a user friendly name of the DP PHY, either "DPRX" or 41162306a36Sopenharmony_ci * "LTTPR <N>", or "<INVALID DP PHY>" on errors. The returned string is always 41262306a36Sopenharmony_ci * non-NULL and valid. 41362306a36Sopenharmony_ci * 41462306a36Sopenharmony_ci * Returns: Name of the DP PHY. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ciconst char *drm_dp_phy_name(enum drm_dp_phy dp_phy) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci static const char * const phy_names[] = { 41962306a36Sopenharmony_ci [DP_PHY_DPRX] = "DPRX", 42062306a36Sopenharmony_ci [DP_PHY_LTTPR1] = "LTTPR 1", 42162306a36Sopenharmony_ci [DP_PHY_LTTPR2] = "LTTPR 2", 42262306a36Sopenharmony_ci [DP_PHY_LTTPR3] = "LTTPR 3", 42362306a36Sopenharmony_ci [DP_PHY_LTTPR4] = "LTTPR 4", 42462306a36Sopenharmony_ci [DP_PHY_LTTPR5] = "LTTPR 5", 42562306a36Sopenharmony_ci [DP_PHY_LTTPR6] = "LTTPR 6", 42662306a36Sopenharmony_ci [DP_PHY_LTTPR7] = "LTTPR 7", 42762306a36Sopenharmony_ci [DP_PHY_LTTPR8] = "LTTPR 8", 42862306a36Sopenharmony_ci }; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (dp_phy < 0 || dp_phy >= ARRAY_SIZE(phy_names) || 43162306a36Sopenharmony_ci WARN_ON(!phy_names[dp_phy])) 43262306a36Sopenharmony_ci return "<INVALID DP PHY>"; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return phy_names[dp_phy]; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_phy_name); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_civoid drm_dp_lttpr_link_train_clock_recovery_delay(void) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci usleep_range(100, 200); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_link_train_clock_recovery_delay); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic u8 dp_lttpr_phy_cap(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE], int r) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci return phy_cap[r - DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1]; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_civoid drm_dp_lttpr_link_train_channel_eq_delay(const struct drm_dp_aux *aux, 45062306a36Sopenharmony_ci const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE]) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci u8 interval = dp_lttpr_phy_cap(phy_cap, 45362306a36Sopenharmony_ci DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) & 45462306a36Sopenharmony_ci DP_TRAINING_AUX_RD_MASK; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci __drm_dp_link_train_channel_eq_delay(aux, interval); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_link_train_channel_eq_delay); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciu8 drm_dp_link_rate_to_bw_code(int link_rate) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci switch (link_rate) { 46362306a36Sopenharmony_ci case 1000000: 46462306a36Sopenharmony_ci return DP_LINK_BW_10; 46562306a36Sopenharmony_ci case 1350000: 46662306a36Sopenharmony_ci return DP_LINK_BW_13_5; 46762306a36Sopenharmony_ci case 2000000: 46862306a36Sopenharmony_ci return DP_LINK_BW_20; 46962306a36Sopenharmony_ci default: 47062306a36Sopenharmony_ci /* Spec says link_bw = link_rate / 0.27Gbps */ 47162306a36Sopenharmony_ci return link_rate / 27000; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciint drm_dp_bw_code_to_link_rate(u8 link_bw) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci switch (link_bw) { 47962306a36Sopenharmony_ci case DP_LINK_BW_10: 48062306a36Sopenharmony_ci return 1000000; 48162306a36Sopenharmony_ci case DP_LINK_BW_13_5: 48262306a36Sopenharmony_ci return 1350000; 48362306a36Sopenharmony_ci case DP_LINK_BW_20: 48462306a36Sopenharmony_ci return 2000000; 48562306a36Sopenharmony_ci default: 48662306a36Sopenharmony_ci /* Spec says link_rate = link_bw * 0.27Gbps */ 48762306a36Sopenharmony_ci return link_bw * 27000; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci#define AUX_RETRY_INTERVAL 500 /* us */ 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic inline void 49562306a36Sopenharmony_cidrm_dp_dump_access(const struct drm_dp_aux *aux, 49662306a36Sopenharmony_ci u8 request, uint offset, void *buffer, int ret) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci const char *arrow = request == DP_AUX_NATIVE_READ ? "->" : "<-"; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (ret > 0) 50162306a36Sopenharmony_ci drm_dbg_dp(aux->drm_dev, "%s: 0x%05x AUX %s (ret=%3d) %*ph\n", 50262306a36Sopenharmony_ci aux->name, offset, arrow, ret, min(ret, 20), buffer); 50362306a36Sopenharmony_ci else 50462306a36Sopenharmony_ci drm_dbg_dp(aux->drm_dev, "%s: 0x%05x AUX %s (ret=%3d)\n", 50562306a36Sopenharmony_ci aux->name, offset, arrow, ret); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/** 50962306a36Sopenharmony_ci * DOC: dp helpers 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * The DisplayPort AUX channel is an abstraction to allow generic, driver- 51262306a36Sopenharmony_ci * independent access to AUX functionality. Drivers can take advantage of 51362306a36Sopenharmony_ci * this by filling in the fields of the drm_dp_aux structure. 51462306a36Sopenharmony_ci * 51562306a36Sopenharmony_ci * Transactions are described using a hardware-independent drm_dp_aux_msg 51662306a36Sopenharmony_ci * structure, which is passed into a driver's .transfer() implementation. 51762306a36Sopenharmony_ci * Both native and I2C-over-AUX transactions are supported. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, 52162306a36Sopenharmony_ci unsigned int offset, void *buffer, size_t size) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct drm_dp_aux_msg msg; 52462306a36Sopenharmony_ci unsigned int retry, native_reply; 52562306a36Sopenharmony_ci int err = 0, ret = 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 52862306a36Sopenharmony_ci msg.address = offset; 52962306a36Sopenharmony_ci msg.request = request; 53062306a36Sopenharmony_ci msg.buffer = buffer; 53162306a36Sopenharmony_ci msg.size = size; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mutex_lock(&aux->hw_mutex); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * The specification doesn't give any recommendation on how often to 53762306a36Sopenharmony_ci * retry native transactions. We used to retry 7 times like for 53862306a36Sopenharmony_ci * aux i2c transactions but real world devices this wasn't 53962306a36Sopenharmony_ci * sufficient, bump to 32 which makes Dell 4k monitors happier. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci for (retry = 0; retry < 32; retry++) { 54262306a36Sopenharmony_ci if (ret != 0 && ret != -ETIMEDOUT) { 54362306a36Sopenharmony_ci usleep_range(AUX_RETRY_INTERVAL, 54462306a36Sopenharmony_ci AUX_RETRY_INTERVAL + 100); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = aux->transfer(aux, &msg); 54862306a36Sopenharmony_ci if (ret >= 0) { 54962306a36Sopenharmony_ci native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK; 55062306a36Sopenharmony_ci if (native_reply == DP_AUX_NATIVE_REPLY_ACK) { 55162306a36Sopenharmony_ci if (ret == size) 55262306a36Sopenharmony_ci goto unlock; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ret = -EPROTO; 55562306a36Sopenharmony_ci } else 55662306a36Sopenharmony_ci ret = -EIO; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * We want the error we return to be the error we received on 56162306a36Sopenharmony_ci * the first transaction, since we may get a different error the 56262306a36Sopenharmony_ci * next time we retry 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (!err) 56562306a36Sopenharmony_ci err = ret; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Too many retries, giving up. First error: %d\n", 56962306a36Sopenharmony_ci aux->name, err); 57062306a36Sopenharmony_ci ret = err; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciunlock: 57362306a36Sopenharmony_ci mutex_unlock(&aux->hw_mutex); 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/** 57862306a36Sopenharmony_ci * drm_dp_dpcd_probe() - probe a given DPCD address with a 1-byte read access 57962306a36Sopenharmony_ci * @aux: DisplayPort AUX channel (SST) 58062306a36Sopenharmony_ci * @offset: address of the register to probe 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * Probe the provided DPCD address by reading 1 byte from it. The function can 58362306a36Sopenharmony_ci * be used to trigger some side-effect the read access has, like waking up the 58462306a36Sopenharmony_ci * sink, without the need for the read-out value. 58562306a36Sopenharmony_ci * 58662306a36Sopenharmony_ci * Returns 0 if the read access suceeded, or a negative error code on failure. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ciint drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci u8 buffer; 59162306a36Sopenharmony_ci int ret; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, 1); 59462306a36Sopenharmony_ci WARN_ON(ret == 0); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, ret); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return ret < 0 ? ret : 0; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dpcd_probe); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/** 60362306a36Sopenharmony_ci * drm_dp_dpcd_read() - read a series of bytes from the DPCD 60462306a36Sopenharmony_ci * @aux: DisplayPort AUX channel (SST or MST) 60562306a36Sopenharmony_ci * @offset: address of the (first) register to read 60662306a36Sopenharmony_ci * @buffer: buffer to store the register values 60762306a36Sopenharmony_ci * @size: number of bytes in @buffer 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * Returns the number of bytes transferred on success, or a negative error 61062306a36Sopenharmony_ci * code on failure. -EIO is returned if the request was NAKed by the sink or 61162306a36Sopenharmony_ci * if the retry count was exceeded. If not all bytes were transferred, this 61262306a36Sopenharmony_ci * function returns -EPROTO. Errors from the underlying AUX channel transfer 61362306a36Sopenharmony_ci * function, with the exception of -EBUSY (which causes the transaction to 61462306a36Sopenharmony_ci * be retried), are propagated to the caller. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_cissize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, 61762306a36Sopenharmony_ci void *buffer, size_t size) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci int ret; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * HP ZR24w corrupts the first DPCD access after entering power save 62362306a36Sopenharmony_ci * mode. Eg. on a read, the entire buffer will be filled with the same 62462306a36Sopenharmony_ci * byte. Do a throw away read to avoid corrupting anything we care 62562306a36Sopenharmony_ci * about. Afterwards things will work correctly until the monitor 62662306a36Sopenharmony_ci * gets woken up and subsequently re-enters power save mode. 62762306a36Sopenharmony_ci * 62862306a36Sopenharmony_ci * The user pressing any button on the monitor is enough to wake it 62962306a36Sopenharmony_ci * up, so there is no particularly good place to do the workaround. 63062306a36Sopenharmony_ci * We just have to do it before any DPCD access and hope that the 63162306a36Sopenharmony_ci * monitor doesn't power down exactly after the throw away read. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci if (!aux->is_remote) { 63462306a36Sopenharmony_ci ret = drm_dp_dpcd_probe(aux, DP_DPCD_REV); 63562306a36Sopenharmony_ci if (ret < 0) 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (aux->is_remote) 64062306a36Sopenharmony_ci ret = drm_dp_mst_dpcd_read(aux, offset, buffer, size); 64162306a36Sopenharmony_ci else 64262306a36Sopenharmony_ci ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, 64362306a36Sopenharmony_ci buffer, size); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret); 64662306a36Sopenharmony_ci return ret; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dpcd_read); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/** 65162306a36Sopenharmony_ci * drm_dp_dpcd_write() - write a series of bytes to the DPCD 65262306a36Sopenharmony_ci * @aux: DisplayPort AUX channel (SST or MST) 65362306a36Sopenharmony_ci * @offset: address of the (first) register to write 65462306a36Sopenharmony_ci * @buffer: buffer containing the values to write 65562306a36Sopenharmony_ci * @size: number of bytes in @buffer 65662306a36Sopenharmony_ci * 65762306a36Sopenharmony_ci * Returns the number of bytes transferred on success, or a negative error 65862306a36Sopenharmony_ci * code on failure. -EIO is returned if the request was NAKed by the sink or 65962306a36Sopenharmony_ci * if the retry count was exceeded. If not all bytes were transferred, this 66062306a36Sopenharmony_ci * function returns -EPROTO. Errors from the underlying AUX channel transfer 66162306a36Sopenharmony_ci * function, with the exception of -EBUSY (which causes the transaction to 66262306a36Sopenharmony_ci * be retried), are propagated to the caller. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_cissize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, 66562306a36Sopenharmony_ci void *buffer, size_t size) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci int ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (aux->is_remote) 67062306a36Sopenharmony_ci ret = drm_dp_mst_dpcd_write(aux, offset, buffer, size); 67162306a36Sopenharmony_ci else 67262306a36Sopenharmony_ci ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, 67362306a36Sopenharmony_ci buffer, size); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci drm_dp_dump_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, ret); 67662306a36Sopenharmony_ci return ret; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dpcd_write); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci/** 68162306a36Sopenharmony_ci * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) 68262306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 68362306a36Sopenharmony_ci * @status: buffer to store the link status in (must be at least 6 bytes) 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * Returns the number of bytes transferred on success or a negative error 68662306a36Sopenharmony_ci * code on failure. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ciint drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, 68962306a36Sopenharmony_ci u8 status[DP_LINK_STATUS_SIZE]) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status, 69262306a36Sopenharmony_ci DP_LINK_STATUS_SIZE); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dpcd_read_link_status); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/** 69762306a36Sopenharmony_ci * drm_dp_dpcd_read_phy_link_status - get the link status information for a DP PHY 69862306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 69962306a36Sopenharmony_ci * @dp_phy: the DP PHY to get the link status for 70062306a36Sopenharmony_ci * @link_status: buffer to return the status in 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * Fetch the AUX DPCD registers for the DPRX or an LTTPR PHY link status. The 70362306a36Sopenharmony_ci * layout of the returned @link_status matches the DPCD register layout of the 70462306a36Sopenharmony_ci * DPRX PHY link status. 70562306a36Sopenharmony_ci * 70662306a36Sopenharmony_ci * Returns 0 if the information was read successfully or a negative error code 70762306a36Sopenharmony_ci * on failure. 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ciint drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux, 71062306a36Sopenharmony_ci enum drm_dp_phy dp_phy, 71162306a36Sopenharmony_ci u8 link_status[DP_LINK_STATUS_SIZE]) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci int ret; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (dp_phy == DP_PHY_DPRX) { 71662306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, 71762306a36Sopenharmony_ci DP_LANE0_1_STATUS, 71862306a36Sopenharmony_ci link_status, 71962306a36Sopenharmony_ci DP_LINK_STATUS_SIZE); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (ret < 0) 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci WARN_ON(ret != DP_LINK_STATUS_SIZE); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, 73062306a36Sopenharmony_ci DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy), 73162306a36Sopenharmony_ci link_status, 73262306a36Sopenharmony_ci DP_LINK_STATUS_SIZE - 1); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (ret < 0) 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci WARN_ON(ret != DP_LINK_STATUS_SIZE - 1); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Convert the LTTPR to the sink PHY link status layout */ 74062306a36Sopenharmony_ci memmove(&link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS + 1], 74162306a36Sopenharmony_ci &link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS], 74262306a36Sopenharmony_ci DP_LINK_STATUS_SIZE - (DP_SINK_STATUS - DP_LANE0_1_STATUS) - 1); 74362306a36Sopenharmony_ci link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS] = 0; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dpcd_read_phy_link_status); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic bool is_edid_digital_input_dp(const struct edid *edid) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci return edid && edid->revision >= 4 && 75262306a36Sopenharmony_ci edid->input & DRM_EDID_INPUT_DIGITAL && 75362306a36Sopenharmony_ci (edid->input & DRM_EDID_DIGITAL_TYPE_MASK) == DRM_EDID_DIGITAL_TYPE_DP; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/** 75762306a36Sopenharmony_ci * drm_dp_downstream_is_type() - is the downstream facing port of certain type? 75862306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 75962306a36Sopenharmony_ci * @port_cap: port capabilities 76062306a36Sopenharmony_ci * @type: port type to be checked. Can be: 76162306a36Sopenharmony_ci * %DP_DS_PORT_TYPE_DP, %DP_DS_PORT_TYPE_VGA, %DP_DS_PORT_TYPE_DVI, 76262306a36Sopenharmony_ci * %DP_DS_PORT_TYPE_HDMI, %DP_DS_PORT_TYPE_NON_EDID, 76362306a36Sopenharmony_ci * %DP_DS_PORT_TYPE_DP_DUALMODE or %DP_DS_PORT_TYPE_WIRELESS. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Caveat: Only works with DPCD 1.1+ port caps. 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * Returns: whether the downstream facing port matches the type. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_cibool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 77062306a36Sopenharmony_ci const u8 port_cap[4], u8 type) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci return drm_dp_is_branch(dpcd) && 77362306a36Sopenharmony_ci dpcd[DP_DPCD_REV] >= 0x11 && 77462306a36Sopenharmony_ci (port_cap[0] & DP_DS_PORT_TYPE_MASK) == type; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_is_type); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/** 77962306a36Sopenharmony_ci * drm_dp_downstream_is_tmds() - is the downstream facing port TMDS? 78062306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 78162306a36Sopenharmony_ci * @port_cap: port capabilities 78262306a36Sopenharmony_ci * @edid: EDID 78362306a36Sopenharmony_ci * 78462306a36Sopenharmony_ci * Returns: whether the downstream facing port is TMDS (HDMI/DVI). 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_cibool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 78762306a36Sopenharmony_ci const u8 port_cap[4], 78862306a36Sopenharmony_ci const struct edid *edid) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x11) { 79162306a36Sopenharmony_ci switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { 79262306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_TMDS: 79362306a36Sopenharmony_ci return true; 79462306a36Sopenharmony_ci default: 79562306a36Sopenharmony_ci return false; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 80062306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP_DUALMODE: 80162306a36Sopenharmony_ci if (is_edid_digital_input_dp(edid)) 80262306a36Sopenharmony_ci return false; 80362306a36Sopenharmony_ci fallthrough; 80462306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DVI: 80562306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 80662306a36Sopenharmony_ci return true; 80762306a36Sopenharmony_ci default: 80862306a36Sopenharmony_ci return false; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_is_tmds); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/** 81462306a36Sopenharmony_ci * drm_dp_send_real_edid_checksum() - send back real edid checksum value 81562306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 81662306a36Sopenharmony_ci * @real_edid_checksum: real edid checksum for the last block 81762306a36Sopenharmony_ci * 81862306a36Sopenharmony_ci * Returns: 81962306a36Sopenharmony_ci * True on success 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_cibool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux, 82262306a36Sopenharmony_ci u8 real_edid_checksum) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci u8 link_edid_read = 0, auto_test_req = 0, test_resp = 0; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (drm_dp_dpcd_read(aux, DP_DEVICE_SERVICE_IRQ_VECTOR, 82762306a36Sopenharmony_ci &auto_test_req, 1) < 1) { 82862306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: DPCD failed read at register 0x%x\n", 82962306a36Sopenharmony_ci aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR); 83062306a36Sopenharmony_ci return false; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci auto_test_req &= DP_AUTOMATED_TEST_REQUEST; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (drm_dp_dpcd_read(aux, DP_TEST_REQUEST, &link_edid_read, 1) < 1) { 83562306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: DPCD failed read at register 0x%x\n", 83662306a36Sopenharmony_ci aux->name, DP_TEST_REQUEST); 83762306a36Sopenharmony_ci return false; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci link_edid_read &= DP_TEST_LINK_EDID_READ; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (!auto_test_req || !link_edid_read) { 84262306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Source DUT does not support TEST_EDID_READ\n", 84362306a36Sopenharmony_ci aux->name); 84462306a36Sopenharmony_ci return false; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (drm_dp_dpcd_write(aux, DP_DEVICE_SERVICE_IRQ_VECTOR, 84862306a36Sopenharmony_ci &auto_test_req, 1) < 1) { 84962306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n", 85062306a36Sopenharmony_ci aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR); 85162306a36Sopenharmony_ci return false; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* send back checksum for the last edid extension block data */ 85562306a36Sopenharmony_ci if (drm_dp_dpcd_write(aux, DP_TEST_EDID_CHECKSUM, 85662306a36Sopenharmony_ci &real_edid_checksum, 1) < 1) { 85762306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n", 85862306a36Sopenharmony_ci aux->name, DP_TEST_EDID_CHECKSUM); 85962306a36Sopenharmony_ci return false; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci test_resp |= DP_TEST_EDID_CHECKSUM_WRITE; 86362306a36Sopenharmony_ci if (drm_dp_dpcd_write(aux, DP_TEST_RESPONSE, &test_resp, 1) < 1) { 86462306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n", 86562306a36Sopenharmony_ci aux->name, DP_TEST_RESPONSE); 86662306a36Sopenharmony_ci return false; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return true; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_send_real_edid_checksum); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci u8 port_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE && port_count > 4) 87862306a36Sopenharmony_ci port_count = 4; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return port_count; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux, 88462306a36Sopenharmony_ci u8 dpcd[DP_RECEIVER_CAP_SIZE]) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; 88762306a36Sopenharmony_ci int ret; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* 89062306a36Sopenharmony_ci * Prior to DP1.3 the bit represented by 89162306a36Sopenharmony_ci * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. 89262306a36Sopenharmony_ci * If it is set DP_DPCD_REV at 0000h could be at a value less than 89362306a36Sopenharmony_ci * the true capability of the panel. The only way to check is to 89462306a36Sopenharmony_ci * then compare 0000h and 2200h. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ci if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] & 89762306a36Sopenharmony_ci DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext, 90162306a36Sopenharmony_ci sizeof(dpcd_ext)); 90262306a36Sopenharmony_ci if (ret < 0) 90362306a36Sopenharmony_ci return ret; 90462306a36Sopenharmony_ci if (ret != sizeof(dpcd_ext)) 90562306a36Sopenharmony_ci return -EIO; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { 90862306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 90962306a36Sopenharmony_ci "%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n", 91062306a36Sopenharmony_ci aux->name, dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext))) 91562306a36Sopenharmony_ci return 0; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Base DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext)); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci/** 92562306a36Sopenharmony_ci * drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if 92662306a36Sopenharmony_ci * available 92762306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 92862306a36Sopenharmony_ci * @dpcd: Buffer to store the resulting DPCD in 92962306a36Sopenharmony_ci * 93062306a36Sopenharmony_ci * Attempts to read the base DPCD caps for @aux. Additionally, this function 93162306a36Sopenharmony_ci * checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if 93262306a36Sopenharmony_ci * present. 93362306a36Sopenharmony_ci * 93462306a36Sopenharmony_ci * Returns: %0 if the DPCD was read successfully, negative error code 93562306a36Sopenharmony_ci * otherwise. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ciint drm_dp_read_dpcd_caps(struct drm_dp_aux *aux, 93862306a36Sopenharmony_ci u8 dpcd[DP_RECEIVER_CAP_SIZE]) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci int ret; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); 94362306a36Sopenharmony_ci if (ret < 0) 94462306a36Sopenharmony_ci return ret; 94562306a36Sopenharmony_ci if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0) 94662306a36Sopenharmony_ci return -EIO; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci ret = drm_dp_read_extended_dpcd_caps(aux, dpcd); 94962306a36Sopenharmony_ci if (ret < 0) 95062306a36Sopenharmony_ci return ret; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return ret; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_dpcd_caps); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/** 95962306a36Sopenharmony_ci * drm_dp_read_downstream_info() - read DPCD downstream port info if available 96062306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 96162306a36Sopenharmony_ci * @dpcd: A cached copy of the port's DPCD 96262306a36Sopenharmony_ci * @downstream_ports: buffer to store the downstream port info in 96362306a36Sopenharmony_ci * 96462306a36Sopenharmony_ci * See also: 96562306a36Sopenharmony_ci * drm_dp_downstream_max_clock() 96662306a36Sopenharmony_ci * drm_dp_downstream_max_bpc() 96762306a36Sopenharmony_ci * 96862306a36Sopenharmony_ci * Returns: 0 if either the downstream port info was read successfully or 96962306a36Sopenharmony_ci * there was no downstream info to read, or a negative error code otherwise. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ciint drm_dp_read_downstream_info(struct drm_dp_aux *aux, 97262306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], 97362306a36Sopenharmony_ci u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci int ret; 97662306a36Sopenharmony_ci u8 len; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci memset(downstream_ports, 0, DP_MAX_DOWNSTREAM_PORTS); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* No downstream info to read */ 98162306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd) || dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) 98262306a36Sopenharmony_ci return 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* Some branches advertise having 0 downstream ports, despite also advertising they have a 98562306a36Sopenharmony_ci * downstream port present. The DP spec isn't clear on if this is allowed or not, but since 98662306a36Sopenharmony_ci * some branches do it we need to handle it regardless. 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_ci len = drm_dp_downstream_port_count(dpcd); 98962306a36Sopenharmony_ci if (!len) 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) 99362306a36Sopenharmony_ci len *= 4; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len); 99662306a36Sopenharmony_ci if (ret < 0) 99762306a36Sopenharmony_ci return ret; 99862306a36Sopenharmony_ci if (ret != len) 99962306a36Sopenharmony_ci return -EIO; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: DPCD DFP: %*ph\n", aux->name, len, downstream_ports); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_downstream_info); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/** 100862306a36Sopenharmony_ci * drm_dp_downstream_max_dotclock() - extract downstream facing port max dot clock 100962306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 101062306a36Sopenharmony_ci * @port_cap: port capabilities 101162306a36Sopenharmony_ci * 101262306a36Sopenharmony_ci * Returns: Downstream facing port max dot clock in kHz on success, 101362306a36Sopenharmony_ci * or 0 if max clock not defined 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ciint drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 101662306a36Sopenharmony_ci const u8 port_cap[4]) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 101962306a36Sopenharmony_ci return 0; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x11) 102262306a36Sopenharmony_ci return 0; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 102562306a36Sopenharmony_ci case DP_DS_PORT_TYPE_VGA: 102662306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 102762306a36Sopenharmony_ci return 0; 102862306a36Sopenharmony_ci return port_cap[1] * 8000; 102962306a36Sopenharmony_ci default: 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_max_dotclock); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci/** 103662306a36Sopenharmony_ci * drm_dp_downstream_max_tmds_clock() - extract downstream facing port max TMDS clock 103762306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 103862306a36Sopenharmony_ci * @port_cap: port capabilities 103962306a36Sopenharmony_ci * @edid: EDID 104062306a36Sopenharmony_ci * 104162306a36Sopenharmony_ci * Returns: HDMI/DVI downstream facing port max TMDS clock in kHz on success, 104262306a36Sopenharmony_ci * or 0 if max TMDS clock not defined 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ciint drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 104562306a36Sopenharmony_ci const u8 port_cap[4], 104662306a36Sopenharmony_ci const struct edid *edid) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 104962306a36Sopenharmony_ci return 0; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x11) { 105262306a36Sopenharmony_ci switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { 105362306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_TMDS: 105462306a36Sopenharmony_ci return 165000; 105562306a36Sopenharmony_ci default: 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 106162306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP_DUALMODE: 106262306a36Sopenharmony_ci if (is_edid_digital_input_dp(edid)) 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci /* 106562306a36Sopenharmony_ci * It's left up to the driver to check the 106662306a36Sopenharmony_ci * DP dual mode adapter's max TMDS clock. 106762306a36Sopenharmony_ci * 106862306a36Sopenharmony_ci * Unfortunately it looks like branch devices 106962306a36Sopenharmony_ci * may not fordward that the DP dual mode i2c 107062306a36Sopenharmony_ci * access so we just usually get i2c nak :( 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_ci fallthrough; 107362306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 107462306a36Sopenharmony_ci /* 107562306a36Sopenharmony_ci * We should perhaps assume 165 MHz when detailed cap 107662306a36Sopenharmony_ci * info is not available. But looks like many typical 107762306a36Sopenharmony_ci * branch devices fall into that category and so we'd 107862306a36Sopenharmony_ci * probably end up with users complaining that they can't 107962306a36Sopenharmony_ci * get high resolution modes with their favorite dongle. 108062306a36Sopenharmony_ci * 108162306a36Sopenharmony_ci * So let's limit to 300 MHz instead since DPCD 1.4 108262306a36Sopenharmony_ci * HDMI 2.0 DFPs are required to have the detailed cap 108362306a36Sopenharmony_ci * info. So it's more likely we're dealing with a HDMI 1.4 108462306a36Sopenharmony_ci * compatible* device here. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 108762306a36Sopenharmony_ci return 300000; 108862306a36Sopenharmony_ci return port_cap[1] * 2500; 108962306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DVI: 109062306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 109162306a36Sopenharmony_ci return 165000; 109262306a36Sopenharmony_ci /* FIXME what to do about DVI dual link? */ 109362306a36Sopenharmony_ci return port_cap[1] * 2500; 109462306a36Sopenharmony_ci default: 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_max_tmds_clock); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci/** 110162306a36Sopenharmony_ci * drm_dp_downstream_min_tmds_clock() - extract downstream facing port min TMDS clock 110262306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 110362306a36Sopenharmony_ci * @port_cap: port capabilities 110462306a36Sopenharmony_ci * @edid: EDID 110562306a36Sopenharmony_ci * 110662306a36Sopenharmony_ci * Returns: HDMI/DVI downstream facing port min TMDS clock in kHz on success, 110762306a36Sopenharmony_ci * or 0 if max TMDS clock not defined 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ciint drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 111062306a36Sopenharmony_ci const u8 port_cap[4], 111162306a36Sopenharmony_ci const struct edid *edid) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x11) { 111762306a36Sopenharmony_ci switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { 111862306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_TMDS: 111962306a36Sopenharmony_ci return 25000; 112062306a36Sopenharmony_ci default: 112162306a36Sopenharmony_ci return 0; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 112662306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP_DUALMODE: 112762306a36Sopenharmony_ci if (is_edid_digital_input_dp(edid)) 112862306a36Sopenharmony_ci return 0; 112962306a36Sopenharmony_ci fallthrough; 113062306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DVI: 113162306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * Unclear whether the protocol converter could 113462306a36Sopenharmony_ci * utilize pixel replication. Assume it won't. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ci return 25000; 113762306a36Sopenharmony_ci default: 113862306a36Sopenharmony_ci return 0; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_min_tmds_clock); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci/** 114462306a36Sopenharmony_ci * drm_dp_downstream_max_bpc() - extract downstream facing port max 114562306a36Sopenharmony_ci * bits per component 114662306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 114762306a36Sopenharmony_ci * @port_cap: downstream facing port capabilities 114862306a36Sopenharmony_ci * @edid: EDID 114962306a36Sopenharmony_ci * 115062306a36Sopenharmony_ci * Returns: Max bpc on success or 0 if max bpc not defined 115162306a36Sopenharmony_ci */ 115262306a36Sopenharmony_ciint drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 115362306a36Sopenharmony_ci const u8 port_cap[4], 115462306a36Sopenharmony_ci const struct edid *edid) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 115762306a36Sopenharmony_ci return 0; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x11) { 116062306a36Sopenharmony_ci switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { 116162306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_DP: 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci default: 116462306a36Sopenharmony_ci return 8; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 116962306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP: 117062306a36Sopenharmony_ci return 0; 117162306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP_DUALMODE: 117262306a36Sopenharmony_ci if (is_edid_digital_input_dp(edid)) 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci fallthrough; 117562306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 117662306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DVI: 117762306a36Sopenharmony_ci case DP_DS_PORT_TYPE_VGA: 117862306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 117962306a36Sopenharmony_ci return 8; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci switch (port_cap[2] & DP_DS_MAX_BPC_MASK) { 118262306a36Sopenharmony_ci case DP_DS_8BPC: 118362306a36Sopenharmony_ci return 8; 118462306a36Sopenharmony_ci case DP_DS_10BPC: 118562306a36Sopenharmony_ci return 10; 118662306a36Sopenharmony_ci case DP_DS_12BPC: 118762306a36Sopenharmony_ci return 12; 118862306a36Sopenharmony_ci case DP_DS_16BPC: 118962306a36Sopenharmony_ci return 16; 119062306a36Sopenharmony_ci default: 119162306a36Sopenharmony_ci return 8; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci default: 119562306a36Sopenharmony_ci return 8; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_max_bpc); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci/** 120162306a36Sopenharmony_ci * drm_dp_downstream_420_passthrough() - determine downstream facing port 120262306a36Sopenharmony_ci * YCbCr 4:2:0 pass-through capability 120362306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 120462306a36Sopenharmony_ci * @port_cap: downstream facing port capabilities 120562306a36Sopenharmony_ci * 120662306a36Sopenharmony_ci * Returns: whether the downstream facing port can pass through YCbCr 4:2:0 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_cibool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 120962306a36Sopenharmony_ci const u8 port_cap[4]) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 121262306a36Sopenharmony_ci return false; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x13) 121562306a36Sopenharmony_ci return false; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 121862306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP: 121962306a36Sopenharmony_ci return true; 122062306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 122162306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 122262306a36Sopenharmony_ci return false; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci return port_cap[3] & DP_DS_HDMI_YCBCR420_PASS_THROUGH; 122562306a36Sopenharmony_ci default: 122662306a36Sopenharmony_ci return false; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_420_passthrough); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/** 123262306a36Sopenharmony_ci * drm_dp_downstream_444_to_420_conversion() - determine downstream facing port 123362306a36Sopenharmony_ci * YCbCr 4:4:4->4:2:0 conversion capability 123462306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 123562306a36Sopenharmony_ci * @port_cap: downstream facing port capabilities 123662306a36Sopenharmony_ci * 123762306a36Sopenharmony_ci * Returns: whether the downstream facing port can convert YCbCr 4:4:4 to 4:2:0 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_cibool drm_dp_downstream_444_to_420_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 124062306a36Sopenharmony_ci const u8 port_cap[4]) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 124362306a36Sopenharmony_ci return false; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x13) 124662306a36Sopenharmony_ci return false; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 124962306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 125062306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 125162306a36Sopenharmony_ci return false; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return port_cap[3] & DP_DS_HDMI_YCBCR444_TO_420_CONV; 125462306a36Sopenharmony_ci default: 125562306a36Sopenharmony_ci return false; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_444_to_420_conversion); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci/** 126162306a36Sopenharmony_ci * drm_dp_downstream_rgb_to_ycbcr_conversion() - determine downstream facing port 126262306a36Sopenharmony_ci * RGB->YCbCr conversion capability 126362306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 126462306a36Sopenharmony_ci * @port_cap: downstream facing port capabilities 126562306a36Sopenharmony_ci * @color_spc: Colorspace for which conversion cap is sought 126662306a36Sopenharmony_ci * 126762306a36Sopenharmony_ci * Returns: whether the downstream facing port can convert RGB->YCbCr for a given 126862306a36Sopenharmony_ci * colorspace. 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_cibool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 127162306a36Sopenharmony_ci const u8 port_cap[4], 127262306a36Sopenharmony_ci u8 color_spc) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 127562306a36Sopenharmony_ci return false; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x13) 127862306a36Sopenharmony_ci return false; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 128162306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 128262306a36Sopenharmony_ci if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) 128362306a36Sopenharmony_ci return false; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return port_cap[3] & color_spc; 128662306a36Sopenharmony_ci default: 128762306a36Sopenharmony_ci return false; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_rgb_to_ycbcr_conversion); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/** 129362306a36Sopenharmony_ci * drm_dp_downstream_mode() - return a mode for downstream facing port 129462306a36Sopenharmony_ci * @dev: DRM device 129562306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 129662306a36Sopenharmony_ci * @port_cap: port capabilities 129762306a36Sopenharmony_ci * 129862306a36Sopenharmony_ci * Provides a suitable mode for downstream facing ports without EDID. 129962306a36Sopenharmony_ci * 130062306a36Sopenharmony_ci * Returns: A new drm_display_mode on success or NULL on failure 130162306a36Sopenharmony_ci */ 130262306a36Sopenharmony_cistruct drm_display_mode * 130362306a36Sopenharmony_cidrm_dp_downstream_mode(struct drm_device *dev, 130462306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], 130562306a36Sopenharmony_ci const u8 port_cap[4]) 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci u8 vic; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 131162306a36Sopenharmony_ci return NULL; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] < 0x11) 131462306a36Sopenharmony_ci return NULL; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { 131762306a36Sopenharmony_ci case DP_DS_PORT_TYPE_NON_EDID: 131862306a36Sopenharmony_ci switch (port_cap[0] & DP_DS_NON_EDID_MASK) { 131962306a36Sopenharmony_ci case DP_DS_NON_EDID_720x480i_60: 132062306a36Sopenharmony_ci vic = 6; 132162306a36Sopenharmony_ci break; 132262306a36Sopenharmony_ci case DP_DS_NON_EDID_720x480i_50: 132362306a36Sopenharmony_ci vic = 21; 132462306a36Sopenharmony_ci break; 132562306a36Sopenharmony_ci case DP_DS_NON_EDID_1920x1080i_60: 132662306a36Sopenharmony_ci vic = 5; 132762306a36Sopenharmony_ci break; 132862306a36Sopenharmony_ci case DP_DS_NON_EDID_1920x1080i_50: 132962306a36Sopenharmony_ci vic = 20; 133062306a36Sopenharmony_ci break; 133162306a36Sopenharmony_ci case DP_DS_NON_EDID_1280x720_60: 133262306a36Sopenharmony_ci vic = 4; 133362306a36Sopenharmony_ci break; 133462306a36Sopenharmony_ci case DP_DS_NON_EDID_1280x720_50: 133562306a36Sopenharmony_ci vic = 19; 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci default: 133862306a36Sopenharmony_ci return NULL; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci return drm_display_mode_from_cea_vic(dev, vic); 134162306a36Sopenharmony_ci default: 134262306a36Sopenharmony_ci return NULL; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_mode); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci/** 134862306a36Sopenharmony_ci * drm_dp_downstream_id() - identify branch device 134962306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 135062306a36Sopenharmony_ci * @id: DisplayPort branch device id 135162306a36Sopenharmony_ci * 135262306a36Sopenharmony_ci * Returns branch device id on success or NULL on failure 135362306a36Sopenharmony_ci */ 135462306a36Sopenharmony_ciint drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci return drm_dp_dpcd_read(aux, DP_BRANCH_ID, id, 6); 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_id); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/** 136162306a36Sopenharmony_ci * drm_dp_downstream_debug() - debug DP branch devices 136262306a36Sopenharmony_ci * @m: pointer for debugfs file 136362306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 136462306a36Sopenharmony_ci * @port_cap: port capabilities 136562306a36Sopenharmony_ci * @edid: EDID 136662306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 136762306a36Sopenharmony_ci * 136862306a36Sopenharmony_ci */ 136962306a36Sopenharmony_civoid drm_dp_downstream_debug(struct seq_file *m, 137062306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], 137162306a36Sopenharmony_ci const u8 port_cap[4], 137262306a36Sopenharmony_ci const struct edid *edid, 137362306a36Sopenharmony_ci struct drm_dp_aux *aux) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & 137662306a36Sopenharmony_ci DP_DETAILED_CAP_INFO_AVAILABLE; 137762306a36Sopenharmony_ci int clk; 137862306a36Sopenharmony_ci int bpc; 137962306a36Sopenharmony_ci char id[7]; 138062306a36Sopenharmony_ci int len; 138162306a36Sopenharmony_ci uint8_t rev[2]; 138262306a36Sopenharmony_ci int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; 138362306a36Sopenharmony_ci bool branch_device = drm_dp_is_branch(dpcd); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci seq_printf(m, "\tDP branch device present: %s\n", 138662306a36Sopenharmony_ci str_yes_no(branch_device)); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (!branch_device) 138962306a36Sopenharmony_ci return; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci switch (type) { 139262306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP: 139362306a36Sopenharmony_ci seq_puts(m, "\t\tType: DisplayPort\n"); 139462306a36Sopenharmony_ci break; 139562306a36Sopenharmony_ci case DP_DS_PORT_TYPE_VGA: 139662306a36Sopenharmony_ci seq_puts(m, "\t\tType: VGA\n"); 139762306a36Sopenharmony_ci break; 139862306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DVI: 139962306a36Sopenharmony_ci seq_puts(m, "\t\tType: DVI\n"); 140062306a36Sopenharmony_ci break; 140162306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 140262306a36Sopenharmony_ci seq_puts(m, "\t\tType: HDMI\n"); 140362306a36Sopenharmony_ci break; 140462306a36Sopenharmony_ci case DP_DS_PORT_TYPE_NON_EDID: 140562306a36Sopenharmony_ci seq_puts(m, "\t\tType: others without EDID support\n"); 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP_DUALMODE: 140862306a36Sopenharmony_ci seq_puts(m, "\t\tType: DP++\n"); 140962306a36Sopenharmony_ci break; 141062306a36Sopenharmony_ci case DP_DS_PORT_TYPE_WIRELESS: 141162306a36Sopenharmony_ci seq_puts(m, "\t\tType: Wireless\n"); 141262306a36Sopenharmony_ci break; 141362306a36Sopenharmony_ci default: 141462306a36Sopenharmony_ci seq_puts(m, "\t\tType: N/A\n"); 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci memset(id, 0, sizeof(id)); 141862306a36Sopenharmony_ci drm_dp_downstream_id(aux, id); 141962306a36Sopenharmony_ci seq_printf(m, "\t\tID: %s\n", id); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci len = drm_dp_dpcd_read(aux, DP_BRANCH_HW_REV, &rev[0], 1); 142262306a36Sopenharmony_ci if (len > 0) 142362306a36Sopenharmony_ci seq_printf(m, "\t\tHW: %d.%d\n", 142462306a36Sopenharmony_ci (rev[0] & 0xf0) >> 4, rev[0] & 0xf); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, rev, 2); 142762306a36Sopenharmony_ci if (len > 0) 142862306a36Sopenharmony_ci seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (detailed_cap_info) { 143162306a36Sopenharmony_ci clk = drm_dp_downstream_max_dotclock(dpcd, port_cap); 143262306a36Sopenharmony_ci if (clk > 0) 143362306a36Sopenharmony_ci seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci clk = drm_dp_downstream_max_tmds_clock(dpcd, port_cap, edid); 143662306a36Sopenharmony_ci if (clk > 0) 143762306a36Sopenharmony_ci seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci clk = drm_dp_downstream_min_tmds_clock(dpcd, port_cap, edid); 144062306a36Sopenharmony_ci if (clk > 0) 144162306a36Sopenharmony_ci seq_printf(m, "\t\tMin TMDS clock: %d kHz\n", clk); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (bpc > 0) 144662306a36Sopenharmony_ci seq_printf(m, "\t\tMax bpc: %d\n", bpc); 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_downstream_debug); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci/** 145262306a36Sopenharmony_ci * drm_dp_subconnector_type() - get DP branch device type 145362306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 145462306a36Sopenharmony_ci * @port_cap: port capabilities 145562306a36Sopenharmony_ci */ 145662306a36Sopenharmony_cienum drm_mode_subconnector 145762306a36Sopenharmony_cidrm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 145862306a36Sopenharmony_ci const u8 port_cap[4]) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci int type; 146162306a36Sopenharmony_ci if (!drm_dp_is_branch(dpcd)) 146262306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_Native; 146362306a36Sopenharmony_ci /* DP 1.0 approach */ 146462306a36Sopenharmony_ci if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) { 146562306a36Sopenharmony_ci type = dpcd[DP_DOWNSTREAMPORT_PRESENT] & 146662306a36Sopenharmony_ci DP_DWN_STRM_PORT_TYPE_MASK; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci switch (type) { 146962306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_TMDS: 147062306a36Sopenharmony_ci /* Can be HDMI or DVI-D, DVI-D is a safer option */ 147162306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_DVID; 147262306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_ANALOG: 147362306a36Sopenharmony_ci /* Can be VGA or DVI-A, VGA is more popular */ 147462306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_VGA; 147562306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_DP: 147662306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_DisplayPort; 147762306a36Sopenharmony_ci case DP_DWN_STRM_PORT_TYPE_OTHER: 147862306a36Sopenharmony_ci default: 147962306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_Unknown; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci type = port_cap[0] & DP_DS_PORT_TYPE_MASK; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci switch (type) { 148562306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP: 148662306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DP_DUALMODE: 148762306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_DisplayPort; 148862306a36Sopenharmony_ci case DP_DS_PORT_TYPE_VGA: 148962306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_VGA; 149062306a36Sopenharmony_ci case DP_DS_PORT_TYPE_DVI: 149162306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_DVID; 149262306a36Sopenharmony_ci case DP_DS_PORT_TYPE_HDMI: 149362306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_HDMIA; 149462306a36Sopenharmony_ci case DP_DS_PORT_TYPE_WIRELESS: 149562306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_Wireless; 149662306a36Sopenharmony_ci case DP_DS_PORT_TYPE_NON_EDID: 149762306a36Sopenharmony_ci default: 149862306a36Sopenharmony_ci return DRM_MODE_SUBCONNECTOR_Unknown; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_subconnector_type); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci/** 150462306a36Sopenharmony_ci * drm_dp_set_subconnector_property - set subconnector for DP connector 150562306a36Sopenharmony_ci * @connector: connector to set property on 150662306a36Sopenharmony_ci * @status: connector status 150762306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 150862306a36Sopenharmony_ci * @port_cap: port capabilities 150962306a36Sopenharmony_ci * 151062306a36Sopenharmony_ci * Called by a driver on every detect event. 151162306a36Sopenharmony_ci */ 151262306a36Sopenharmony_civoid drm_dp_set_subconnector_property(struct drm_connector *connector, 151362306a36Sopenharmony_ci enum drm_connector_status status, 151462306a36Sopenharmony_ci const u8 *dpcd, 151562306a36Sopenharmony_ci const u8 port_cap[4]) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (status == connector_status_connected) 152062306a36Sopenharmony_ci subconnector = drm_dp_subconnector_type(dpcd, port_cap); 152162306a36Sopenharmony_ci drm_object_property_set_value(&connector->base, 152262306a36Sopenharmony_ci connector->dev->mode_config.dp_subconnector_property, 152362306a36Sopenharmony_ci subconnector); 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_set_subconnector_property); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci/** 152862306a36Sopenharmony_ci * drm_dp_read_sink_count_cap() - Check whether a given connector has a valid sink 152962306a36Sopenharmony_ci * count 153062306a36Sopenharmony_ci * @connector: The DRM connector to check 153162306a36Sopenharmony_ci * @dpcd: A cached copy of the connector's DPCD RX capabilities 153262306a36Sopenharmony_ci * @desc: A cached copy of the connector's DP descriptor 153362306a36Sopenharmony_ci * 153462306a36Sopenharmony_ci * See also: drm_dp_read_sink_count() 153562306a36Sopenharmony_ci * 153662306a36Sopenharmony_ci * Returns: %True if the (e)DP connector has a valid sink count that should 153762306a36Sopenharmony_ci * be probed, %false otherwise. 153862306a36Sopenharmony_ci */ 153962306a36Sopenharmony_cibool drm_dp_read_sink_count_cap(struct drm_connector *connector, 154062306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], 154162306a36Sopenharmony_ci const struct drm_dp_desc *desc) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci /* Some eDP panels don't set a valid value for the sink count */ 154462306a36Sopenharmony_ci return connector->connector_type != DRM_MODE_CONNECTOR_eDP && 154562306a36Sopenharmony_ci dpcd[DP_DPCD_REV] >= DP_DPCD_REV_11 && 154662306a36Sopenharmony_ci dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT && 154762306a36Sopenharmony_ci !drm_dp_has_quirk(desc, DP_DPCD_QUIRK_NO_SINK_COUNT); 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_sink_count_cap); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci/** 155262306a36Sopenharmony_ci * drm_dp_read_sink_count() - Retrieve the sink count for a given sink 155362306a36Sopenharmony_ci * @aux: The DP AUX channel to use 155462306a36Sopenharmony_ci * 155562306a36Sopenharmony_ci * See also: drm_dp_read_sink_count_cap() 155662306a36Sopenharmony_ci * 155762306a36Sopenharmony_ci * Returns: The current sink count reported by @aux, or a negative error code 155862306a36Sopenharmony_ci * otherwise. 155962306a36Sopenharmony_ci */ 156062306a36Sopenharmony_ciint drm_dp_read_sink_count(struct drm_dp_aux *aux) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci u8 count; 156362306a36Sopenharmony_ci int ret; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_SINK_COUNT, &count); 156662306a36Sopenharmony_ci if (ret < 0) 156762306a36Sopenharmony_ci return ret; 156862306a36Sopenharmony_ci if (ret != 1) 156962306a36Sopenharmony_ci return -EIO; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return DP_GET_SINK_COUNT(count); 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_sink_count); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci/* 157662306a36Sopenharmony_ci * I2C-over-AUX implementation 157762306a36Sopenharmony_ci */ 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_cistatic u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | 158262306a36Sopenharmony_ci I2C_FUNC_SMBUS_READ_BLOCK_DATA | 158362306a36Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_PROC_CALL | 158462306a36Sopenharmony_ci I2C_FUNC_10BIT_ADDR; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci /* 159062306a36Sopenharmony_ci * In case of i2c defer or short i2c ack reply to a write, 159162306a36Sopenharmony_ci * we need to switch to WRITE_STATUS_UPDATE to drain the 159262306a36Sopenharmony_ci * rest of the message 159362306a36Sopenharmony_ci */ 159462306a36Sopenharmony_ci if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) { 159562306a36Sopenharmony_ci msg->request &= DP_AUX_I2C_MOT; 159662306a36Sopenharmony_ci msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci#define AUX_PRECHARGE_LEN 10 /* 10 to 16 */ 160162306a36Sopenharmony_ci#define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */ 160262306a36Sopenharmony_ci#define AUX_STOP_LEN 4 160362306a36Sopenharmony_ci#define AUX_CMD_LEN 4 160462306a36Sopenharmony_ci#define AUX_ADDRESS_LEN 20 160562306a36Sopenharmony_ci#define AUX_REPLY_PAD_LEN 4 160662306a36Sopenharmony_ci#define AUX_LENGTH_LEN 8 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci/* 160962306a36Sopenharmony_ci * Calculate the duration of the AUX request/reply in usec. Gives the 161062306a36Sopenharmony_ci * "best" case estimate, ie. successful while as short as possible. 161162306a36Sopenharmony_ci */ 161262306a36Sopenharmony_cistatic int drm_dp_aux_req_duration(const struct drm_dp_aux_msg *msg) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN + 161562306a36Sopenharmony_ci AUX_CMD_LEN + AUX_ADDRESS_LEN + AUX_LENGTH_LEN; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if ((msg->request & DP_AUX_I2C_READ) == 0) 161862306a36Sopenharmony_ci len += msg->size * 8; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return len; 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cistatic int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN + 162662306a36Sopenharmony_ci AUX_CMD_LEN + AUX_REPLY_PAD_LEN; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci /* 162962306a36Sopenharmony_ci * For read we expect what was asked. For writes there will 163062306a36Sopenharmony_ci * be 0 or 1 data bytes. Assume 0 for the "best" case. 163162306a36Sopenharmony_ci */ 163262306a36Sopenharmony_ci if (msg->request & DP_AUX_I2C_READ) 163362306a36Sopenharmony_ci len += msg->size * 8; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci return len; 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci#define I2C_START_LEN 1 163962306a36Sopenharmony_ci#define I2C_STOP_LEN 1 164062306a36Sopenharmony_ci#define I2C_ADDR_LEN 9 /* ADDRESS + R/W + ACK/NACK */ 164162306a36Sopenharmony_ci#define I2C_DATA_LEN 9 /* DATA + ACK/NACK */ 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci/* 164462306a36Sopenharmony_ci * Calculate the length of the i2c transfer in usec, assuming 164562306a36Sopenharmony_ci * the i2c bus speed is as specified. Gives the "worst" 164662306a36Sopenharmony_ci * case estimate, ie. successful while as long as possible. 164762306a36Sopenharmony_ci * Doesn't account the "MOT" bit, and instead assumes each 164862306a36Sopenharmony_ci * message includes a START, ADDRESS and STOP. Neither does it 164962306a36Sopenharmony_ci * account for additional random variables such as clock stretching. 165062306a36Sopenharmony_ci */ 165162306a36Sopenharmony_cistatic int drm_dp_i2c_msg_duration(const struct drm_dp_aux_msg *msg, 165262306a36Sopenharmony_ci int i2c_speed_khz) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci /* AUX bitrate is 1MHz, i2c bitrate as specified */ 165562306a36Sopenharmony_ci return DIV_ROUND_UP((I2C_START_LEN + I2C_ADDR_LEN + 165662306a36Sopenharmony_ci msg->size * I2C_DATA_LEN + 165762306a36Sopenharmony_ci I2C_STOP_LEN) * 1000, i2c_speed_khz); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci/* 166162306a36Sopenharmony_ci * Determine how many retries should be attempted to successfully transfer 166262306a36Sopenharmony_ci * the specified message, based on the estimated durations of the 166362306a36Sopenharmony_ci * i2c and AUX transfers. 166462306a36Sopenharmony_ci */ 166562306a36Sopenharmony_cistatic int drm_dp_i2c_retry_count(const struct drm_dp_aux_msg *msg, 166662306a36Sopenharmony_ci int i2c_speed_khz) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci int aux_time_us = drm_dp_aux_req_duration(msg) + 166962306a36Sopenharmony_ci drm_dp_aux_reply_duration(msg); 167062306a36Sopenharmony_ci int i2c_time_us = drm_dp_i2c_msg_duration(msg, i2c_speed_khz); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci return DIV_ROUND_UP(i2c_time_us, aux_time_us + AUX_RETRY_INTERVAL); 167362306a36Sopenharmony_ci} 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci/* 167662306a36Sopenharmony_ci * FIXME currently assumes 10 kHz as some real world devices seem 167762306a36Sopenharmony_ci * to require it. We should query/set the speed via DPCD if supported. 167862306a36Sopenharmony_ci */ 167962306a36Sopenharmony_cistatic int dp_aux_i2c_speed_khz __read_mostly = 10; 168062306a36Sopenharmony_cimodule_param_unsafe(dp_aux_i2c_speed_khz, int, 0644); 168162306a36Sopenharmony_ciMODULE_PARM_DESC(dp_aux_i2c_speed_khz, 168262306a36Sopenharmony_ci "Assumed speed of the i2c bus in kHz, (1-400, default 10)"); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci/* 168562306a36Sopenharmony_ci * Transfer a single I2C-over-AUX message and handle various error conditions, 168662306a36Sopenharmony_ci * retrying the transaction as appropriate. It is assumed that the 168762306a36Sopenharmony_ci * &drm_dp_aux.transfer function does not modify anything in the msg other than the 168862306a36Sopenharmony_ci * reply field. 168962306a36Sopenharmony_ci * 169062306a36Sopenharmony_ci * Returns bytes transferred on success, or a negative error code on failure. 169162306a36Sopenharmony_ci */ 169262306a36Sopenharmony_cistatic int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci unsigned int retry, defer_i2c; 169562306a36Sopenharmony_ci int ret; 169662306a36Sopenharmony_ci /* 169762306a36Sopenharmony_ci * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device 169862306a36Sopenharmony_ci * is required to retry at least seven times upon receiving AUX_DEFER 169962306a36Sopenharmony_ci * before giving up the AUX transaction. 170062306a36Sopenharmony_ci * 170162306a36Sopenharmony_ci * We also try to account for the i2c bus speed. 170262306a36Sopenharmony_ci */ 170362306a36Sopenharmony_ci int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz)); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) { 170662306a36Sopenharmony_ci ret = aux->transfer(aux, msg); 170762306a36Sopenharmony_ci if (ret < 0) { 170862306a36Sopenharmony_ci if (ret == -EBUSY) 170962306a36Sopenharmony_ci continue; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci /* 171262306a36Sopenharmony_ci * While timeouts can be errors, they're usually normal 171362306a36Sopenharmony_ci * behavior (for instance, when a driver tries to 171462306a36Sopenharmony_ci * communicate with a non-existent DisplayPort device). 171562306a36Sopenharmony_ci * Avoid spamming the kernel log with timeout errors. 171662306a36Sopenharmony_ci */ 171762306a36Sopenharmony_ci if (ret == -ETIMEDOUT) 171862306a36Sopenharmony_ci drm_dbg_kms_ratelimited(aux->drm_dev, "%s: transaction timed out\n", 171962306a36Sopenharmony_ci aux->name); 172062306a36Sopenharmony_ci else 172162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: transaction failed: %d\n", 172262306a36Sopenharmony_ci aux->name, ret); 172362306a36Sopenharmony_ci return ret; 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) { 172862306a36Sopenharmony_ci case DP_AUX_NATIVE_REPLY_ACK: 172962306a36Sopenharmony_ci /* 173062306a36Sopenharmony_ci * For I2C-over-AUX transactions this isn't enough, we 173162306a36Sopenharmony_ci * need to check for the I2C ACK reply. 173262306a36Sopenharmony_ci */ 173362306a36Sopenharmony_ci break; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci case DP_AUX_NATIVE_REPLY_NACK: 173662306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: native nack (result=%d, size=%zu)\n", 173762306a36Sopenharmony_ci aux->name, ret, msg->size); 173862306a36Sopenharmony_ci return -EREMOTEIO; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci case DP_AUX_NATIVE_REPLY_DEFER: 174162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: native defer\n", aux->name); 174262306a36Sopenharmony_ci /* 174362306a36Sopenharmony_ci * We could check for I2C bit rate capabilities and if 174462306a36Sopenharmony_ci * available adjust this interval. We could also be 174562306a36Sopenharmony_ci * more careful with DP-to-legacy adapters where a 174662306a36Sopenharmony_ci * long legacy cable may force very low I2C bit rates. 174762306a36Sopenharmony_ci * 174862306a36Sopenharmony_ci * For now just defer for long enough to hopefully be 174962306a36Sopenharmony_ci * safe for all use-cases. 175062306a36Sopenharmony_ci */ 175162306a36Sopenharmony_ci usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100); 175262306a36Sopenharmony_ci continue; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci default: 175562306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: invalid native reply %#04x\n", 175662306a36Sopenharmony_ci aux->name, msg->reply); 175762306a36Sopenharmony_ci return -EREMOTEIO; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci switch (msg->reply & DP_AUX_I2C_REPLY_MASK) { 176162306a36Sopenharmony_ci case DP_AUX_I2C_REPLY_ACK: 176262306a36Sopenharmony_ci /* 176362306a36Sopenharmony_ci * Both native ACK and I2C ACK replies received. We 176462306a36Sopenharmony_ci * can assume the transfer was successful. 176562306a36Sopenharmony_ci */ 176662306a36Sopenharmony_ci if (ret != msg->size) 176762306a36Sopenharmony_ci drm_dp_i2c_msg_write_status_update(msg); 176862306a36Sopenharmony_ci return ret; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci case DP_AUX_I2C_REPLY_NACK: 177162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: I2C nack (result=%d, size=%zu)\n", 177262306a36Sopenharmony_ci aux->name, ret, msg->size); 177362306a36Sopenharmony_ci aux->i2c_nack_count++; 177462306a36Sopenharmony_ci return -EREMOTEIO; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci case DP_AUX_I2C_REPLY_DEFER: 177762306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: I2C defer\n", aux->name); 177862306a36Sopenharmony_ci /* DP Compliance Test 4.2.2.5 Requirement: 177962306a36Sopenharmony_ci * Must have at least 7 retries for I2C defers on the 178062306a36Sopenharmony_ci * transaction to pass this test 178162306a36Sopenharmony_ci */ 178262306a36Sopenharmony_ci aux->i2c_defer_count++; 178362306a36Sopenharmony_ci if (defer_i2c < 7) 178462306a36Sopenharmony_ci defer_i2c++; 178562306a36Sopenharmony_ci usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100); 178662306a36Sopenharmony_ci drm_dp_i2c_msg_write_status_update(msg); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci continue; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci default: 179162306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: invalid I2C reply %#04x\n", 179262306a36Sopenharmony_ci aux->name, msg->reply); 179362306a36Sopenharmony_ci return -EREMOTEIO; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Too many retries, giving up\n", aux->name); 179862306a36Sopenharmony_ci return -EREMOTEIO; 179962306a36Sopenharmony_ci} 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_cistatic void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg, 180262306a36Sopenharmony_ci const struct i2c_msg *i2c_msg) 180362306a36Sopenharmony_ci{ 180462306a36Sopenharmony_ci msg->request = (i2c_msg->flags & I2C_M_RD) ? 180562306a36Sopenharmony_ci DP_AUX_I2C_READ : DP_AUX_I2C_WRITE; 180662306a36Sopenharmony_ci if (!(i2c_msg->flags & I2C_M_STOP)) 180762306a36Sopenharmony_ci msg->request |= DP_AUX_I2C_MOT; 180862306a36Sopenharmony_ci} 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci/* 181162306a36Sopenharmony_ci * Keep retrying drm_dp_i2c_do_msg until all data has been transferred. 181262306a36Sopenharmony_ci * 181362306a36Sopenharmony_ci * Returns an error code on failure, or a recommended transfer size on success. 181462306a36Sopenharmony_ci */ 181562306a36Sopenharmony_cistatic int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci int err, ret = orig_msg->size; 181862306a36Sopenharmony_ci struct drm_dp_aux_msg msg = *orig_msg; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci while (msg.size > 0) { 182162306a36Sopenharmony_ci err = drm_dp_i2c_do_msg(aux, &msg); 182262306a36Sopenharmony_ci if (err <= 0) 182362306a36Sopenharmony_ci return err == 0 ? -EPROTO : err; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (err < msg.size && err < ret) { 182662306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 182762306a36Sopenharmony_ci "%s: Partial I2C reply: requested %zu bytes got %d bytes\n", 182862306a36Sopenharmony_ci aux->name, msg.size, err); 182962306a36Sopenharmony_ci ret = err; 183062306a36Sopenharmony_ci } 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci msg.size -= err; 183362306a36Sopenharmony_ci msg.buffer += err; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci return ret; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci/* 184062306a36Sopenharmony_ci * Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX 184162306a36Sopenharmony_ci * packets to be as large as possible. If not, the I2C transactions never 184262306a36Sopenharmony_ci * succeed. Hence the default is maximum. 184362306a36Sopenharmony_ci */ 184462306a36Sopenharmony_cistatic int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES; 184562306a36Sopenharmony_cimodule_param_unsafe(dp_aux_i2c_transfer_size, int, 0644); 184662306a36Sopenharmony_ciMODULE_PARM_DESC(dp_aux_i2c_transfer_size, 184762306a36Sopenharmony_ci "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)"); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, 185062306a36Sopenharmony_ci int num) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci struct drm_dp_aux *aux = adapter->algo_data; 185362306a36Sopenharmony_ci unsigned int i, j; 185462306a36Sopenharmony_ci unsigned transfer_size; 185562306a36Sopenharmony_ci struct drm_dp_aux_msg msg; 185662306a36Sopenharmony_ci int err = 0; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci for (i = 0; i < num; i++) { 186362306a36Sopenharmony_ci msg.address = msgs[i].addr; 186462306a36Sopenharmony_ci drm_dp_i2c_msg_set_request(&msg, &msgs[i]); 186562306a36Sopenharmony_ci /* Send a bare address packet to start the transaction. 186662306a36Sopenharmony_ci * Zero sized messages specify an address only (bare 186762306a36Sopenharmony_ci * address) transaction. 186862306a36Sopenharmony_ci */ 186962306a36Sopenharmony_ci msg.buffer = NULL; 187062306a36Sopenharmony_ci msg.size = 0; 187162306a36Sopenharmony_ci err = drm_dp_i2c_do_msg(aux, &msg); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci /* 187462306a36Sopenharmony_ci * Reset msg.request in case in case it got 187562306a36Sopenharmony_ci * changed into a WRITE_STATUS_UPDATE. 187662306a36Sopenharmony_ci */ 187762306a36Sopenharmony_ci drm_dp_i2c_msg_set_request(&msg, &msgs[i]); 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci if (err < 0) 188062306a36Sopenharmony_ci break; 188162306a36Sopenharmony_ci /* We want each transaction to be as large as possible, but 188262306a36Sopenharmony_ci * we'll go to smaller sizes if the hardware gives us a 188362306a36Sopenharmony_ci * short reply. 188462306a36Sopenharmony_ci */ 188562306a36Sopenharmony_ci transfer_size = dp_aux_i2c_transfer_size; 188662306a36Sopenharmony_ci for (j = 0; j < msgs[i].len; j += msg.size) { 188762306a36Sopenharmony_ci msg.buffer = msgs[i].buf + j; 188862306a36Sopenharmony_ci msg.size = min(transfer_size, msgs[i].len - j); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci err = drm_dp_i2c_drain_msg(aux, &msg); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci /* 189362306a36Sopenharmony_ci * Reset msg.request in case in case it got 189462306a36Sopenharmony_ci * changed into a WRITE_STATUS_UPDATE. 189562306a36Sopenharmony_ci */ 189662306a36Sopenharmony_ci drm_dp_i2c_msg_set_request(&msg, &msgs[i]); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (err < 0) 189962306a36Sopenharmony_ci break; 190062306a36Sopenharmony_ci transfer_size = err; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci if (err < 0) 190362306a36Sopenharmony_ci break; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci if (err >= 0) 190662306a36Sopenharmony_ci err = num; 190762306a36Sopenharmony_ci /* Send a bare address packet to close out the transaction. 190862306a36Sopenharmony_ci * Zero sized messages specify an address only (bare 190962306a36Sopenharmony_ci * address) transaction. 191062306a36Sopenharmony_ci */ 191162306a36Sopenharmony_ci msg.request &= ~DP_AUX_I2C_MOT; 191262306a36Sopenharmony_ci msg.buffer = NULL; 191362306a36Sopenharmony_ci msg.size = 0; 191462306a36Sopenharmony_ci (void)drm_dp_i2c_do_msg(aux, &msg); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci return err; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic const struct i2c_algorithm drm_dp_i2c_algo = { 192062306a36Sopenharmony_ci .functionality = drm_dp_i2c_functionality, 192162306a36Sopenharmony_ci .master_xfer = drm_dp_i2c_xfer, 192262306a36Sopenharmony_ci}; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_cistatic struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c) 192562306a36Sopenharmony_ci{ 192662306a36Sopenharmony_ci return container_of(i2c, struct drm_dp_aux, ddc); 192762306a36Sopenharmony_ci} 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_cistatic void lock_bus(struct i2c_adapter *i2c, unsigned int flags) 193062306a36Sopenharmony_ci{ 193162306a36Sopenharmony_ci mutex_lock(&i2c_to_aux(i2c)->hw_mutex); 193262306a36Sopenharmony_ci} 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_cistatic int trylock_bus(struct i2c_adapter *i2c, unsigned int flags) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex); 193762306a36Sopenharmony_ci} 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_cistatic void unlock_bus(struct i2c_adapter *i2c, unsigned int flags) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci mutex_unlock(&i2c_to_aux(i2c)->hw_mutex); 194262306a36Sopenharmony_ci} 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_cistatic const struct i2c_lock_operations drm_dp_i2c_lock_ops = { 194562306a36Sopenharmony_ci .lock_bus = lock_bus, 194662306a36Sopenharmony_ci .trylock_bus = trylock_bus, 194762306a36Sopenharmony_ci .unlock_bus = unlock_bus, 194862306a36Sopenharmony_ci}; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_cistatic int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci u8 buf, count; 195362306a36Sopenharmony_ci int ret; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); 195662306a36Sopenharmony_ci if (ret < 0) 195762306a36Sopenharmony_ci return ret; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci WARN_ON(!(buf & DP_TEST_SINK_START)); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK_MISC, &buf); 196262306a36Sopenharmony_ci if (ret < 0) 196362306a36Sopenharmony_ci return ret; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci count = buf & DP_TEST_COUNT_MASK; 196662306a36Sopenharmony_ci if (count == aux->crc_count) 196762306a36Sopenharmony_ci return -EAGAIN; /* No CRC yet */ 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci aux->crc_count = count; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* 197262306a36Sopenharmony_ci * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes 197362306a36Sopenharmony_ci * per component (RGB or CrYCb). 197462306a36Sopenharmony_ci */ 197562306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, DP_TEST_CRC_R_CR, crc, 6); 197662306a36Sopenharmony_ci if (ret < 0) 197762306a36Sopenharmony_ci return ret; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci return 0; 198062306a36Sopenharmony_ci} 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cistatic void drm_dp_aux_crc_work(struct work_struct *work) 198362306a36Sopenharmony_ci{ 198462306a36Sopenharmony_ci struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux, 198562306a36Sopenharmony_ci crc_work); 198662306a36Sopenharmony_ci struct drm_crtc *crtc; 198762306a36Sopenharmony_ci u8 crc_bytes[6]; 198862306a36Sopenharmony_ci uint32_t crcs[3]; 198962306a36Sopenharmony_ci int ret; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci if (WARN_ON(!aux->crtc)) 199262306a36Sopenharmony_ci return; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci crtc = aux->crtc; 199562306a36Sopenharmony_ci while (crtc->crc.opened) { 199662306a36Sopenharmony_ci drm_crtc_wait_one_vblank(crtc); 199762306a36Sopenharmony_ci if (!crtc->crc.opened) 199862306a36Sopenharmony_ci break; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci ret = drm_dp_aux_get_crc(aux, crc_bytes); 200162306a36Sopenharmony_ci if (ret == -EAGAIN) { 200262306a36Sopenharmony_ci usleep_range(1000, 2000); 200362306a36Sopenharmony_ci ret = drm_dp_aux_get_crc(aux, crc_bytes); 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci if (ret == -EAGAIN) { 200762306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Get CRC failed after retrying: %d\n", 200862306a36Sopenharmony_ci aux->name, ret); 200962306a36Sopenharmony_ci continue; 201062306a36Sopenharmony_ci } else if (ret) { 201162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to get a CRC: %d\n", aux->name, ret); 201262306a36Sopenharmony_ci continue; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci crcs[0] = crc_bytes[0] | crc_bytes[1] << 8; 201662306a36Sopenharmony_ci crcs[1] = crc_bytes[2] | crc_bytes[3] << 8; 201762306a36Sopenharmony_ci crcs[2] = crc_bytes[4] | crc_bytes[5] << 8; 201862306a36Sopenharmony_ci drm_crtc_add_crc_entry(crtc, false, 0, crcs); 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci/** 202362306a36Sopenharmony_ci * drm_dp_remote_aux_init() - minimally initialise a remote aux channel 202462306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 202562306a36Sopenharmony_ci * 202662306a36Sopenharmony_ci * Used for remote aux channel in general. Merely initialize the crc work 202762306a36Sopenharmony_ci * struct. 202862306a36Sopenharmony_ci */ 202962306a36Sopenharmony_civoid drm_dp_remote_aux_init(struct drm_dp_aux *aux) 203062306a36Sopenharmony_ci{ 203162306a36Sopenharmony_ci INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); 203262306a36Sopenharmony_ci} 203362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_remote_aux_init); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci/** 203662306a36Sopenharmony_ci * drm_dp_aux_init() - minimally initialise an aux channel 203762306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 203862306a36Sopenharmony_ci * 203962306a36Sopenharmony_ci * If you need to use the drm_dp_aux's i2c adapter prior to registering it with 204062306a36Sopenharmony_ci * the outside world, call drm_dp_aux_init() first. For drivers which are 204162306a36Sopenharmony_ci * grandparents to their AUX adapters (e.g. the AUX adapter is parented by a 204262306a36Sopenharmony_ci * &drm_connector), you must still call drm_dp_aux_register() once the connector 204362306a36Sopenharmony_ci * has been registered to allow userspace access to the auxiliary DP channel. 204462306a36Sopenharmony_ci * Likewise, for such drivers you should also assign &drm_dp_aux.drm_dev as 204562306a36Sopenharmony_ci * early as possible so that the &drm_device that corresponds to the AUX adapter 204662306a36Sopenharmony_ci * may be mentioned in debugging output from the DRM DP helpers. 204762306a36Sopenharmony_ci * 204862306a36Sopenharmony_ci * For devices which use a separate platform device for their AUX adapters, this 204962306a36Sopenharmony_ci * may be called as early as required by the driver. 205062306a36Sopenharmony_ci * 205162306a36Sopenharmony_ci */ 205262306a36Sopenharmony_civoid drm_dp_aux_init(struct drm_dp_aux *aux) 205362306a36Sopenharmony_ci{ 205462306a36Sopenharmony_ci mutex_init(&aux->hw_mutex); 205562306a36Sopenharmony_ci mutex_init(&aux->cec.lock); 205662306a36Sopenharmony_ci INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci aux->ddc.algo = &drm_dp_i2c_algo; 205962306a36Sopenharmony_ci aux->ddc.algo_data = aux; 206062306a36Sopenharmony_ci aux->ddc.retries = 3; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci aux->ddc.lock_ops = &drm_dp_i2c_lock_ops; 206362306a36Sopenharmony_ci} 206462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_aux_init); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci/** 206762306a36Sopenharmony_ci * drm_dp_aux_register() - initialise and register aux channel 206862306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 206962306a36Sopenharmony_ci * 207062306a36Sopenharmony_ci * Automatically calls drm_dp_aux_init() if this hasn't been done yet. This 207162306a36Sopenharmony_ci * should only be called once the parent of @aux, &drm_dp_aux.dev, is 207262306a36Sopenharmony_ci * initialized. For devices which are grandparents of their AUX channels, 207362306a36Sopenharmony_ci * &drm_dp_aux.dev will typically be the &drm_connector &device which 207462306a36Sopenharmony_ci * corresponds to @aux. For these devices, it's advised to call 207562306a36Sopenharmony_ci * drm_dp_aux_register() in &drm_connector_funcs.late_register, and likewise to 207662306a36Sopenharmony_ci * call drm_dp_aux_unregister() in &drm_connector_funcs.early_unregister. 207762306a36Sopenharmony_ci * Functions which don't follow this will likely Oops when 207862306a36Sopenharmony_ci * %CONFIG_DRM_DP_AUX_CHARDEV is enabled. 207962306a36Sopenharmony_ci * 208062306a36Sopenharmony_ci * For devices where the AUX channel is a device that exists independently of 208162306a36Sopenharmony_ci * the &drm_device that uses it, such as SoCs and bridge devices, it is 208262306a36Sopenharmony_ci * recommended to call drm_dp_aux_register() after a &drm_device has been 208362306a36Sopenharmony_ci * assigned to &drm_dp_aux.drm_dev, and likewise to call 208462306a36Sopenharmony_ci * drm_dp_aux_unregister() once the &drm_device should no longer be associated 208562306a36Sopenharmony_ci * with the AUX channel (e.g. on bridge detach). 208662306a36Sopenharmony_ci * 208762306a36Sopenharmony_ci * Drivers which need to use the aux channel before either of the two points 208862306a36Sopenharmony_ci * mentioned above need to call drm_dp_aux_init() in order to use the AUX 208962306a36Sopenharmony_ci * channel before registration. 209062306a36Sopenharmony_ci * 209162306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 209262306a36Sopenharmony_ci */ 209362306a36Sopenharmony_ciint drm_dp_aux_register(struct drm_dp_aux *aux) 209462306a36Sopenharmony_ci{ 209562306a36Sopenharmony_ci int ret; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci WARN_ON_ONCE(!aux->drm_dev); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci if (!aux->ddc.algo) 210062306a36Sopenharmony_ci drm_dp_aux_init(aux); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci aux->ddc.class = I2C_CLASS_DDC; 210362306a36Sopenharmony_ci aux->ddc.owner = THIS_MODULE; 210462306a36Sopenharmony_ci aux->ddc.dev.parent = aux->dev; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci strscpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), 210762306a36Sopenharmony_ci sizeof(aux->ddc.name)); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci ret = drm_dp_aux_register_devnode(aux); 211062306a36Sopenharmony_ci if (ret) 211162306a36Sopenharmony_ci return ret; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci ret = i2c_add_adapter(&aux->ddc); 211462306a36Sopenharmony_ci if (ret) { 211562306a36Sopenharmony_ci drm_dp_aux_unregister_devnode(aux); 211662306a36Sopenharmony_ci return ret; 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci return 0; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_aux_register); 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci/** 212462306a36Sopenharmony_ci * drm_dp_aux_unregister() - unregister an AUX adapter 212562306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 212662306a36Sopenharmony_ci */ 212762306a36Sopenharmony_civoid drm_dp_aux_unregister(struct drm_dp_aux *aux) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci drm_dp_aux_unregister_devnode(aux); 213062306a36Sopenharmony_ci i2c_del_adapter(&aux->ddc); 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_aux_unregister); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci#define PSR_SETUP_TIME(x) [DP_PSR_SETUP_TIME_ ## x >> DP_PSR_SETUP_TIME_SHIFT] = (x) 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci/** 213762306a36Sopenharmony_ci * drm_dp_psr_setup_time() - PSR setup in time usec 213862306a36Sopenharmony_ci * @psr_cap: PSR capabilities from DPCD 213962306a36Sopenharmony_ci * 214062306a36Sopenharmony_ci * Returns: 214162306a36Sopenharmony_ci * PSR setup time for the panel in microseconds, negative 214262306a36Sopenharmony_ci * error code on failure. 214362306a36Sopenharmony_ci */ 214462306a36Sopenharmony_ciint drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]) 214562306a36Sopenharmony_ci{ 214662306a36Sopenharmony_ci static const u16 psr_setup_time_us[] = { 214762306a36Sopenharmony_ci PSR_SETUP_TIME(330), 214862306a36Sopenharmony_ci PSR_SETUP_TIME(275), 214962306a36Sopenharmony_ci PSR_SETUP_TIME(220), 215062306a36Sopenharmony_ci PSR_SETUP_TIME(165), 215162306a36Sopenharmony_ci PSR_SETUP_TIME(110), 215262306a36Sopenharmony_ci PSR_SETUP_TIME(55), 215362306a36Sopenharmony_ci PSR_SETUP_TIME(0), 215462306a36Sopenharmony_ci }; 215562306a36Sopenharmony_ci int i; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci i = (psr_cap[1] & DP_PSR_SETUP_TIME_MASK) >> DP_PSR_SETUP_TIME_SHIFT; 215862306a36Sopenharmony_ci if (i >= ARRAY_SIZE(psr_setup_time_us)) 215962306a36Sopenharmony_ci return -EINVAL; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci return psr_setup_time_us[i]; 216262306a36Sopenharmony_ci} 216362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_psr_setup_time); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci#undef PSR_SETUP_TIME 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci/** 216862306a36Sopenharmony_ci * drm_dp_start_crc() - start capture of frame CRCs 216962306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 217062306a36Sopenharmony_ci * @crtc: CRTC displaying the frames whose CRCs are to be captured 217162306a36Sopenharmony_ci * 217262306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 217362306a36Sopenharmony_ci */ 217462306a36Sopenharmony_ciint drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc) 217562306a36Sopenharmony_ci{ 217662306a36Sopenharmony_ci u8 buf; 217762306a36Sopenharmony_ci int ret; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); 218062306a36Sopenharmony_ci if (ret < 0) 218162306a36Sopenharmony_ci return ret; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf | DP_TEST_SINK_START); 218462306a36Sopenharmony_ci if (ret < 0) 218562306a36Sopenharmony_ci return ret; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci aux->crc_count = 0; 218862306a36Sopenharmony_ci aux->crtc = crtc; 218962306a36Sopenharmony_ci schedule_work(&aux->crc_work); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci return 0; 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_start_crc); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci/** 219662306a36Sopenharmony_ci * drm_dp_stop_crc() - stop capture of frame CRCs 219762306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 219862306a36Sopenharmony_ci * 219962306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 220062306a36Sopenharmony_ci */ 220162306a36Sopenharmony_ciint drm_dp_stop_crc(struct drm_dp_aux *aux) 220262306a36Sopenharmony_ci{ 220362306a36Sopenharmony_ci u8 buf; 220462306a36Sopenharmony_ci int ret; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); 220762306a36Sopenharmony_ci if (ret < 0) 220862306a36Sopenharmony_ci return ret; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf & ~DP_TEST_SINK_START); 221162306a36Sopenharmony_ci if (ret < 0) 221262306a36Sopenharmony_ci return ret; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci flush_work(&aux->crc_work); 221562306a36Sopenharmony_ci aux->crtc = NULL; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci return 0; 221862306a36Sopenharmony_ci} 221962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_stop_crc); 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_cistruct dpcd_quirk { 222262306a36Sopenharmony_ci u8 oui[3]; 222362306a36Sopenharmony_ci u8 device_id[6]; 222462306a36Sopenharmony_ci bool is_branch; 222562306a36Sopenharmony_ci u32 quirks; 222662306a36Sopenharmony_ci}; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci#define OUI(first, second, third) { (first), (second), (third) } 222962306a36Sopenharmony_ci#define DEVICE_ID(first, second, third, fourth, fifth, sixth) \ 223062306a36Sopenharmony_ci { (first), (second), (third), (fourth), (fifth), (sixth) } 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci#define DEVICE_ID_ANY DEVICE_ID(0, 0, 0, 0, 0, 0) 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cistatic const struct dpcd_quirk dpcd_quirk_list[] = { 223562306a36Sopenharmony_ci /* Analogix 7737 needs reduced M and N at HBR2 link rates */ 223662306a36Sopenharmony_ci { OUI(0x00, 0x22, 0xb9), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_CONSTANT_N) }, 223762306a36Sopenharmony_ci /* LG LP140WF6-SPM1 eDP panel */ 223862306a36Sopenharmony_ci { OUI(0x00, 0x22, 0xb9), DEVICE_ID('s', 'i', 'v', 'a', 'r', 'T'), false, BIT(DP_DPCD_QUIRK_CONSTANT_N) }, 223962306a36Sopenharmony_ci /* Apple panels need some additional handling to support PSR */ 224062306a36Sopenharmony_ci { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) }, 224162306a36Sopenharmony_ci /* CH7511 seems to leave SINK_COUNT zeroed */ 224262306a36Sopenharmony_ci { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) }, 224362306a36Sopenharmony_ci /* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */ 224462306a36Sopenharmony_ci { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) }, 224562306a36Sopenharmony_ci /* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */ 224662306a36Sopenharmony_ci { OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) }, 224762306a36Sopenharmony_ci}; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci#undef OUI 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci/* 225262306a36Sopenharmony_ci * Get a bit mask of DPCD quirks for the sink/branch device identified by 225362306a36Sopenharmony_ci * ident. The quirk data is shared but it's up to the drivers to act on the 225462306a36Sopenharmony_ci * data. 225562306a36Sopenharmony_ci * 225662306a36Sopenharmony_ci * For now, only the OUI (first three bytes) is used, but this may be extended 225762306a36Sopenharmony_ci * to device identification string and hardware/firmware revisions later. 225862306a36Sopenharmony_ci */ 225962306a36Sopenharmony_cistatic u32 226062306a36Sopenharmony_cidrm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch) 226162306a36Sopenharmony_ci{ 226262306a36Sopenharmony_ci const struct dpcd_quirk *quirk; 226362306a36Sopenharmony_ci u32 quirks = 0; 226462306a36Sopenharmony_ci int i; 226562306a36Sopenharmony_ci u8 any_device[] = DEVICE_ID_ANY; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dpcd_quirk_list); i++) { 226862306a36Sopenharmony_ci quirk = &dpcd_quirk_list[i]; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci if (quirk->is_branch != is_branch) 227162306a36Sopenharmony_ci continue; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci if (memcmp(quirk->oui, ident->oui, sizeof(ident->oui)) != 0) 227462306a36Sopenharmony_ci continue; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci if (memcmp(quirk->device_id, any_device, sizeof(any_device)) != 0 && 227762306a36Sopenharmony_ci memcmp(quirk->device_id, ident->device_id, sizeof(ident->device_id)) != 0) 227862306a36Sopenharmony_ci continue; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci quirks |= quirk->quirks; 228162306a36Sopenharmony_ci } 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci return quirks; 228462306a36Sopenharmony_ci} 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci#undef DEVICE_ID_ANY 228762306a36Sopenharmony_ci#undef DEVICE_ID 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci/** 229062306a36Sopenharmony_ci * drm_dp_read_desc - read sink/branch descriptor from DPCD 229162306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 229262306a36Sopenharmony_ci * @desc: Device descriptor to fill from DPCD 229362306a36Sopenharmony_ci * @is_branch: true for branch devices, false for sink devices 229462306a36Sopenharmony_ci * 229562306a36Sopenharmony_ci * Read DPCD 0x400 (sink) or 0x500 (branch) into @desc. Also debug log the 229662306a36Sopenharmony_ci * identification. 229762306a36Sopenharmony_ci * 229862306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 229962306a36Sopenharmony_ci */ 230062306a36Sopenharmony_ciint drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc, 230162306a36Sopenharmony_ci bool is_branch) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci struct drm_dp_dpcd_ident *ident = &desc->ident; 230462306a36Sopenharmony_ci unsigned int offset = is_branch ? DP_BRANCH_OUI : DP_SINK_OUI; 230562306a36Sopenharmony_ci int ret, dev_id_len; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, offset, ident, sizeof(*ident)); 230862306a36Sopenharmony_ci if (ret < 0) 230962306a36Sopenharmony_ci return ret; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci desc->quirks = drm_dp_get_quirks(ident, is_branch); 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id)); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 231662306a36Sopenharmony_ci "%s: DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n", 231762306a36Sopenharmony_ci aux->name, is_branch ? "branch" : "sink", 231862306a36Sopenharmony_ci (int)sizeof(ident->oui), ident->oui, dev_id_len, 231962306a36Sopenharmony_ci ident->device_id, ident->hw_rev >> 4, ident->hw_rev & 0xf, 232062306a36Sopenharmony_ci ident->sw_major_rev, ident->sw_minor_rev, desc->quirks); 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_desc); 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci/** 232762306a36Sopenharmony_ci * drm_dp_dsc_sink_max_slice_count() - Get the max slice count 232862306a36Sopenharmony_ci * supported by the DSC sink. 232962306a36Sopenharmony_ci * @dsc_dpcd: DSC capabilities from DPCD 233062306a36Sopenharmony_ci * @is_edp: true if its eDP, false for DP 233162306a36Sopenharmony_ci * 233262306a36Sopenharmony_ci * Read the slice capabilities DPCD register from DSC sink to get 233362306a36Sopenharmony_ci * the maximum slice count supported. This is used to populate 233462306a36Sopenharmony_ci * the DSC parameters in the &struct drm_dsc_config by the driver. 233562306a36Sopenharmony_ci * Driver creates an infoframe using these parameters to populate 233662306a36Sopenharmony_ci * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC 233762306a36Sopenharmony_ci * infoframe using the helper function drm_dsc_pps_infoframe_pack() 233862306a36Sopenharmony_ci * 233962306a36Sopenharmony_ci * Returns: 234062306a36Sopenharmony_ci * Maximum slice count supported by DSC sink or 0 its invalid 234162306a36Sopenharmony_ci */ 234262306a36Sopenharmony_ciu8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], 234362306a36Sopenharmony_ci bool is_edp) 234462306a36Sopenharmony_ci{ 234562306a36Sopenharmony_ci u8 slice_cap1 = dsc_dpcd[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT]; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci if (is_edp) { 234862306a36Sopenharmony_ci /* For eDP, register DSC_SLICE_CAPABILITIES_1 gives slice count */ 234962306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_4_PER_DP_DSC_SINK) 235062306a36Sopenharmony_ci return 4; 235162306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_2_PER_DP_DSC_SINK) 235262306a36Sopenharmony_ci return 2; 235362306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_1_PER_DP_DSC_SINK) 235462306a36Sopenharmony_ci return 1; 235562306a36Sopenharmony_ci } else { 235662306a36Sopenharmony_ci /* For DP, use values from DSC_SLICE_CAP_1 and DSC_SLICE_CAP2 */ 235762306a36Sopenharmony_ci u8 slice_cap2 = dsc_dpcd[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT]; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci if (slice_cap2 & DP_DSC_24_PER_DP_DSC_SINK) 236062306a36Sopenharmony_ci return 24; 236162306a36Sopenharmony_ci if (slice_cap2 & DP_DSC_20_PER_DP_DSC_SINK) 236262306a36Sopenharmony_ci return 20; 236362306a36Sopenharmony_ci if (slice_cap2 & DP_DSC_16_PER_DP_DSC_SINK) 236462306a36Sopenharmony_ci return 16; 236562306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_12_PER_DP_DSC_SINK) 236662306a36Sopenharmony_ci return 12; 236762306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_10_PER_DP_DSC_SINK) 236862306a36Sopenharmony_ci return 10; 236962306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_8_PER_DP_DSC_SINK) 237062306a36Sopenharmony_ci return 8; 237162306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_6_PER_DP_DSC_SINK) 237262306a36Sopenharmony_ci return 6; 237362306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_4_PER_DP_DSC_SINK) 237462306a36Sopenharmony_ci return 4; 237562306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_2_PER_DP_DSC_SINK) 237662306a36Sopenharmony_ci return 2; 237762306a36Sopenharmony_ci if (slice_cap1 & DP_DSC_1_PER_DP_DSC_SINK) 237862306a36Sopenharmony_ci return 1; 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci return 0; 238262306a36Sopenharmony_ci} 238362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dsc_sink_max_slice_count); 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci/** 238662306a36Sopenharmony_ci * drm_dp_dsc_sink_line_buf_depth() - Get the line buffer depth in bits 238762306a36Sopenharmony_ci * @dsc_dpcd: DSC capabilities from DPCD 238862306a36Sopenharmony_ci * 238962306a36Sopenharmony_ci * Read the DSC DPCD register to parse the line buffer depth in bits which is 239062306a36Sopenharmony_ci * number of bits of precision within the decoder line buffer supported by 239162306a36Sopenharmony_ci * the DSC sink. This is used to populate the DSC parameters in the 239262306a36Sopenharmony_ci * &struct drm_dsc_config by the driver. 239362306a36Sopenharmony_ci * Driver creates an infoframe using these parameters to populate 239462306a36Sopenharmony_ci * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC 239562306a36Sopenharmony_ci * infoframe using the helper function drm_dsc_pps_infoframe_pack() 239662306a36Sopenharmony_ci * 239762306a36Sopenharmony_ci * Returns: 239862306a36Sopenharmony_ci * Line buffer depth supported by DSC panel or 0 its invalid 239962306a36Sopenharmony_ci */ 240062306a36Sopenharmony_ciu8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) 240162306a36Sopenharmony_ci{ 240262306a36Sopenharmony_ci u8 line_buf_depth = dsc_dpcd[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT]; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci switch (line_buf_depth & DP_DSC_LINE_BUF_BIT_DEPTH_MASK) { 240562306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_9: 240662306a36Sopenharmony_ci return 9; 240762306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_10: 240862306a36Sopenharmony_ci return 10; 240962306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_11: 241062306a36Sopenharmony_ci return 11; 241162306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_12: 241262306a36Sopenharmony_ci return 12; 241362306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_13: 241462306a36Sopenharmony_ci return 13; 241562306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_14: 241662306a36Sopenharmony_ci return 14; 241762306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_15: 241862306a36Sopenharmony_ci return 15; 241962306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_16: 242062306a36Sopenharmony_ci return 16; 242162306a36Sopenharmony_ci case DP_DSC_LINE_BUF_BIT_DEPTH_8: 242262306a36Sopenharmony_ci return 8; 242362306a36Sopenharmony_ci } 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci return 0; 242662306a36Sopenharmony_ci} 242762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dsc_sink_line_buf_depth); 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci/** 243062306a36Sopenharmony_ci * drm_dp_dsc_sink_supported_input_bpcs() - Get all the input bits per component 243162306a36Sopenharmony_ci * values supported by the DSC sink. 243262306a36Sopenharmony_ci * @dsc_dpcd: DSC capabilities from DPCD 243362306a36Sopenharmony_ci * @dsc_bpc: An array to be filled by this helper with supported 243462306a36Sopenharmony_ci * input bpcs. 243562306a36Sopenharmony_ci * 243662306a36Sopenharmony_ci * Read the DSC DPCD from the sink device to parse the supported bits per 243762306a36Sopenharmony_ci * component values. This is used to populate the DSC parameters 243862306a36Sopenharmony_ci * in the &struct drm_dsc_config by the driver. 243962306a36Sopenharmony_ci * Driver creates an infoframe using these parameters to populate 244062306a36Sopenharmony_ci * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC 244162306a36Sopenharmony_ci * infoframe using the helper function drm_dsc_pps_infoframe_pack() 244262306a36Sopenharmony_ci * 244362306a36Sopenharmony_ci * Returns: 244462306a36Sopenharmony_ci * Number of input BPC values parsed from the DPCD 244562306a36Sopenharmony_ci */ 244662306a36Sopenharmony_ciint drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], 244762306a36Sopenharmony_ci u8 dsc_bpc[3]) 244862306a36Sopenharmony_ci{ 244962306a36Sopenharmony_ci int num_bpc = 0; 245062306a36Sopenharmony_ci u8 color_depth = dsc_dpcd[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT]; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci if (color_depth & DP_DSC_12_BPC) 245362306a36Sopenharmony_ci dsc_bpc[num_bpc++] = 12; 245462306a36Sopenharmony_ci if (color_depth & DP_DSC_10_BPC) 245562306a36Sopenharmony_ci dsc_bpc[num_bpc++] = 10; 245662306a36Sopenharmony_ci if (color_depth & DP_DSC_8_BPC) 245762306a36Sopenharmony_ci dsc_bpc[num_bpc++] = 8; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci return num_bpc; 246062306a36Sopenharmony_ci} 246162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs); 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_cistatic int drm_dp_read_lttpr_regs(struct drm_dp_aux *aux, 246462306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], int address, 246562306a36Sopenharmony_ci u8 *buf, int buf_size) 246662306a36Sopenharmony_ci{ 246762306a36Sopenharmony_ci /* 246862306a36Sopenharmony_ci * At least the DELL P2715Q monitor with a DPCD_REV < 0x14 returns 246962306a36Sopenharmony_ci * corrupted values when reading from the 0xF0000- range with a block 247062306a36Sopenharmony_ci * size bigger than 1. 247162306a36Sopenharmony_ci */ 247262306a36Sopenharmony_ci int block_size = dpcd[DP_DPCD_REV] < 0x14 ? 1 : buf_size; 247362306a36Sopenharmony_ci int offset; 247462306a36Sopenharmony_ci int ret; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci for (offset = 0; offset < buf_size; offset += block_size) { 247762306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, 247862306a36Sopenharmony_ci address + offset, 247962306a36Sopenharmony_ci &buf[offset], block_size); 248062306a36Sopenharmony_ci if (ret < 0) 248162306a36Sopenharmony_ci return ret; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci WARN_ON(ret != block_size); 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci return 0; 248762306a36Sopenharmony_ci} 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci/** 249062306a36Sopenharmony_ci * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities 249162306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 249262306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 249362306a36Sopenharmony_ci * @caps: buffer to return the capability info in 249462306a36Sopenharmony_ci * 249562306a36Sopenharmony_ci * Read capabilities common to all LTTPRs. 249662306a36Sopenharmony_ci * 249762306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 249862306a36Sopenharmony_ci */ 249962306a36Sopenharmony_ciint drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux, 250062306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], 250162306a36Sopenharmony_ci u8 caps[DP_LTTPR_COMMON_CAP_SIZE]) 250262306a36Sopenharmony_ci{ 250362306a36Sopenharmony_ci return drm_dp_read_lttpr_regs(aux, dpcd, 250462306a36Sopenharmony_ci DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV, 250562306a36Sopenharmony_ci caps, DP_LTTPR_COMMON_CAP_SIZE); 250662306a36Sopenharmony_ci} 250762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_lttpr_common_caps); 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci/** 251062306a36Sopenharmony_ci * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY 251162306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 251262306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 251362306a36Sopenharmony_ci * @dp_phy: LTTPR PHY to read the capabilities for 251462306a36Sopenharmony_ci * @caps: buffer to return the capability info in 251562306a36Sopenharmony_ci * 251662306a36Sopenharmony_ci * Read the capabilities for the given LTTPR PHY. 251762306a36Sopenharmony_ci * 251862306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_ciint drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux, 252162306a36Sopenharmony_ci const u8 dpcd[DP_RECEIVER_CAP_SIZE], 252262306a36Sopenharmony_ci enum drm_dp_phy dp_phy, 252362306a36Sopenharmony_ci u8 caps[DP_LTTPR_PHY_CAP_SIZE]) 252462306a36Sopenharmony_ci{ 252562306a36Sopenharmony_ci return drm_dp_read_lttpr_regs(aux, dpcd, 252662306a36Sopenharmony_ci DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy), 252762306a36Sopenharmony_ci caps, DP_LTTPR_PHY_CAP_SIZE); 252862306a36Sopenharmony_ci} 252962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps); 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_cistatic u8 dp_lttpr_common_cap(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE], int r) 253262306a36Sopenharmony_ci{ 253362306a36Sopenharmony_ci return caps[r - DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV]; 253462306a36Sopenharmony_ci} 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci/** 253762306a36Sopenharmony_ci * drm_dp_lttpr_count - get the number of detected LTTPRs 253862306a36Sopenharmony_ci * @caps: LTTPR common capabilities 253962306a36Sopenharmony_ci * 254062306a36Sopenharmony_ci * Get the number of detected LTTPRs from the LTTPR common capabilities info. 254162306a36Sopenharmony_ci * 254262306a36Sopenharmony_ci * Returns: 254362306a36Sopenharmony_ci * -ERANGE if more than supported number (8) of LTTPRs are detected 254462306a36Sopenharmony_ci * -EINVAL if the DP_PHY_REPEATER_CNT register contains an invalid value 254562306a36Sopenharmony_ci * otherwise the number of detected LTTPRs 254662306a36Sopenharmony_ci */ 254762306a36Sopenharmony_ciint drm_dp_lttpr_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]) 254862306a36Sopenharmony_ci{ 254962306a36Sopenharmony_ci u8 count = dp_lttpr_common_cap(caps, DP_PHY_REPEATER_CNT); 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci switch (hweight8(count)) { 255262306a36Sopenharmony_ci case 0: 255362306a36Sopenharmony_ci return 0; 255462306a36Sopenharmony_ci case 1: 255562306a36Sopenharmony_ci return 8 - ilog2(count); 255662306a36Sopenharmony_ci case 8: 255762306a36Sopenharmony_ci return -ERANGE; 255862306a36Sopenharmony_ci default: 255962306a36Sopenharmony_ci return -EINVAL; 256062306a36Sopenharmony_ci } 256162306a36Sopenharmony_ci} 256262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_count); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci/** 256562306a36Sopenharmony_ci * drm_dp_lttpr_max_link_rate - get the maximum link rate supported by all LTTPRs 256662306a36Sopenharmony_ci * @caps: LTTPR common capabilities 256762306a36Sopenharmony_ci * 256862306a36Sopenharmony_ci * Returns the maximum link rate supported by all detected LTTPRs. 256962306a36Sopenharmony_ci */ 257062306a36Sopenharmony_ciint drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]) 257162306a36Sopenharmony_ci{ 257262306a36Sopenharmony_ci u8 rate = dp_lttpr_common_cap(caps, DP_MAX_LINK_RATE_PHY_REPEATER); 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci return drm_dp_bw_code_to_link_rate(rate); 257562306a36Sopenharmony_ci} 257662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_max_link_rate); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci/** 257962306a36Sopenharmony_ci * drm_dp_lttpr_max_lane_count - get the maximum lane count supported by all LTTPRs 258062306a36Sopenharmony_ci * @caps: LTTPR common capabilities 258162306a36Sopenharmony_ci * 258262306a36Sopenharmony_ci * Returns the maximum lane count supported by all detected LTTPRs. 258362306a36Sopenharmony_ci */ 258462306a36Sopenharmony_ciint drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]) 258562306a36Sopenharmony_ci{ 258662306a36Sopenharmony_ci u8 max_lanes = dp_lttpr_common_cap(caps, DP_MAX_LANE_COUNT_PHY_REPEATER); 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci return max_lanes & DP_MAX_LANE_COUNT_MASK; 258962306a36Sopenharmony_ci} 259062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_max_lane_count); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci/** 259362306a36Sopenharmony_ci * drm_dp_lttpr_voltage_swing_level_3_supported - check for LTTPR vswing3 support 259462306a36Sopenharmony_ci * @caps: LTTPR PHY capabilities 259562306a36Sopenharmony_ci * 259662306a36Sopenharmony_ci * Returns true if the @caps for an LTTPR TX PHY indicate support for 259762306a36Sopenharmony_ci * voltage swing level 3. 259862306a36Sopenharmony_ci */ 259962306a36Sopenharmony_cibool 260062306a36Sopenharmony_cidrm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]) 260162306a36Sopenharmony_ci{ 260262306a36Sopenharmony_ci u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1); 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci return txcap & DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED; 260562306a36Sopenharmony_ci} 260662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_voltage_swing_level_3_supported); 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci/** 260962306a36Sopenharmony_ci * drm_dp_lttpr_pre_emphasis_level_3_supported - check for LTTPR preemph3 support 261062306a36Sopenharmony_ci * @caps: LTTPR PHY capabilities 261162306a36Sopenharmony_ci * 261262306a36Sopenharmony_ci * Returns true if the @caps for an LTTPR TX PHY indicate support for 261362306a36Sopenharmony_ci * pre-emphasis level 3. 261462306a36Sopenharmony_ci */ 261562306a36Sopenharmony_cibool 261662306a36Sopenharmony_cidrm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]) 261762306a36Sopenharmony_ci{ 261862306a36Sopenharmony_ci u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1); 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci return txcap & DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED; 262162306a36Sopenharmony_ci} 262262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_lttpr_pre_emphasis_level_3_supported); 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci/** 262562306a36Sopenharmony_ci * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink. 262662306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 262762306a36Sopenharmony_ci * @data: DP phy compliance test parameters. 262862306a36Sopenharmony_ci * 262962306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 263062306a36Sopenharmony_ci */ 263162306a36Sopenharmony_ciint drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux, 263262306a36Sopenharmony_ci struct drm_dp_phy_test_params *data) 263362306a36Sopenharmony_ci{ 263462306a36Sopenharmony_ci int err; 263562306a36Sopenharmony_ci u8 rate, lanes; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci err = drm_dp_dpcd_readb(aux, DP_TEST_LINK_RATE, &rate); 263862306a36Sopenharmony_ci if (err < 0) 263962306a36Sopenharmony_ci return err; 264062306a36Sopenharmony_ci data->link_rate = drm_dp_bw_code_to_link_rate(rate); 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci err = drm_dp_dpcd_readb(aux, DP_TEST_LANE_COUNT, &lanes); 264362306a36Sopenharmony_ci if (err < 0) 264462306a36Sopenharmony_ci return err; 264562306a36Sopenharmony_ci data->num_lanes = lanes & DP_MAX_LANE_COUNT_MASK; 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci if (lanes & DP_ENHANCED_FRAME_CAP) 264862306a36Sopenharmony_ci data->enhanced_frame_cap = true; 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci err = drm_dp_dpcd_readb(aux, DP_PHY_TEST_PATTERN, &data->phy_pattern); 265162306a36Sopenharmony_ci if (err < 0) 265262306a36Sopenharmony_ci return err; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci switch (data->phy_pattern) { 265562306a36Sopenharmony_ci case DP_PHY_TEST_PATTERN_80BIT_CUSTOM: 265662306a36Sopenharmony_ci err = drm_dp_dpcd_read(aux, DP_TEST_80BIT_CUSTOM_PATTERN_7_0, 265762306a36Sopenharmony_ci &data->custom80, sizeof(data->custom80)); 265862306a36Sopenharmony_ci if (err < 0) 265962306a36Sopenharmony_ci return err; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci break; 266262306a36Sopenharmony_ci case DP_PHY_TEST_PATTERN_CP2520: 266362306a36Sopenharmony_ci err = drm_dp_dpcd_read(aux, DP_TEST_HBR2_SCRAMBLER_RESET, 266462306a36Sopenharmony_ci &data->hbr2_reset, 266562306a36Sopenharmony_ci sizeof(data->hbr2_reset)); 266662306a36Sopenharmony_ci if (err < 0) 266762306a36Sopenharmony_ci return err; 266862306a36Sopenharmony_ci } 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci return 0; 267162306a36Sopenharmony_ci} 267262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_get_phy_test_pattern); 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci/** 267562306a36Sopenharmony_ci * drm_dp_set_phy_test_pattern() - set the pattern to the sink. 267662306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 267762306a36Sopenharmony_ci * @data: DP phy compliance test parameters. 267862306a36Sopenharmony_ci * @dp_rev: DP revision to use for compliance testing 267962306a36Sopenharmony_ci * 268062306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 268162306a36Sopenharmony_ci */ 268262306a36Sopenharmony_ciint drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux, 268362306a36Sopenharmony_ci struct drm_dp_phy_test_params *data, u8 dp_rev) 268462306a36Sopenharmony_ci{ 268562306a36Sopenharmony_ci int err, i; 268662306a36Sopenharmony_ci u8 test_pattern; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci test_pattern = data->phy_pattern; 268962306a36Sopenharmony_ci if (dp_rev < 0x12) { 269062306a36Sopenharmony_ci test_pattern = (test_pattern << 2) & 269162306a36Sopenharmony_ci DP_LINK_QUAL_PATTERN_11_MASK; 269262306a36Sopenharmony_ci err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, 269362306a36Sopenharmony_ci test_pattern); 269462306a36Sopenharmony_ci if (err < 0) 269562306a36Sopenharmony_ci return err; 269662306a36Sopenharmony_ci } else { 269762306a36Sopenharmony_ci for (i = 0; i < data->num_lanes; i++) { 269862306a36Sopenharmony_ci err = drm_dp_dpcd_writeb(aux, 269962306a36Sopenharmony_ci DP_LINK_QUAL_LANE0_SET + i, 270062306a36Sopenharmony_ci test_pattern); 270162306a36Sopenharmony_ci if (err < 0) 270262306a36Sopenharmony_ci return err; 270362306a36Sopenharmony_ci } 270462306a36Sopenharmony_ci } 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci return 0; 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_set_phy_test_pattern); 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_cistatic const char *dp_pixelformat_get_name(enum dp_pixelformat pixelformat) 271162306a36Sopenharmony_ci{ 271262306a36Sopenharmony_ci if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED) 271362306a36Sopenharmony_ci return "Invalid"; 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci switch (pixelformat) { 271662306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 271762306a36Sopenharmony_ci return "RGB"; 271862306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 271962306a36Sopenharmony_ci return "YUV444"; 272062306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 272162306a36Sopenharmony_ci return "YUV422"; 272262306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 272362306a36Sopenharmony_ci return "YUV420"; 272462306a36Sopenharmony_ci case DP_PIXELFORMAT_Y_ONLY: 272562306a36Sopenharmony_ci return "Y_ONLY"; 272662306a36Sopenharmony_ci case DP_PIXELFORMAT_RAW: 272762306a36Sopenharmony_ci return "RAW"; 272862306a36Sopenharmony_ci default: 272962306a36Sopenharmony_ci return "Reserved"; 273062306a36Sopenharmony_ci } 273162306a36Sopenharmony_ci} 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_cistatic const char *dp_colorimetry_get_name(enum dp_pixelformat pixelformat, 273462306a36Sopenharmony_ci enum dp_colorimetry colorimetry) 273562306a36Sopenharmony_ci{ 273662306a36Sopenharmony_ci if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED) 273762306a36Sopenharmony_ci return "Invalid"; 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci switch (colorimetry) { 274062306a36Sopenharmony_ci case DP_COLORIMETRY_DEFAULT: 274162306a36Sopenharmony_ci switch (pixelformat) { 274262306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 274362306a36Sopenharmony_ci return "sRGB"; 274462306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 274562306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 274662306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 274762306a36Sopenharmony_ci return "BT.601"; 274862306a36Sopenharmony_ci case DP_PIXELFORMAT_Y_ONLY: 274962306a36Sopenharmony_ci return "DICOM PS3.14"; 275062306a36Sopenharmony_ci case DP_PIXELFORMAT_RAW: 275162306a36Sopenharmony_ci return "Custom Color Profile"; 275262306a36Sopenharmony_ci default: 275362306a36Sopenharmony_ci return "Reserved"; 275462306a36Sopenharmony_ci } 275562306a36Sopenharmony_ci case DP_COLORIMETRY_RGB_WIDE_FIXED: /* and DP_COLORIMETRY_BT709_YCC */ 275662306a36Sopenharmony_ci switch (pixelformat) { 275762306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 275862306a36Sopenharmony_ci return "Wide Fixed"; 275962306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 276062306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 276162306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 276262306a36Sopenharmony_ci return "BT.709"; 276362306a36Sopenharmony_ci default: 276462306a36Sopenharmony_ci return "Reserved"; 276562306a36Sopenharmony_ci } 276662306a36Sopenharmony_ci case DP_COLORIMETRY_RGB_WIDE_FLOAT: /* and DP_COLORIMETRY_XVYCC_601 */ 276762306a36Sopenharmony_ci switch (pixelformat) { 276862306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 276962306a36Sopenharmony_ci return "Wide Float"; 277062306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 277162306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 277262306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 277362306a36Sopenharmony_ci return "xvYCC 601"; 277462306a36Sopenharmony_ci default: 277562306a36Sopenharmony_ci return "Reserved"; 277662306a36Sopenharmony_ci } 277762306a36Sopenharmony_ci case DP_COLORIMETRY_OPRGB: /* and DP_COLORIMETRY_XVYCC_709 */ 277862306a36Sopenharmony_ci switch (pixelformat) { 277962306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 278062306a36Sopenharmony_ci return "OpRGB"; 278162306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 278262306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 278362306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 278462306a36Sopenharmony_ci return "xvYCC 709"; 278562306a36Sopenharmony_ci default: 278662306a36Sopenharmony_ci return "Reserved"; 278762306a36Sopenharmony_ci } 278862306a36Sopenharmony_ci case DP_COLORIMETRY_DCI_P3_RGB: /* and DP_COLORIMETRY_SYCC_601 */ 278962306a36Sopenharmony_ci switch (pixelformat) { 279062306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 279162306a36Sopenharmony_ci return "DCI-P3"; 279262306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 279362306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 279462306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 279562306a36Sopenharmony_ci return "sYCC 601"; 279662306a36Sopenharmony_ci default: 279762306a36Sopenharmony_ci return "Reserved"; 279862306a36Sopenharmony_ci } 279962306a36Sopenharmony_ci case DP_COLORIMETRY_RGB_CUSTOM: /* and DP_COLORIMETRY_OPYCC_601 */ 280062306a36Sopenharmony_ci switch (pixelformat) { 280162306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 280262306a36Sopenharmony_ci return "Custom Profile"; 280362306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 280462306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 280562306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 280662306a36Sopenharmony_ci return "OpYCC 601"; 280762306a36Sopenharmony_ci default: 280862306a36Sopenharmony_ci return "Reserved"; 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci case DP_COLORIMETRY_BT2020_RGB: /* and DP_COLORIMETRY_BT2020_CYCC */ 281162306a36Sopenharmony_ci switch (pixelformat) { 281262306a36Sopenharmony_ci case DP_PIXELFORMAT_RGB: 281362306a36Sopenharmony_ci return "BT.2020 RGB"; 281462306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 281562306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 281662306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 281762306a36Sopenharmony_ci return "BT.2020 CYCC"; 281862306a36Sopenharmony_ci default: 281962306a36Sopenharmony_ci return "Reserved"; 282062306a36Sopenharmony_ci } 282162306a36Sopenharmony_ci case DP_COLORIMETRY_BT2020_YCC: 282262306a36Sopenharmony_ci switch (pixelformat) { 282362306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV444: 282462306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV422: 282562306a36Sopenharmony_ci case DP_PIXELFORMAT_YUV420: 282662306a36Sopenharmony_ci return "BT.2020 YCC"; 282762306a36Sopenharmony_ci default: 282862306a36Sopenharmony_ci return "Reserved"; 282962306a36Sopenharmony_ci } 283062306a36Sopenharmony_ci default: 283162306a36Sopenharmony_ci return "Invalid"; 283262306a36Sopenharmony_ci } 283362306a36Sopenharmony_ci} 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_cistatic const char *dp_dynamic_range_get_name(enum dp_dynamic_range dynamic_range) 283662306a36Sopenharmony_ci{ 283762306a36Sopenharmony_ci switch (dynamic_range) { 283862306a36Sopenharmony_ci case DP_DYNAMIC_RANGE_VESA: 283962306a36Sopenharmony_ci return "VESA range"; 284062306a36Sopenharmony_ci case DP_DYNAMIC_RANGE_CTA: 284162306a36Sopenharmony_ci return "CTA range"; 284262306a36Sopenharmony_ci default: 284362306a36Sopenharmony_ci return "Invalid"; 284462306a36Sopenharmony_ci } 284562306a36Sopenharmony_ci} 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_cistatic const char *dp_content_type_get_name(enum dp_content_type content_type) 284862306a36Sopenharmony_ci{ 284962306a36Sopenharmony_ci switch (content_type) { 285062306a36Sopenharmony_ci case DP_CONTENT_TYPE_NOT_DEFINED: 285162306a36Sopenharmony_ci return "Not defined"; 285262306a36Sopenharmony_ci case DP_CONTENT_TYPE_GRAPHICS: 285362306a36Sopenharmony_ci return "Graphics"; 285462306a36Sopenharmony_ci case DP_CONTENT_TYPE_PHOTO: 285562306a36Sopenharmony_ci return "Photo"; 285662306a36Sopenharmony_ci case DP_CONTENT_TYPE_VIDEO: 285762306a36Sopenharmony_ci return "Video"; 285862306a36Sopenharmony_ci case DP_CONTENT_TYPE_GAME: 285962306a36Sopenharmony_ci return "Game"; 286062306a36Sopenharmony_ci default: 286162306a36Sopenharmony_ci return "Reserved"; 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci} 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_civoid drm_dp_vsc_sdp_log(const char *level, struct device *dev, 286662306a36Sopenharmony_ci const struct drm_dp_vsc_sdp *vsc) 286762306a36Sopenharmony_ci{ 286862306a36Sopenharmony_ci#define DP_SDP_LOG(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) 286962306a36Sopenharmony_ci DP_SDP_LOG("DP SDP: %s, revision %u, length %u\n", "VSC", 287062306a36Sopenharmony_ci vsc->revision, vsc->length); 287162306a36Sopenharmony_ci DP_SDP_LOG(" pixelformat: %s\n", 287262306a36Sopenharmony_ci dp_pixelformat_get_name(vsc->pixelformat)); 287362306a36Sopenharmony_ci DP_SDP_LOG(" colorimetry: %s\n", 287462306a36Sopenharmony_ci dp_colorimetry_get_name(vsc->pixelformat, vsc->colorimetry)); 287562306a36Sopenharmony_ci DP_SDP_LOG(" bpc: %u\n", vsc->bpc); 287662306a36Sopenharmony_ci DP_SDP_LOG(" dynamic range: %s\n", 287762306a36Sopenharmony_ci dp_dynamic_range_get_name(vsc->dynamic_range)); 287862306a36Sopenharmony_ci DP_SDP_LOG(" content type: %s\n", 287962306a36Sopenharmony_ci dp_content_type_get_name(vsc->content_type)); 288062306a36Sopenharmony_ci#undef DP_SDP_LOG 288162306a36Sopenharmony_ci} 288262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_vsc_sdp_log); 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci/** 288562306a36Sopenharmony_ci * drm_dp_get_pcon_max_frl_bw() - maximum frl supported by PCON 288662306a36Sopenharmony_ci * @dpcd: DisplayPort configuration data 288762306a36Sopenharmony_ci * @port_cap: port capabilities 288862306a36Sopenharmony_ci * 288962306a36Sopenharmony_ci * Returns maximum frl bandwidth supported by PCON in GBPS, 289062306a36Sopenharmony_ci * returns 0 if not supported. 289162306a36Sopenharmony_ci */ 289262306a36Sopenharmony_ciint drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE], 289362306a36Sopenharmony_ci const u8 port_cap[4]) 289462306a36Sopenharmony_ci{ 289562306a36Sopenharmony_ci int bw; 289662306a36Sopenharmony_ci u8 buf; 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci buf = port_cap[2]; 289962306a36Sopenharmony_ci bw = buf & DP_PCON_MAX_FRL_BW; 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci switch (bw) { 290262306a36Sopenharmony_ci case DP_PCON_MAX_9GBPS: 290362306a36Sopenharmony_ci return 9; 290462306a36Sopenharmony_ci case DP_PCON_MAX_18GBPS: 290562306a36Sopenharmony_ci return 18; 290662306a36Sopenharmony_ci case DP_PCON_MAX_24GBPS: 290762306a36Sopenharmony_ci return 24; 290862306a36Sopenharmony_ci case DP_PCON_MAX_32GBPS: 290962306a36Sopenharmony_ci return 32; 291062306a36Sopenharmony_ci case DP_PCON_MAX_40GBPS: 291162306a36Sopenharmony_ci return 40; 291262306a36Sopenharmony_ci case DP_PCON_MAX_48GBPS: 291362306a36Sopenharmony_ci return 48; 291462306a36Sopenharmony_ci case DP_PCON_MAX_0GBPS: 291562306a36Sopenharmony_ci default: 291662306a36Sopenharmony_ci return 0; 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci return 0; 292062306a36Sopenharmony_ci} 292162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_get_pcon_max_frl_bw); 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci/** 292462306a36Sopenharmony_ci * drm_dp_pcon_frl_prepare() - Prepare PCON for FRL. 292562306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 292662306a36Sopenharmony_ci * @enable_frl_ready_hpd: Configure DP_PCON_ENABLE_HPD_READY. 292762306a36Sopenharmony_ci * 292862306a36Sopenharmony_ci * Returns 0 if success, else returns negative error code. 292962306a36Sopenharmony_ci */ 293062306a36Sopenharmony_ciint drm_dp_pcon_frl_prepare(struct drm_dp_aux *aux, bool enable_frl_ready_hpd) 293162306a36Sopenharmony_ci{ 293262306a36Sopenharmony_ci int ret; 293362306a36Sopenharmony_ci u8 buf = DP_PCON_ENABLE_SOURCE_CTL_MODE | 293462306a36Sopenharmony_ci DP_PCON_ENABLE_LINK_FRL_MODE; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci if (enable_frl_ready_hpd) 293762306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_HPD_READY; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf); 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci return ret; 294262306a36Sopenharmony_ci} 294362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_frl_prepare); 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci/** 294662306a36Sopenharmony_ci * drm_dp_pcon_is_frl_ready() - Is PCON ready for FRL 294762306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 294862306a36Sopenharmony_ci * 294962306a36Sopenharmony_ci * Returns true if success, else returns false. 295062306a36Sopenharmony_ci */ 295162306a36Sopenharmony_cibool drm_dp_pcon_is_frl_ready(struct drm_dp_aux *aux) 295262306a36Sopenharmony_ci{ 295362306a36Sopenharmony_ci int ret; 295462306a36Sopenharmony_ci u8 buf; 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_TX_LINK_STATUS, &buf); 295762306a36Sopenharmony_ci if (ret < 0) 295862306a36Sopenharmony_ci return false; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci if (buf & DP_PCON_FRL_READY) 296162306a36Sopenharmony_ci return true; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci return false; 296462306a36Sopenharmony_ci} 296562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_is_frl_ready); 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci/** 296862306a36Sopenharmony_ci * drm_dp_pcon_frl_configure_1() - Set HDMI LINK Configuration-Step1 296962306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 297062306a36Sopenharmony_ci * @max_frl_gbps: maximum frl bw to be configured between PCON and HDMI sink 297162306a36Sopenharmony_ci * @frl_mode: FRL Training mode, it can be either Concurrent or Sequential. 297262306a36Sopenharmony_ci * In Concurrent Mode, the FRL link bring up can be done along with 297362306a36Sopenharmony_ci * DP Link training. In Sequential mode, the FRL link bring up is done prior to 297462306a36Sopenharmony_ci * the DP Link training. 297562306a36Sopenharmony_ci * 297662306a36Sopenharmony_ci * Returns 0 if success, else returns negative error code. 297762306a36Sopenharmony_ci */ 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ciint drm_dp_pcon_frl_configure_1(struct drm_dp_aux *aux, int max_frl_gbps, 298062306a36Sopenharmony_ci u8 frl_mode) 298162306a36Sopenharmony_ci{ 298262306a36Sopenharmony_ci int ret; 298362306a36Sopenharmony_ci u8 buf; 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_LINK_CONFIG_1, &buf); 298662306a36Sopenharmony_ci if (ret < 0) 298762306a36Sopenharmony_ci return ret; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci if (frl_mode == DP_PCON_ENABLE_CONCURRENT_LINK) 299062306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_CONCURRENT_LINK; 299162306a36Sopenharmony_ci else 299262306a36Sopenharmony_ci buf &= ~DP_PCON_ENABLE_CONCURRENT_LINK; 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci switch (max_frl_gbps) { 299562306a36Sopenharmony_ci case 9: 299662306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_9GBPS; 299762306a36Sopenharmony_ci break; 299862306a36Sopenharmony_ci case 18: 299962306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_18GBPS; 300062306a36Sopenharmony_ci break; 300162306a36Sopenharmony_ci case 24: 300262306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_24GBPS; 300362306a36Sopenharmony_ci break; 300462306a36Sopenharmony_ci case 32: 300562306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_32GBPS; 300662306a36Sopenharmony_ci break; 300762306a36Sopenharmony_ci case 40: 300862306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_40GBPS; 300962306a36Sopenharmony_ci break; 301062306a36Sopenharmony_ci case 48: 301162306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_48GBPS; 301262306a36Sopenharmony_ci break; 301362306a36Sopenharmony_ci case 0: 301462306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_MAX_BW_0GBPS; 301562306a36Sopenharmony_ci break; 301662306a36Sopenharmony_ci default: 301762306a36Sopenharmony_ci return -EINVAL; 301862306a36Sopenharmony_ci } 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf); 302162306a36Sopenharmony_ci if (ret < 0) 302262306a36Sopenharmony_ci return ret; 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci return 0; 302562306a36Sopenharmony_ci} 302662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_frl_configure_1); 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci/** 302962306a36Sopenharmony_ci * drm_dp_pcon_frl_configure_2() - Set HDMI Link configuration Step-2 303062306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 303162306a36Sopenharmony_ci * @max_frl_mask : Max FRL BW to be tried by the PCON with HDMI Sink 303262306a36Sopenharmony_ci * @frl_type : FRL training type, can be Extended, or Normal. 303362306a36Sopenharmony_ci * In Normal FRL training, the PCON tries each frl bw from the max_frl_mask 303462306a36Sopenharmony_ci * starting from min, and stops when link training is successful. In Extended 303562306a36Sopenharmony_ci * FRL training, all frl bw selected in the mask are trained by the PCON. 303662306a36Sopenharmony_ci * 303762306a36Sopenharmony_ci * Returns 0 if success, else returns negative error code. 303862306a36Sopenharmony_ci */ 303962306a36Sopenharmony_ciint drm_dp_pcon_frl_configure_2(struct drm_dp_aux *aux, int max_frl_mask, 304062306a36Sopenharmony_ci u8 frl_type) 304162306a36Sopenharmony_ci{ 304262306a36Sopenharmony_ci int ret; 304362306a36Sopenharmony_ci u8 buf = max_frl_mask; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci if (frl_type == DP_PCON_FRL_LINK_TRAIN_EXTENDED) 304662306a36Sopenharmony_ci buf |= DP_PCON_FRL_LINK_TRAIN_EXTENDED; 304762306a36Sopenharmony_ci else 304862306a36Sopenharmony_ci buf &= ~DP_PCON_FRL_LINK_TRAIN_EXTENDED; 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_2, buf); 305162306a36Sopenharmony_ci if (ret < 0) 305262306a36Sopenharmony_ci return ret; 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci return 0; 305562306a36Sopenharmony_ci} 305662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_frl_configure_2); 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci/** 305962306a36Sopenharmony_ci * drm_dp_pcon_reset_frl_config() - Re-Set HDMI Link configuration. 306062306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 306162306a36Sopenharmony_ci * 306262306a36Sopenharmony_ci * Returns 0 if success, else returns negative error code. 306362306a36Sopenharmony_ci */ 306462306a36Sopenharmony_ciint drm_dp_pcon_reset_frl_config(struct drm_dp_aux *aux) 306562306a36Sopenharmony_ci{ 306662306a36Sopenharmony_ci int ret; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, 0x0); 306962306a36Sopenharmony_ci if (ret < 0) 307062306a36Sopenharmony_ci return ret; 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci return 0; 307362306a36Sopenharmony_ci} 307462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_reset_frl_config); 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci/** 307762306a36Sopenharmony_ci * drm_dp_pcon_frl_enable() - Enable HDMI link through FRL 307862306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 307962306a36Sopenharmony_ci * 308062306a36Sopenharmony_ci * Returns 0 if success, else returns negative error code. 308162306a36Sopenharmony_ci */ 308262306a36Sopenharmony_ciint drm_dp_pcon_frl_enable(struct drm_dp_aux *aux) 308362306a36Sopenharmony_ci{ 308462306a36Sopenharmony_ci int ret; 308562306a36Sopenharmony_ci u8 buf = 0; 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_LINK_CONFIG_1, &buf); 308862306a36Sopenharmony_ci if (ret < 0) 308962306a36Sopenharmony_ci return ret; 309062306a36Sopenharmony_ci if (!(buf & DP_PCON_ENABLE_SOURCE_CTL_MODE)) { 309162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: PCON in Autonomous mode, can't enable FRL\n", 309262306a36Sopenharmony_ci aux->name); 309362306a36Sopenharmony_ci return -EINVAL; 309462306a36Sopenharmony_ci } 309562306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_HDMI_LINK; 309662306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf); 309762306a36Sopenharmony_ci if (ret < 0) 309862306a36Sopenharmony_ci return ret; 309962306a36Sopenharmony_ci 310062306a36Sopenharmony_ci return 0; 310162306a36Sopenharmony_ci} 310262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_frl_enable); 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci/** 310562306a36Sopenharmony_ci * drm_dp_pcon_hdmi_link_active() - check if the PCON HDMI LINK status is active. 310662306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 310762306a36Sopenharmony_ci * 310862306a36Sopenharmony_ci * Returns true if link is active else returns false. 310962306a36Sopenharmony_ci */ 311062306a36Sopenharmony_cibool drm_dp_pcon_hdmi_link_active(struct drm_dp_aux *aux) 311162306a36Sopenharmony_ci{ 311262306a36Sopenharmony_ci u8 buf; 311362306a36Sopenharmony_ci int ret; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_TX_LINK_STATUS, &buf); 311662306a36Sopenharmony_ci if (ret < 0) 311762306a36Sopenharmony_ci return false; 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci return buf & DP_PCON_HDMI_TX_LINK_ACTIVE; 312062306a36Sopenharmony_ci} 312162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_hdmi_link_active); 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci/** 312462306a36Sopenharmony_ci * drm_dp_pcon_hdmi_link_mode() - get the PCON HDMI LINK MODE 312562306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 312662306a36Sopenharmony_ci * @frl_trained_mask: pointer to store bitmask of the trained bw configuration. 312762306a36Sopenharmony_ci * Valid only if the MODE returned is FRL. For Normal Link training mode 312862306a36Sopenharmony_ci * only 1 of the bits will be set, but in case of Extended mode, more than 312962306a36Sopenharmony_ci * one bits can be set. 313062306a36Sopenharmony_ci * 313162306a36Sopenharmony_ci * Returns the link mode : TMDS or FRL on success, else returns negative error 313262306a36Sopenharmony_ci * code. 313362306a36Sopenharmony_ci */ 313462306a36Sopenharmony_ciint drm_dp_pcon_hdmi_link_mode(struct drm_dp_aux *aux, u8 *frl_trained_mask) 313562306a36Sopenharmony_ci{ 313662306a36Sopenharmony_ci u8 buf; 313762306a36Sopenharmony_ci int mode; 313862306a36Sopenharmony_ci int ret; 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_POST_FRL_STATUS, &buf); 314162306a36Sopenharmony_ci if (ret < 0) 314262306a36Sopenharmony_ci return ret; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci mode = buf & DP_PCON_HDMI_LINK_MODE; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci if (frl_trained_mask && DP_PCON_HDMI_MODE_FRL == mode) 314762306a36Sopenharmony_ci *frl_trained_mask = (buf & DP_PCON_HDMI_FRL_TRAINED_BW) >> 1; 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci return mode; 315062306a36Sopenharmony_ci} 315162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_hdmi_link_mode); 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci/** 315462306a36Sopenharmony_ci * drm_dp_pcon_hdmi_frl_link_error_count() - print the error count per lane 315562306a36Sopenharmony_ci * during link failure between PCON and HDMI sink 315662306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 315762306a36Sopenharmony_ci * @connector: DRM connector 315862306a36Sopenharmony_ci * code. 315962306a36Sopenharmony_ci **/ 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_civoid drm_dp_pcon_hdmi_frl_link_error_count(struct drm_dp_aux *aux, 316262306a36Sopenharmony_ci struct drm_connector *connector) 316362306a36Sopenharmony_ci{ 316462306a36Sopenharmony_ci u8 buf, error_count; 316562306a36Sopenharmony_ci int i, num_error; 316662306a36Sopenharmony_ci struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci for (i = 0; i < hdmi->max_lanes; i++) { 316962306a36Sopenharmony_ci if (drm_dp_dpcd_readb(aux, DP_PCON_HDMI_ERROR_STATUS_LN0 + i, &buf) < 0) 317062306a36Sopenharmony_ci return; 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci error_count = buf & DP_PCON_HDMI_ERROR_COUNT_MASK; 317362306a36Sopenharmony_ci switch (error_count) { 317462306a36Sopenharmony_ci case DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS: 317562306a36Sopenharmony_ci num_error = 100; 317662306a36Sopenharmony_ci break; 317762306a36Sopenharmony_ci case DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS: 317862306a36Sopenharmony_ci num_error = 10; 317962306a36Sopenharmony_ci break; 318062306a36Sopenharmony_ci case DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS: 318162306a36Sopenharmony_ci num_error = 3; 318262306a36Sopenharmony_ci break; 318362306a36Sopenharmony_ci default: 318462306a36Sopenharmony_ci num_error = 0; 318562306a36Sopenharmony_ci } 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: More than %d errors since the last read for lane %d", 318862306a36Sopenharmony_ci aux->name, num_error, i); 318962306a36Sopenharmony_ci } 319062306a36Sopenharmony_ci} 319162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_hdmi_frl_link_error_count); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci/* 319462306a36Sopenharmony_ci * drm_dp_pcon_enc_is_dsc_1_2 - Does PCON Encoder supports DSC 1.2 319562306a36Sopenharmony_ci * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder 319662306a36Sopenharmony_ci * 319762306a36Sopenharmony_ci * Returns true is PCON encoder is DSC 1.2 else returns false. 319862306a36Sopenharmony_ci */ 319962306a36Sopenharmony_cibool drm_dp_pcon_enc_is_dsc_1_2(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]) 320062306a36Sopenharmony_ci{ 320162306a36Sopenharmony_ci u8 buf; 320262306a36Sopenharmony_ci u8 major_v, minor_v; 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci buf = pcon_dsc_dpcd[DP_PCON_DSC_VERSION - DP_PCON_DSC_ENCODER]; 320562306a36Sopenharmony_ci major_v = (buf & DP_PCON_DSC_MAJOR_MASK) >> DP_PCON_DSC_MAJOR_SHIFT; 320662306a36Sopenharmony_ci minor_v = (buf & DP_PCON_DSC_MINOR_MASK) >> DP_PCON_DSC_MINOR_SHIFT; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci if (major_v == 1 && minor_v == 2) 320962306a36Sopenharmony_ci return true; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci return false; 321262306a36Sopenharmony_ci} 321362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_enc_is_dsc_1_2); 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci/* 321662306a36Sopenharmony_ci * drm_dp_pcon_dsc_max_slices - Get max slices supported by PCON DSC Encoder 321762306a36Sopenharmony_ci * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder 321862306a36Sopenharmony_ci * 321962306a36Sopenharmony_ci * Returns maximum no. of slices supported by the PCON DSC Encoder. 322062306a36Sopenharmony_ci */ 322162306a36Sopenharmony_ciint drm_dp_pcon_dsc_max_slices(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]) 322262306a36Sopenharmony_ci{ 322362306a36Sopenharmony_ci u8 slice_cap1, slice_cap2; 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci slice_cap1 = pcon_dsc_dpcd[DP_PCON_DSC_SLICE_CAP_1 - DP_PCON_DSC_ENCODER]; 322662306a36Sopenharmony_ci slice_cap2 = pcon_dsc_dpcd[DP_PCON_DSC_SLICE_CAP_2 - DP_PCON_DSC_ENCODER]; 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci if (slice_cap2 & DP_PCON_DSC_24_PER_DSC_ENC) 322962306a36Sopenharmony_ci return 24; 323062306a36Sopenharmony_ci if (slice_cap2 & DP_PCON_DSC_20_PER_DSC_ENC) 323162306a36Sopenharmony_ci return 20; 323262306a36Sopenharmony_ci if (slice_cap2 & DP_PCON_DSC_16_PER_DSC_ENC) 323362306a36Sopenharmony_ci return 16; 323462306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_12_PER_DSC_ENC) 323562306a36Sopenharmony_ci return 12; 323662306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_10_PER_DSC_ENC) 323762306a36Sopenharmony_ci return 10; 323862306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_8_PER_DSC_ENC) 323962306a36Sopenharmony_ci return 8; 324062306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_6_PER_DSC_ENC) 324162306a36Sopenharmony_ci return 6; 324262306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_4_PER_DSC_ENC) 324362306a36Sopenharmony_ci return 4; 324462306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_2_PER_DSC_ENC) 324562306a36Sopenharmony_ci return 2; 324662306a36Sopenharmony_ci if (slice_cap1 & DP_PCON_DSC_1_PER_DSC_ENC) 324762306a36Sopenharmony_ci return 1; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci return 0; 325062306a36Sopenharmony_ci} 325162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_dsc_max_slices); 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci/* 325462306a36Sopenharmony_ci * drm_dp_pcon_dsc_max_slice_width() - Get max slice width for Pcon DSC encoder 325562306a36Sopenharmony_ci * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder 325662306a36Sopenharmony_ci * 325762306a36Sopenharmony_ci * Returns maximum width of the slices in pixel width i.e. no. of pixels x 320. 325862306a36Sopenharmony_ci */ 325962306a36Sopenharmony_ciint drm_dp_pcon_dsc_max_slice_width(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]) 326062306a36Sopenharmony_ci{ 326162306a36Sopenharmony_ci u8 buf; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci buf = pcon_dsc_dpcd[DP_PCON_DSC_MAX_SLICE_WIDTH - DP_PCON_DSC_ENCODER]; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci return buf * DP_DSC_SLICE_WIDTH_MULTIPLIER; 326662306a36Sopenharmony_ci} 326762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_dsc_max_slice_width); 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci/* 327062306a36Sopenharmony_ci * drm_dp_pcon_dsc_bpp_incr() - Get bits per pixel increment for PCON DSC encoder 327162306a36Sopenharmony_ci * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder 327262306a36Sopenharmony_ci * 327362306a36Sopenharmony_ci * Returns the bpp precision supported by the PCON encoder. 327462306a36Sopenharmony_ci */ 327562306a36Sopenharmony_ciint drm_dp_pcon_dsc_bpp_incr(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]) 327662306a36Sopenharmony_ci{ 327762306a36Sopenharmony_ci u8 buf; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci buf = pcon_dsc_dpcd[DP_PCON_DSC_BPP_INCR - DP_PCON_DSC_ENCODER]; 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci switch (buf & DP_PCON_DSC_BPP_INCR_MASK) { 328262306a36Sopenharmony_ci case DP_PCON_DSC_ONE_16TH_BPP: 328362306a36Sopenharmony_ci return 16; 328462306a36Sopenharmony_ci case DP_PCON_DSC_ONE_8TH_BPP: 328562306a36Sopenharmony_ci return 8; 328662306a36Sopenharmony_ci case DP_PCON_DSC_ONE_4TH_BPP: 328762306a36Sopenharmony_ci return 4; 328862306a36Sopenharmony_ci case DP_PCON_DSC_ONE_HALF_BPP: 328962306a36Sopenharmony_ci return 2; 329062306a36Sopenharmony_ci case DP_PCON_DSC_ONE_BPP: 329162306a36Sopenharmony_ci return 1; 329262306a36Sopenharmony_ci } 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci return 0; 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_dsc_bpp_incr); 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_cistatic 329962306a36Sopenharmony_ciint drm_dp_pcon_configure_dsc_enc(struct drm_dp_aux *aux, u8 pps_buf_config) 330062306a36Sopenharmony_ci{ 330162306a36Sopenharmony_ci u8 buf; 330262306a36Sopenharmony_ci int ret; 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, &buf); 330562306a36Sopenharmony_ci if (ret < 0) 330662306a36Sopenharmony_ci return ret; 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci buf |= DP_PCON_ENABLE_DSC_ENCODER; 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci if (pps_buf_config <= DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER) { 331162306a36Sopenharmony_ci buf &= ~DP_PCON_ENCODER_PPS_OVERRIDE_MASK; 331262306a36Sopenharmony_ci buf |= pps_buf_config << 2; 331362306a36Sopenharmony_ci } 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, buf); 331662306a36Sopenharmony_ci if (ret < 0) 331762306a36Sopenharmony_ci return ret; 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_ci return 0; 332062306a36Sopenharmony_ci} 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci/** 332362306a36Sopenharmony_ci * drm_dp_pcon_pps_default() - Let PCON fill the default pps parameters 332462306a36Sopenharmony_ci * for DSC1.2 between PCON & HDMI2.1 sink 332562306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 332662306a36Sopenharmony_ci * 332762306a36Sopenharmony_ci * Returns 0 on success, else returns negative error code. 332862306a36Sopenharmony_ci */ 332962306a36Sopenharmony_ciint drm_dp_pcon_pps_default(struct drm_dp_aux *aux) 333062306a36Sopenharmony_ci{ 333162306a36Sopenharmony_ci int ret; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_DISABLED); 333462306a36Sopenharmony_ci if (ret < 0) 333562306a36Sopenharmony_ci return ret; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci return 0; 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_pps_default); 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci/** 334262306a36Sopenharmony_ci * drm_dp_pcon_pps_override_buf() - Configure PPS encoder override buffer for 334362306a36Sopenharmony_ci * HDMI sink 334462306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 334562306a36Sopenharmony_ci * @pps_buf: 128 bytes to be written into PPS buffer for HDMI sink by PCON. 334662306a36Sopenharmony_ci * 334762306a36Sopenharmony_ci * Returns 0 on success, else returns negative error code. 334862306a36Sopenharmony_ci */ 334962306a36Sopenharmony_ciint drm_dp_pcon_pps_override_buf(struct drm_dp_aux *aux, u8 pps_buf[128]) 335062306a36Sopenharmony_ci{ 335162306a36Sopenharmony_ci int ret; 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVERRIDE_BASE, &pps_buf, 128); 335462306a36Sopenharmony_ci if (ret < 0) 335562306a36Sopenharmony_ci return ret; 335662306a36Sopenharmony_ci 335762306a36Sopenharmony_ci ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER); 335862306a36Sopenharmony_ci if (ret < 0) 335962306a36Sopenharmony_ci return ret; 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci return 0; 336262306a36Sopenharmony_ci} 336362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_pps_override_buf); 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci/* 336662306a36Sopenharmony_ci * drm_dp_pcon_pps_override_param() - Write PPS parameters to DSC encoder 336762306a36Sopenharmony_ci * override registers 336862306a36Sopenharmony_ci * @aux: DisplayPort AUX channel 336962306a36Sopenharmony_ci * @pps_param: 3 Parameters (2 Bytes each) : Slice Width, Slice Height, 337062306a36Sopenharmony_ci * bits_per_pixel. 337162306a36Sopenharmony_ci * 337262306a36Sopenharmony_ci * Returns 0 on success, else returns negative error code. 337362306a36Sopenharmony_ci */ 337462306a36Sopenharmony_ciint drm_dp_pcon_pps_override_param(struct drm_dp_aux *aux, u8 pps_param[6]) 337562306a36Sopenharmony_ci{ 337662306a36Sopenharmony_ci int ret; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT, &pps_param[0], 2); 337962306a36Sopenharmony_ci if (ret < 0) 338062306a36Sopenharmony_ci return ret; 338162306a36Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH, &pps_param[2], 2); 338262306a36Sopenharmony_ci if (ret < 0) 338362306a36Sopenharmony_ci return ret; 338462306a36Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_BPP, &pps_param[4], 2); 338562306a36Sopenharmony_ci if (ret < 0) 338662306a36Sopenharmony_ci return ret; 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER); 338962306a36Sopenharmony_ci if (ret < 0) 339062306a36Sopenharmony_ci return ret; 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci return 0; 339362306a36Sopenharmony_ci} 339462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_pps_override_param); 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci/* 339762306a36Sopenharmony_ci * drm_dp_pcon_convert_rgb_to_ycbcr() - Configure the PCon to convert RGB to Ycbcr 339862306a36Sopenharmony_ci * @aux: displayPort AUX channel 339962306a36Sopenharmony_ci * @color_spc: Color-space/s for which conversion is to be enabled, 0 for disable. 340062306a36Sopenharmony_ci * 340162306a36Sopenharmony_ci * Returns 0 on success, else returns negative error code. 340262306a36Sopenharmony_ci */ 340362306a36Sopenharmony_ciint drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc) 340462306a36Sopenharmony_ci{ 340562306a36Sopenharmony_ci int ret; 340662306a36Sopenharmony_ci u8 buf; 340762306a36Sopenharmony_ci 340862306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, &buf); 340962306a36Sopenharmony_ci if (ret < 0) 341062306a36Sopenharmony_ci return ret; 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci if (color_spc & DP_CONVERSION_RGB_YCBCR_MASK) 341362306a36Sopenharmony_ci buf |= (color_spc & DP_CONVERSION_RGB_YCBCR_MASK); 341462306a36Sopenharmony_ci else 341562306a36Sopenharmony_ci buf &= ~DP_CONVERSION_RGB_YCBCR_MASK; 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, buf); 341862306a36Sopenharmony_ci if (ret < 0) 341962306a36Sopenharmony_ci return ret; 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci return 0; 342262306a36Sopenharmony_ci} 342362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_dp_pcon_convert_rgb_to_ycbcr); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci/** 342662306a36Sopenharmony_ci * drm_edp_backlight_set_level() - Set the backlight level of an eDP panel via AUX 342762306a36Sopenharmony_ci * @aux: The DP AUX channel to use 342862306a36Sopenharmony_ci * @bl: Backlight capability info from drm_edp_backlight_init() 342962306a36Sopenharmony_ci * @level: The brightness level to set 343062306a36Sopenharmony_ci * 343162306a36Sopenharmony_ci * Sets the brightness level of an eDP panel's backlight. Note that the panel's backlight must 343262306a36Sopenharmony_ci * already have been enabled by the driver by calling drm_edp_backlight_enable(). 343362306a36Sopenharmony_ci * 343462306a36Sopenharmony_ci * Returns: %0 on success, negative error code on failure 343562306a36Sopenharmony_ci */ 343662306a36Sopenharmony_ciint drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, 343762306a36Sopenharmony_ci u16 level) 343862306a36Sopenharmony_ci{ 343962306a36Sopenharmony_ci int ret; 344062306a36Sopenharmony_ci u8 buf[2] = { 0 }; 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci /* The panel uses the PWM for controlling brightness levels */ 344362306a36Sopenharmony_ci if (!bl->aux_set) 344462306a36Sopenharmony_ci return 0; 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci if (bl->lsb_reg_used) { 344762306a36Sopenharmony_ci buf[0] = (level & 0xff00) >> 8; 344862306a36Sopenharmony_ci buf[1] = (level & 0x00ff); 344962306a36Sopenharmony_ci } else { 345062306a36Sopenharmony_ci buf[0] = level; 345162306a36Sopenharmony_ci } 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, sizeof(buf)); 345462306a36Sopenharmony_ci if (ret != sizeof(buf)) { 345562306a36Sopenharmony_ci drm_err(aux->drm_dev, 345662306a36Sopenharmony_ci "%s: Failed to write aux backlight level: %d\n", 345762306a36Sopenharmony_ci aux->name, ret); 345862306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 345962306a36Sopenharmony_ci } 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci return 0; 346262306a36Sopenharmony_ci} 346362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edp_backlight_set_level); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_cistatic int 346662306a36Sopenharmony_cidrm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, 346762306a36Sopenharmony_ci bool enable) 346862306a36Sopenharmony_ci{ 346962306a36Sopenharmony_ci int ret; 347062306a36Sopenharmony_ci u8 buf; 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci /* This panel uses the EDP_BL_PWR GPIO for enablement */ 347362306a36Sopenharmony_ci if (!bl->aux_enable) 347462306a36Sopenharmony_ci return 0; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, &buf); 347762306a36Sopenharmony_ci if (ret != 1) { 347862306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: Failed to read eDP display control register: %d\n", 347962306a36Sopenharmony_ci aux->name, ret); 348062306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 348162306a36Sopenharmony_ci } 348262306a36Sopenharmony_ci if (enable) 348362306a36Sopenharmony_ci buf |= DP_EDP_BACKLIGHT_ENABLE; 348462306a36Sopenharmony_ci else 348562306a36Sopenharmony_ci buf &= ~DP_EDP_BACKLIGHT_ENABLE; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, buf); 348862306a36Sopenharmony_ci if (ret != 1) { 348962306a36Sopenharmony_ci drm_err(aux->drm_dev, "%s: Failed to write eDP display control register: %d\n", 349062306a36Sopenharmony_ci aux->name, ret); 349162306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 349262306a36Sopenharmony_ci } 349362306a36Sopenharmony_ci 349462306a36Sopenharmony_ci return 0; 349562306a36Sopenharmony_ci} 349662306a36Sopenharmony_ci 349762306a36Sopenharmony_ci/** 349862306a36Sopenharmony_ci * drm_edp_backlight_enable() - Enable an eDP panel's backlight using DPCD 349962306a36Sopenharmony_ci * @aux: The DP AUX channel to use 350062306a36Sopenharmony_ci * @bl: Backlight capability info from drm_edp_backlight_init() 350162306a36Sopenharmony_ci * @level: The initial backlight level to set via AUX, if there is one 350262306a36Sopenharmony_ci * 350362306a36Sopenharmony_ci * This function handles enabling DPCD backlight controls on a panel over DPCD, while additionally 350462306a36Sopenharmony_ci * restoring any important backlight state such as the given backlight level, the brightness byte 350562306a36Sopenharmony_ci * count, backlight frequency, etc. 350662306a36Sopenharmony_ci * 350762306a36Sopenharmony_ci * Note that certain panels do not support being enabled or disabled via DPCD, but instead require 350862306a36Sopenharmony_ci * that the driver handle enabling/disabling the panel through implementation-specific means using 350962306a36Sopenharmony_ci * the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false, 351062306a36Sopenharmony_ci * this function becomes a no-op, and the driver is expected to handle powering the panel on using 351162306a36Sopenharmony_ci * the EDP_BL_PWR GPIO. 351262306a36Sopenharmony_ci * 351362306a36Sopenharmony_ci * Returns: %0 on success, negative error code on failure. 351462306a36Sopenharmony_ci */ 351562306a36Sopenharmony_ciint drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, 351662306a36Sopenharmony_ci const u16 level) 351762306a36Sopenharmony_ci{ 351862306a36Sopenharmony_ci int ret; 351962306a36Sopenharmony_ci u8 dpcd_buf; 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci if (bl->aux_set) 352262306a36Sopenharmony_ci dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; 352362306a36Sopenharmony_ci else 352462306a36Sopenharmony_ci dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_PWM; 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci if (bl->pwmgen_bit_count) { 352762306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count); 352862306a36Sopenharmony_ci if (ret != 1) 352962306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n", 353062306a36Sopenharmony_ci aux->name, ret); 353162306a36Sopenharmony_ci } 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci if (bl->pwm_freq_pre_divider) { 353462306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_FREQ_SET, bl->pwm_freq_pre_divider); 353562306a36Sopenharmony_ci if (ret != 1) 353662306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 353762306a36Sopenharmony_ci "%s: Failed to write aux backlight frequency: %d\n", 353862306a36Sopenharmony_ci aux->name, ret); 353962306a36Sopenharmony_ci else 354062306a36Sopenharmony_ci dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE; 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, dpcd_buf); 354462306a36Sopenharmony_ci if (ret != 1) { 354562306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux backlight mode: %d\n", 354662306a36Sopenharmony_ci aux->name, ret); 354762306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 354862306a36Sopenharmony_ci } 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci ret = drm_edp_backlight_set_level(aux, bl, level); 355162306a36Sopenharmony_ci if (ret < 0) 355262306a36Sopenharmony_ci return ret; 355362306a36Sopenharmony_ci ret = drm_edp_backlight_set_enable(aux, bl, true); 355462306a36Sopenharmony_ci if (ret < 0) 355562306a36Sopenharmony_ci return ret; 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci return 0; 355862306a36Sopenharmony_ci} 355962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edp_backlight_enable); 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci/** 356262306a36Sopenharmony_ci * drm_edp_backlight_disable() - Disable an eDP backlight using DPCD, if supported 356362306a36Sopenharmony_ci * @aux: The DP AUX channel to use 356462306a36Sopenharmony_ci * @bl: Backlight capability info from drm_edp_backlight_init() 356562306a36Sopenharmony_ci * 356662306a36Sopenharmony_ci * This function handles disabling DPCD backlight controls on a panel over AUX. 356762306a36Sopenharmony_ci * 356862306a36Sopenharmony_ci * Note that certain panels do not support being enabled or disabled via DPCD, but instead require 356962306a36Sopenharmony_ci * that the driver handle enabling/disabling the panel through implementation-specific means using 357062306a36Sopenharmony_ci * the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false, 357162306a36Sopenharmony_ci * this function becomes a no-op, and the driver is expected to handle powering the panel off using 357262306a36Sopenharmony_ci * the EDP_BL_PWR GPIO. 357362306a36Sopenharmony_ci * 357462306a36Sopenharmony_ci * Returns: %0 on success or no-op, negative error code on failure. 357562306a36Sopenharmony_ci */ 357662306a36Sopenharmony_ciint drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl) 357762306a36Sopenharmony_ci{ 357862306a36Sopenharmony_ci int ret; 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci ret = drm_edp_backlight_set_enable(aux, bl, false); 358162306a36Sopenharmony_ci if (ret < 0) 358262306a36Sopenharmony_ci return ret; 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci return 0; 358562306a36Sopenharmony_ci} 358662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edp_backlight_disable); 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_cistatic inline int 358962306a36Sopenharmony_cidrm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, 359062306a36Sopenharmony_ci u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]) 359162306a36Sopenharmony_ci{ 359262306a36Sopenharmony_ci int fxp, fxp_min, fxp_max, fxp_actual, f = 1; 359362306a36Sopenharmony_ci int ret; 359462306a36Sopenharmony_ci u8 pn, pn_min, pn_max; 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci if (!bl->aux_set) 359762306a36Sopenharmony_ci return 0; 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn); 360062306a36Sopenharmony_ci if (ret != 1) { 360162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n", 360262306a36Sopenharmony_ci aux->name, ret); 360362306a36Sopenharmony_ci return -ENODEV; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; 360762306a36Sopenharmony_ci bl->max = (1 << pn) - 1; 360862306a36Sopenharmony_ci if (!driver_pwm_freq_hz) 360962306a36Sopenharmony_ci return 0; 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci /* 361262306a36Sopenharmony_ci * Set PWM Frequency divider to match desired frequency provided by the driver. 361362306a36Sopenharmony_ci * The PWM Frequency is calculated as 27Mhz / (F x P). 361462306a36Sopenharmony_ci * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the 361562306a36Sopenharmony_ci * EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h) 361662306a36Sopenharmony_ci * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the 361762306a36Sopenharmony_ci * EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h) 361862306a36Sopenharmony_ci */ 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_ci /* Find desired value of (F x P) 362162306a36Sopenharmony_ci * Note that, if F x P is out of supported range, the maximum value or minimum value will 362262306a36Sopenharmony_ci * applied automatically. So no need to check that. 362362306a36Sopenharmony_ci */ 362462306a36Sopenharmony_ci fxp = DIV_ROUND_CLOSEST(1000 * DP_EDP_BACKLIGHT_FREQ_BASE_KHZ, driver_pwm_freq_hz); 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_ci /* Use highest possible value of Pn for more granularity of brightness adjustment while 362762306a36Sopenharmony_ci * satisfying the conditions below. 362862306a36Sopenharmony_ci * - Pn is in the range of Pn_min and Pn_max 362962306a36Sopenharmony_ci * - F is in the range of 1 and 255 363062306a36Sopenharmony_ci * - FxP is within 25% of desired value. 363162306a36Sopenharmony_ci * Note: 25% is arbitrary value and may need some tweak. 363262306a36Sopenharmony_ci */ 363362306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); 363462306a36Sopenharmony_ci if (ret != 1) { 363562306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n", 363662306a36Sopenharmony_ci aux->name, ret); 363762306a36Sopenharmony_ci return 0; 363862306a36Sopenharmony_ci } 363962306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); 364062306a36Sopenharmony_ci if (ret != 1) { 364162306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n", 364262306a36Sopenharmony_ci aux->name, ret); 364362306a36Sopenharmony_ci return 0; 364462306a36Sopenharmony_ci } 364562306a36Sopenharmony_ci pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; 364662306a36Sopenharmony_ci pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci /* Ensure frequency is within 25% of desired value */ 364962306a36Sopenharmony_ci fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); 365062306a36Sopenharmony_ci fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); 365162306a36Sopenharmony_ci if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) { 365262306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 365362306a36Sopenharmony_ci "%s: Driver defined backlight frequency (%d) out of range\n", 365462306a36Sopenharmony_ci aux->name, driver_pwm_freq_hz); 365562306a36Sopenharmony_ci return 0; 365662306a36Sopenharmony_ci } 365762306a36Sopenharmony_ci 365862306a36Sopenharmony_ci for (pn = pn_max; pn >= pn_min; pn--) { 365962306a36Sopenharmony_ci f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255); 366062306a36Sopenharmony_ci fxp_actual = f << pn; 366162306a36Sopenharmony_ci if (fxp_min <= fxp_actual && fxp_actual <= fxp_max) 366262306a36Sopenharmony_ci break; 366362306a36Sopenharmony_ci } 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, pn); 366662306a36Sopenharmony_ci if (ret != 1) { 366762306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n", 366862306a36Sopenharmony_ci aux->name, ret); 366962306a36Sopenharmony_ci return 0; 367062306a36Sopenharmony_ci } 367162306a36Sopenharmony_ci bl->pwmgen_bit_count = pn; 367262306a36Sopenharmony_ci bl->max = (1 << pn) - 1; 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci if (edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) { 367562306a36Sopenharmony_ci bl->pwm_freq_pre_divider = f; 367662306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Using backlight frequency from driver (%dHz)\n", 367762306a36Sopenharmony_ci aux->name, driver_pwm_freq_hz); 367862306a36Sopenharmony_ci } 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_ci return 0; 368162306a36Sopenharmony_ci} 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_cistatic inline int 368462306a36Sopenharmony_cidrm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, 368562306a36Sopenharmony_ci u8 *current_mode) 368662306a36Sopenharmony_ci{ 368762306a36Sopenharmony_ci int ret; 368862306a36Sopenharmony_ci u8 buf[2]; 368962306a36Sopenharmony_ci u8 mode_reg; 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg); 369262306a36Sopenharmony_ci if (ret != 1) { 369362306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight mode: %d\n", 369462306a36Sopenharmony_ci aux->name, ret); 369562306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 369662306a36Sopenharmony_ci } 369762306a36Sopenharmony_ci 369862306a36Sopenharmony_ci *current_mode = (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK); 369962306a36Sopenharmony_ci if (!bl->aux_set) 370062306a36Sopenharmony_ci return 0; 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { 370362306a36Sopenharmony_ci int size = 1 + bl->lsb_reg_used; 370462306a36Sopenharmony_ci 370562306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, size); 370662306a36Sopenharmony_ci if (ret != size) { 370762306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight level: %d\n", 370862306a36Sopenharmony_ci aux->name, ret); 370962306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci if (bl->lsb_reg_used) 371362306a36Sopenharmony_ci return (buf[0] << 8) | buf[1]; 371462306a36Sopenharmony_ci else 371562306a36Sopenharmony_ci return buf[0]; 371662306a36Sopenharmony_ci } 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci /* 371962306a36Sopenharmony_ci * If we're not in DPCD control mode yet, the programmed brightness value is meaningless and 372062306a36Sopenharmony_ci * the driver should assume max brightness 372162306a36Sopenharmony_ci */ 372262306a36Sopenharmony_ci return bl->max; 372362306a36Sopenharmony_ci} 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci/** 372662306a36Sopenharmony_ci * drm_edp_backlight_init() - Probe a display panel's TCON using the standard VESA eDP backlight 372762306a36Sopenharmony_ci * interface. 372862306a36Sopenharmony_ci * @aux: The DP aux device to use for probing 372962306a36Sopenharmony_ci * @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight 373062306a36Sopenharmony_ci * @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz 373162306a36Sopenharmony_ci * @edp_dpcd: A cached copy of the eDP DPCD 373262306a36Sopenharmony_ci * @current_level: Where to store the probed brightness level, if any 373362306a36Sopenharmony_ci * @current_mode: Where to store the currently set backlight control mode 373462306a36Sopenharmony_ci * 373562306a36Sopenharmony_ci * Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities, 373662306a36Sopenharmony_ci * along with also probing the current and maximum supported brightness levels. 373762306a36Sopenharmony_ci * 373862306a36Sopenharmony_ci * If @driver_pwm_freq_hz is non-zero, this will be used as the backlight frequency. Otherwise, the 373962306a36Sopenharmony_ci * default frequency from the panel is used. 374062306a36Sopenharmony_ci * 374162306a36Sopenharmony_ci * Returns: %0 on success, negative error code on failure. 374262306a36Sopenharmony_ci */ 374362306a36Sopenharmony_ciint 374462306a36Sopenharmony_cidrm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, 374562306a36Sopenharmony_ci u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE], 374662306a36Sopenharmony_ci u16 *current_level, u8 *current_mode) 374762306a36Sopenharmony_ci{ 374862306a36Sopenharmony_ci int ret; 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci if (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) 375162306a36Sopenharmony_ci bl->aux_enable = true; 375262306a36Sopenharmony_ci if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP) 375362306a36Sopenharmony_ci bl->aux_set = true; 375462306a36Sopenharmony_ci if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) 375562306a36Sopenharmony_ci bl->lsb_reg_used = true; 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci /* Sanity check caps */ 375862306a36Sopenharmony_ci if (!bl->aux_set && !(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) { 375962306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 376062306a36Sopenharmony_ci "%s: Panel supports neither AUX or PWM brightness control? Aborting\n", 376162306a36Sopenharmony_ci aux->name); 376262306a36Sopenharmony_ci return -EINVAL; 376362306a36Sopenharmony_ci } 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd); 376662306a36Sopenharmony_ci if (ret < 0) 376762306a36Sopenharmony_ci return ret; 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci ret = drm_edp_backlight_probe_state(aux, bl, current_mode); 377062306a36Sopenharmony_ci if (ret < 0) 377162306a36Sopenharmony_ci return ret; 377262306a36Sopenharmony_ci *current_level = ret; 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 377562306a36Sopenharmony_ci "%s: Found backlight: aux_set=%d aux_enable=%d mode=%d\n", 377662306a36Sopenharmony_ci aux->name, bl->aux_set, bl->aux_enable, *current_mode); 377762306a36Sopenharmony_ci if (bl->aux_set) { 377862306a36Sopenharmony_ci drm_dbg_kms(aux->drm_dev, 377962306a36Sopenharmony_ci "%s: Backlight caps: level=%d/%d pwm_freq_pre_divider=%d lsb_reg_used=%d\n", 378062306a36Sopenharmony_ci aux->name, *current_level, bl->max, bl->pwm_freq_pre_divider, 378162306a36Sopenharmony_ci bl->lsb_reg_used); 378262306a36Sopenharmony_ci } 378362306a36Sopenharmony_ci 378462306a36Sopenharmony_ci return 0; 378562306a36Sopenharmony_ci} 378662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edp_backlight_init); 378762306a36Sopenharmony_ci 378862306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ 378962306a36Sopenharmony_ci (IS_MODULE(CONFIG_DRM_KMS_HELPER) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE)) 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_cistatic int dp_aux_backlight_update_status(struct backlight_device *bd) 379262306a36Sopenharmony_ci{ 379362306a36Sopenharmony_ci struct dp_aux_backlight *bl = bl_get_data(bd); 379462306a36Sopenharmony_ci u16 brightness = backlight_get_brightness(bd); 379562306a36Sopenharmony_ci int ret = 0; 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ci if (!backlight_is_blank(bd)) { 379862306a36Sopenharmony_ci if (!bl->enabled) { 379962306a36Sopenharmony_ci drm_edp_backlight_enable(bl->aux, &bl->info, brightness); 380062306a36Sopenharmony_ci bl->enabled = true; 380162306a36Sopenharmony_ci return 0; 380262306a36Sopenharmony_ci } 380362306a36Sopenharmony_ci ret = drm_edp_backlight_set_level(bl->aux, &bl->info, brightness); 380462306a36Sopenharmony_ci } else { 380562306a36Sopenharmony_ci if (bl->enabled) { 380662306a36Sopenharmony_ci drm_edp_backlight_disable(bl->aux, &bl->info); 380762306a36Sopenharmony_ci bl->enabled = false; 380862306a36Sopenharmony_ci } 380962306a36Sopenharmony_ci } 381062306a36Sopenharmony_ci 381162306a36Sopenharmony_ci return ret; 381262306a36Sopenharmony_ci} 381362306a36Sopenharmony_ci 381462306a36Sopenharmony_cistatic const struct backlight_ops dp_aux_bl_ops = { 381562306a36Sopenharmony_ci .update_status = dp_aux_backlight_update_status, 381662306a36Sopenharmony_ci}; 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci/** 381962306a36Sopenharmony_ci * drm_panel_dp_aux_backlight - create and use DP AUX backlight 382062306a36Sopenharmony_ci * @panel: DRM panel 382162306a36Sopenharmony_ci * @aux: The DP AUX channel to use 382262306a36Sopenharmony_ci * 382362306a36Sopenharmony_ci * Use this function to create and handle backlight if your panel 382462306a36Sopenharmony_ci * supports backlight control over DP AUX channel using DPCD 382562306a36Sopenharmony_ci * registers as per VESA's standard backlight control interface. 382662306a36Sopenharmony_ci * 382762306a36Sopenharmony_ci * When the panel is enabled backlight will be enabled after a 382862306a36Sopenharmony_ci * successful call to &drm_panel_funcs.enable() 382962306a36Sopenharmony_ci * 383062306a36Sopenharmony_ci * When the panel is disabled backlight will be disabled before the 383162306a36Sopenharmony_ci * call to &drm_panel_funcs.disable(). 383262306a36Sopenharmony_ci * 383362306a36Sopenharmony_ci * A typical implementation for a panel driver supporting backlight 383462306a36Sopenharmony_ci * control over DP AUX will call this function at probe time. 383562306a36Sopenharmony_ci * Backlight will then be handled transparently without requiring 383662306a36Sopenharmony_ci * any intervention from the driver. 383762306a36Sopenharmony_ci * 383862306a36Sopenharmony_ci * drm_panel_dp_aux_backlight() must be called after the call to drm_panel_init(). 383962306a36Sopenharmony_ci * 384062306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure. 384162306a36Sopenharmony_ci */ 384262306a36Sopenharmony_ciint drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux) 384362306a36Sopenharmony_ci{ 384462306a36Sopenharmony_ci struct dp_aux_backlight *bl; 384562306a36Sopenharmony_ci struct backlight_properties props = { 0 }; 384662306a36Sopenharmony_ci u16 current_level; 384762306a36Sopenharmony_ci u8 current_mode; 384862306a36Sopenharmony_ci u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]; 384962306a36Sopenharmony_ci int ret; 385062306a36Sopenharmony_ci 385162306a36Sopenharmony_ci if (!panel || !panel->dev || !aux) 385262306a36Sopenharmony_ci return -EINVAL; 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci ret = drm_dp_dpcd_read(aux, DP_EDP_DPCD_REV, edp_dpcd, 385562306a36Sopenharmony_ci EDP_DISPLAY_CTL_CAP_SIZE); 385662306a36Sopenharmony_ci if (ret < 0) 385762306a36Sopenharmony_ci return ret; 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci if (!drm_edp_backlight_supported(edp_dpcd)) { 386062306a36Sopenharmony_ci DRM_DEV_INFO(panel->dev, "DP AUX backlight is not supported\n"); 386162306a36Sopenharmony_ci return 0; 386262306a36Sopenharmony_ci } 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci bl = devm_kzalloc(panel->dev, sizeof(*bl), GFP_KERNEL); 386562306a36Sopenharmony_ci if (!bl) 386662306a36Sopenharmony_ci return -ENOMEM; 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci bl->aux = aux; 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_ci ret = drm_edp_backlight_init(aux, &bl->info, 0, edp_dpcd, 387162306a36Sopenharmony_ci ¤t_level, ¤t_mode); 387262306a36Sopenharmony_ci if (ret < 0) 387362306a36Sopenharmony_ci return ret; 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci props.type = BACKLIGHT_RAW; 387662306a36Sopenharmony_ci props.brightness = current_level; 387762306a36Sopenharmony_ci props.max_brightness = bl->info.max; 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci bl->base = devm_backlight_device_register(panel->dev, "dp_aux_backlight", 388062306a36Sopenharmony_ci panel->dev, bl, 388162306a36Sopenharmony_ci &dp_aux_bl_ops, &props); 388262306a36Sopenharmony_ci if (IS_ERR(bl->base)) 388362306a36Sopenharmony_ci return PTR_ERR(bl->base); 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_ci backlight_disable(bl->base); 388662306a36Sopenharmony_ci 388762306a36Sopenharmony_ci panel->backlight = bl->base; 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci return 0; 389062306a36Sopenharmony_ci} 389162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_panel_dp_aux_backlight); 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci#endif 3894