static bool _lspcon_write_avi_infoframe_mca(struct drm_dp_aux *aux, const u8 *buffer, ssize_t len) { int ret; u32 val = 0; u32 retry; u16 reg; const u8 *data = buffer; reg = LSPCON_MCA_AVI_IF_WRITE_OFFSET; while (val < len) { /* DPCD write for AVI IF can fail on a slow FW day, so retry */ for (retry = 0; retry < 5; retry++) { ret = drm_dp_dpcd_write(aux, reg, (void *)data, 1); if (ret == 1) { break; } else if (retry < 4) { mdelay(50); continue; } else { DRM_ERROR("DPCD write failed at:0x%x\n", reg); return false; } } val++; reg++; data++; } val = 0; reg = LSPCON_MCA_AVI_IF_CTRL; ret = drm_dp_dpcd_read(aux, reg, &val, 1); if (ret < 0) { DRM_ERROR("DPCD read failed, address 0x%x\n", reg); return false; } /* Indicate LSPCON chip about infoframe, clear bit 1 and set bit 0 */ val &= ~LSPCON_MCA_AVI_IF_HANDLED; val |= LSPCON_MCA_AVI_IF_KICKOFF; ret = drm_dp_dpcd_write(aux, reg, &val, 1); if (ret < 0) { DRM_ERROR("DPCD read failed, address 0x%x\n", reg); return false; } val = 0; ret = drm_dp_dpcd_read(aux, reg, &val, 1); if (ret < 0) { DRM_ERROR("DPCD read failed, address 0x%x\n", reg); return false; } if (val == LSPCON_MCA_AVI_IF_HANDLED) DRM_DEBUG_KMS("AVI IF handled by FW\n"); return true; }
static int edp_lane_set_write(struct edp_ctrl *ctrl, u8 voltage_level, u8 pre_emphasis_level) { int i; u8 buf[4]; if (voltage_level >= DPCD_LINK_VOLTAGE_MAX) voltage_level |= 0x04; if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX) pre_emphasis_level |= 0x04; pre_emphasis_level <<= 3; for (i = 0; i < 4; i++) buf[i] = voltage_level | pre_emphasis_level; DBG("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level); if (drm_dp_dpcd_write(ctrl->drm_aux, 0x103, buf, 4) < 4) { pr_err("%s: Set sw/pe to panel failed\n", __func__); return -ENOLINK; } return 0; }
static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint8_t dp_train_pat) { uint8_t buf[sizeof(intel_dp->train_set) + 1]; int ret, len; intel_dp_program_link_training_pattern(intel_dp, dp_train_pat); buf[0] = dp_train_pat; if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) == DP_TRAINING_PATTERN_DISABLE) { /* don't write DP_TRAINING_LANEx_SET on disable */ len = 1; } else { /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */ memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count); len = intel_dp->lane_count + 1; } ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET, buf, len); return ret == len; }
static bool _lspcon_parade_write_infoframe_blocks(struct drm_dp_aux *aux, u8 *avi_buf) { u8 avi_if_ctrl; u8 block_count = 0; u8 *data; u16 reg; ssize_t ret; while (block_count < 4) { if (!lspcon_parade_fw_ready(aux)) { DRM_DEBUG_KMS("LSPCON FW not ready, block %d\n", block_count); return false; } reg = LSPCON_PARADE_AVI_IF_WRITE_OFFSET; data = avi_buf + block_count * 8; ret = drm_dp_dpcd_write(aux, reg, data, 8); if (ret < 0) { DRM_ERROR("Failed to write AVI IF block %d\n", block_count); return false; } /* * Once a block of data is written, we have to inform the FW * about this by writing into avi infoframe control register: * - set the kickoff bit[7] to 1 * - write the block no. to bits[1:0] */ reg = LSPCON_PARADE_AVI_IF_CTRL; avi_if_ctrl = LSPCON_PARADE_AVI_IF_KICKOFF | block_count; ret = drm_dp_dpcd_write(aux, reg, &avi_if_ctrl, 1); if (ret < 0) { DRM_ERROR("Failed to update (0x%x), block %d\n", reg, block_count); return false; } block_count++; } DRM_DEBUG_KMS("Wrote AVI IF blocks successfully\n"); return true; }
static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info) { /* set the initial vs/emph on the source */ atombios_dig_transmitter_setup(dp_info->encoder, ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, 0, dp_info->train_set[0]); /* sets all lanes at once */ /* set the vs/emph on the sink */ drm_dp_dpcd_write(dp_info->aux, DP_TRAINING_LANE0_SET, dp_info->train_set, dp_info->dp_lane_count); }
static bool intel_dp_update_link_train(struct intel_dp *intel_dp) { int ret; intel_dp_set_signal_levels(intel_dp); ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET, intel_dp->train_set, intel_dp->lane_count); return ret == intel_dp->lane_count; }
static int edp_train_pattern_set_write(struct edp_ctrl *ctrl, u8 pattern) { u8 p = pattern; DBG("pattern=%x", p); if (drm_dp_dpcd_write(ctrl->drm_aux, DP_TRAINING_PATTERN_SET, &p, 1) < 1) { pr_err("%s: Set training pattern to panel failed\n", __func__); return -ENOLINK; } return 0; }
static int drm_dp_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) { struct drm_dp_aux *aux = cec_get_drvdata(adap); /* Bit 15 (logical address 15) should always be set */ u16 la_mask = 1 << CEC_LOG_ADDR_BROADCAST; u8 mask[2]; ssize_t err; if (addr != CEC_LOG_ADDR_INVALID) la_mask |= adap->log_addrs.log_addr_mask | (1 << addr); mask[0] = la_mask & 0xff; mask[1] = la_mask >> 8; err = drm_dp_dpcd_write(aux, DP_CEC_LOGICAL_ADDRESS_MASK, mask, 2); return (addr != CEC_LOG_ADDR_INVALID && err < 0) ? err : 0; }
static int drm_dp_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct drm_dp_aux *aux = cec_get_drvdata(adap); unsigned int retries = min(5, attempts - 1); ssize_t err; err = drm_dp_dpcd_write(aux, DP_CEC_TX_MESSAGE_BUFFER, msg->msg, msg->len); if (err < 0) return err; err = drm_dp_dpcd_writeb(aux, DP_CEC_TX_MESSAGE_INFO, (msg->len - 1) | (retries << 4) | DP_CEC_TX_MESSAGE_SEND); return err < 0 ? err : 0; }
bool dm_helpers_dp_write_dpcd( struct dc_context *ctx, const struct dc_link *link, uint32_t address, const uint8_t *data, uint32_t size) { struct amdgpu_dm_connector *aconnector = link->priv; if (!aconnector) { DRM_ERROR("Failed to found connector for link!"); return false; } return drm_dp_dpcd_write(&aconnector->dm_dp_aux.aux, address, (uint8_t *)data, size) > 0; }
/** * xilinx_drm_dp_update_vs_emph - Update the training values * @dp: DisplayPort IP core structure * * Update the training values based on the request from sink. The mapped values * are predefined, and values(vs, pe, pc) are from the device manual. * * Return: 0 if vs and emph are updated successfully, or the error code returned * by drm_dp_dpcd_write(). */ static int xilinx_drm_dp_update_vs_emph(struct xilinx_drm_dp *dp) { u8 *train_set = dp->train_set; u8 i, v_level, p_level; int ret; static u8 vs[4][4] = { { 0x2a, 0x27, 0x24, 0x20 }, { 0x27, 0x23, 0x20, 0xff }, { 0x24, 0x20, 0xff, 0xff }, { 0xff, 0xff, 0xff, 0xff } }; static u8 pe[4][4] = { { 0x2, 0x2, 0x2, 0x2 }, { 0x1, 0x1, 0x1, 0xff }, { 0x0, 0x0, 0xff, 0xff }, { 0xff, 0xff, 0xff, 0xff } }; ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->mode.lane_cnt); if (ret < 0) return ret; for (i = 0; i < dp->mode.lane_cnt; i++) { v_level = (train_set[i] & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT; p_level = (train_set[i] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT; if (dp->phy[i]) { u32 reg = XILINX_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4; xpsgtr_margining_factor(dp->phy[i], p_level, v_level); xpsgtr_override_deemph(dp->phy[i], p_level, v_level); xilinx_drm_writel(dp->iomem, reg, 0x2); } else { u32 reg; reg = XILINX_DP_TX_PHY_VOLTAGE_DIFF_LANE_0 + i + 4; xilinx_drm_writel(dp->iomem, reg, vs[p_level][v_level]); reg = XILINX_DP_TX_PHY_PRECURSOR_LANE_0 + i + 4; xilinx_drm_writel(dp->iomem, reg, pe[p_level][v_level]); reg = XILINX_DP_TX_PHY_POSTCURSOR_LANE_0 + i + 4; xilinx_drm_writel(dp->iomem, reg, 0); } } return 0; }
/** * xilinx_drm_dp_update_vs_emph - Update the training values * @dp: DisplayPort IP core structure * * Update the training values based on the request from sink. The mapped values * are predefined, and values(vs, pe, pc) are from the reference codes. * * Return: 0 if vs and emph are updated successfully, or the error code returned * by drm_dp_dpcd_write(). */ static int xilinx_drm_dp_update_vs_emph(struct xilinx_drm_dp *dp) { u8 *train_set = dp->train_set; u8 i, level; int ret; u8 vs[4] = { 0x3, 0x7, 0xb, 0xf }; u8 pe[4] = { 0x0, 0x3, 0x5, 0x6 }; u8 pc[4] = { 0x0, 0xe, 0x14, 0x1b }; ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->mode.lane_cnt); if (ret < 0) return ret; for (i = 0; i < dp->mode.lane_cnt; i++) { level = (train_set[i] & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_PHY_VOLTAGE_DIFF_LANE_0 + i * 4, vs[level]); level = (train_set[i] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_PHY_PREEMPHASIS_LANE_0 + i * 4, pe[level]); } for (i = 0; i < dp->mode.lane_cnt; i++) { level = (train_set[i] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_PHY_POSTCURSOR_LANE_0 + i * 4, pc[level]); xilinx_drm_writel(dp->iomem, XILINX_DP_TX_PHY_PRECURSOR_LANE_0 + i * 4, 0); } return 0; }
static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) { int lane, lane_count, retval; u32 reg; u8 link_align, link_status[2], adjust_request[2]; usleep_range(400, 401); lane_count = dp->link_train.lane_count; retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); if (retval < 0) return retval; if (analogix_dp_clock_recovery_ok(link_status, lane_count)) { analogix_dp_reduce_link_rate(dp); return -EIO; } retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, adjust_request, 2); if (retval < 0) return retval; retval = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED, &link_align); if (retval < 0) return retval; analogix_dp_get_adjust_training_lane(dp, adjust_request); if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) { /* traing pattern Set to Normal */ analogix_dp_training_pattern_dis(dp); dev_info(dp->dev, "Link Training success!\n"); analogix_dp_get_link_bandwidth(dp, ®); dp->link_train.link_rate = reg; dev_dbg(dp->dev, "final bandwidth = %.2x\n", dp->link_train.link_rate); analogix_dp_get_lane_count(dp, ®); dp->link_train.lane_count = reg; dev_dbg(dp->dev, "final lane count = %.2x\n", dp->link_train.lane_count); /* set enhanced mode if available */ analogix_dp_set_enhanced_mode(dp); dp->link_train.lt_state = FINISHED; return 0; } /* not all locked */ dp->link_train.eq_loop++; if (dp->link_train.eq_loop > MAX_EQ_LOOP) { dev_err(dp->dev, "EQ Max loop\n"); analogix_dp_reduce_link_rate(dp); return -EIO; } for (lane = 0; lane < lane_count; lane++) analogix_dp_set_lane_link_training(dp, dp->link_train.training_lane[lane], lane); retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link_train.training_lane, lane_count); if (retval < 0) return retval; return 0; }
static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) { int lane, lane_count, retval; u8 voltage_swing, pre_emphasis, training_lane; u8 link_status[2], adjust_request[2]; usleep_range(100, 101); lane_count = dp->link_train.lane_count; retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); if (retval < 0) return retval; retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, adjust_request, 2); if (retval < 0) return retval; if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) { /* set training pattern 2 for EQ */ analogix_dp_set_training_pattern(dp, TRAINING_PTN2); retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2); if (retval < 0) return retval; dev_info(dp->dev, "Link Training Clock Recovery success\n"); dp->link_train.lt_state = EQUALIZER_TRAINING; } else { for (lane = 0; lane < lane_count; lane++) { training_lane = analogix_dp_get_lane_link_training( dp, lane); voltage_swing = analogix_dp_get_adjust_request_voltage( adjust_request, lane); pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis( adjust_request, lane); if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis) dp->link_train.cr_loop[lane]++; if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP || voltage_swing == VOLTAGE_LEVEL_3 || pre_emphasis == PRE_EMPHASIS_LEVEL_3) { dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n", dp->link_train.cr_loop[lane], voltage_swing, pre_emphasis); analogix_dp_reduce_link_rate(dp); return -EIO; } } } analogix_dp_get_adjust_training_lane(dp, adjust_request); for (lane = 0; lane < lane_count; lane++) analogix_dp_set_lane_link_training(dp, dp->link_train.training_lane[lane], lane); retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link_train.training_lane, lane_count); if (retval < 0) return retval; return 0; }
static int analogix_dp_link_start(struct analogix_dp_device *dp) { u8 buf[4]; int lane, lane_count, pll_tries, retval; lane_count = dp->link_train.lane_count; dp->link_train.lt_state = CLOCK_RECOVERY; dp->link_train.eq_loop = 0; for (lane = 0; lane < lane_count; lane++) dp->link_train.cr_loop[lane] = 0; /* Set link rate and count as you want to establish*/ analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate); analogix_dp_set_lane_count(dp, dp->link_train.lane_count); /* Setup RX configuration */ buf[0] = dp->link_train.link_rate; buf[1] = dp->link_train.lane_count; retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2); if (retval < 0) return retval; /* Set TX pre-emphasis to minimum */ for (lane = 0; lane < lane_count; lane++) analogix_dp_set_lane_lane_pre_emphasis(dp, PRE_EMPHASIS_LEVEL_0, lane); /* Wait for PLL lock */ pll_tries = 0; while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { if (pll_tries == DP_TIMEOUT_LOOP_COUNT) { dev_err(dp->dev, "Wait for PLL lock timed out\n"); return -ETIMEDOUT; } pll_tries++; usleep_range(90, 120); } /* Set training pattern 1 */ analogix_dp_set_training_pattern(dp, TRAINING_PTN1); /* Set RX training pattern */ retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1); if (retval < 0) return retval; for (lane = 0; lane < lane_count; lane++) buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 | DP_TRAIN_VOLTAGE_SWING_LEVEL_0; retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, buf, lane_count); if (retval < 0) return retval; return 0; }
/* Enable corresponding port and start training pattern 1 */ static void intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) { int i; uint8_t voltage; int voltage_tries, loop_tries; uint8_t link_config[2]; uint8_t link_bw, rate_select; if (intel_dp->prepare_link_retrain) intel_dp->prepare_link_retrain(intel_dp); intel_dp_compute_rate(intel_dp, intel_dp->link_rate, &link_bw, &rate_select); /* Write the link configuration data */ link_config[0] = link_bw; link_config[1] = intel_dp->lane_count; if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); if (intel_dp->num_sink_rates) drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET, &rate_select, 1); link_config[0] = 0; link_config[1] = DP_SET_ANSI_8B10B; drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2); intel_dp->DP |= DP_PORT_EN; /* clock recovery */ if (!intel_dp_reset_link_train(intel_dp, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE)) { DRM_ERROR("failed to enable link training\n"); return; } voltage = 0xff; voltage_tries = 0; loop_tries = 0; for (;;) { uint8_t link_status[DP_LINK_STATUS_SIZE]; drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd); if (!intel_dp_get_link_status(intel_dp, link_status)) { DRM_ERROR("failed to get link status\n"); break; } if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); break; } /* Check to see if we've tried the max voltage */ for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; if (i == intel_dp->lane_count) { ++loop_tries; if (loop_tries == 5) { DRM_ERROR("too many full retries, give up\n"); intel_dp_dump_link_status(link_status); break; } intel_dp_reset_link_train(intel_dp, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE); voltage_tries = 0; continue; } /* Check to see if we've tried the same voltage 5 times */ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { ++voltage_tries; if (voltage_tries == 5) { DRM_ERROR("too many voltage retries, give up\n"); break; } } else voltage_tries = 0; voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Update training set as requested by target */ intel_get_adjust_train(intel_dp, link_status); if (!intel_dp_update_link_train(intel_dp)) { DRM_ERROR("failed to update link training\n"); break; } } }