static void xilinx_drm_dp_dpms(struct drm_encoder *encoder, int dpms) { struct xilinx_drm_dp *dp = to_dp(encoder); void __iomem *iomem = dp->iomem; if (dp->dpms == dpms) return; dp->dpms = dpms; switch (dpms) { case DRM_MODE_DPMS_ON: xilinx_drm_writel(dp->iomem, XILINX_DP_TX_SW_RESET, XILINX_DP_TX_SW_RESET_ALL); if (dp->aud_clk) xilinx_drm_writel(iomem, XILINX_DP_TX_AUDIO_CONTROL, 1); xilinx_drm_writel(iomem, XILINX_DP_TX_PHY_POWER_DOWN, 0); drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); xilinx_drm_dp_train(dp); xilinx_drm_writel(iomem, XILINX_DP_TX_ENABLE_MAIN_STREAM, 1); return; default: xilinx_drm_writel(iomem, XILINX_DP_TX_ENABLE_MAIN_STREAM, 0); drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3); xilinx_drm_writel(iomem, XILINX_DP_TX_PHY_POWER_DOWN, XILINX_DP_TX_PHY_POWER_DOWN_ALL); if (dp->aud_clk) xilinx_drm_writel(iomem, XILINX_DP_TX_AUDIO_CONTROL, 0); return; } }
static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp) { analogix_dp_set_training_pattern(dp, DP_NONE); drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); }
static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) { int rtp = 0; /* set training pattern on the source */ if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) { switch (tp) { case DP_TRAINING_PATTERN_1: rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1; break; case DP_TRAINING_PATTERN_2: rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2; break; case DP_TRAINING_PATTERN_3: rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3; break; } atombios_dig_encoder_setup(dp_info->encoder, rtp, 0); } else { switch (tp) { case DP_TRAINING_PATTERN_1: rtp = 0; break; case DP_TRAINING_PATTERN_2: rtp = 1; break; } radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, dp_info->dp_clock, dp_info->enc_id, rtp); } /* enable training pattern on the sink */ drm_dp_dpcd_writeb(dp_info->aux, DP_TRAINING_PATTERN_SET, tp); }
/** * xilinx_drm_dp_link_train_cr - Train clock recovery * @dp: DisplayPort IP core structure * * Return: 0 if clock recovery train is done successfully, or corresponding * error code. */ static int xilinx_drm_dp_link_train_cr(struct xilinx_drm_dp *dp) { u8 link_status[DP_LINK_STATUS_SIZE]; u8 lane_cnt = dp->mode.lane_cnt; u8 vs = 0, tries = 0; u16 max_tries, i; bool cr_done; int ret; ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE); if (ret < 0) return ret; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_1); /* 256 loops should be maximum iterations for 4 lanes and 4 values. * So, This loop should exit before 512 iterations */ for (max_tries = 0; max_tries < 512; max_tries++) { ret = xilinx_drm_dp_update_vs_emph(dp); if (ret) return ret; drm_dp_link_train_clock_recovery_delay(dp->dpcd); ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status); if (ret < 0) return ret; cr_done = drm_dp_clock_recovery_ok(link_status, lane_cnt); if (cr_done) break; for (i = 0; i < lane_cnt; i++) if (!(dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED)) break; if (i == lane_cnt) break; if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == vs) tries++; else tries = 0; if (tries == DP_MAX_TRAINING_TRIES) break; vs = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; xilinx_drm_dp_adjust_train(dp, link_status); } if (!cr_done) return -EIO; return 0; }
static void analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp, bool enable) { u8 data; drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data); if (enable) drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, DP_LANE_COUNT_ENHANCED_FRAME_EN | DPCD_LANE_COUNT_SET(data)); else drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, DPCD_LANE_COUNT_SET(data)); }
static int drm_dp_cec_adap_enable(struct cec_adapter *adap, bool enable) { struct drm_dp_aux *aux = cec_get_drvdata(adap); u32 val = enable ? DP_CEC_TUNNELING_ENABLE : 0; ssize_t err = 0; err = drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_CONTROL, val); return (enable && err < 0) ? err : 0; }
static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, bool enable) { u8 data; if (enable) { analogix_dp_enable_scrambling(dp); drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data); drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE)); } else { analogix_dp_disable_scrambling(dp); drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data); drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, (u8)(data | DP_LINK_SCRAMBLING_DISABLE)); } }
static void xilinx_drm_dp_dpms(struct drm_encoder *encoder, int dpms) { struct xilinx_drm_dp *dp = to_dp(encoder); void __iomem *iomem = dp->iomem; unsigned int i; int ret; if (dp->dpms == dpms) return; dp->dpms = dpms; switch (dpms) { case DRM_MODE_DPMS_ON: if (dp->aud_clk) xilinx_drm_writel(iomem, XILINX_DP_TX_AUDIO_CONTROL, 1); xilinx_drm_writel(iomem, XILINX_DP_TX_PHY_POWER_DOWN, 0); for (i = 0; i < 3; i++) { ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); if (ret == 1) break; usleep_range(300, 500); } xilinx_drm_dp_train(dp); xilinx_drm_writel(dp->iomem, XILINX_DP_TX_SW_RESET, XILINX_DP_TX_SW_RESET_ALL); xilinx_drm_writel(iomem, XILINX_DP_TX_ENABLE_MAIN_STREAM, 1); return; default: xilinx_drm_writel(iomem, XILINX_DP_TX_ENABLE_MAIN_STREAM, 0); drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3); xilinx_drm_writel(iomem, XILINX_DP_TX_PHY_POWER_DOWN, XILINX_DP_TX_PHY_POWER_DOWN_ALL); if (dp->aud_clk) xilinx_drm_writel(iomem, XILINX_DP_TX_AUDIO_CONTROL, 0); return; } }
static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) { unsigned char psr_en; /* Disable psr function */ drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en); psr_en &= ~DP_PSR_ENABLE; drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); /* Main-Link transmitter remains active during PSR active states */ psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION; drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); /* Enable psr function */ psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION; drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); analogix_dp_enable_psr_crc(dp); }
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; }
/** * xilinx_drm_dp_link_train_ce - Train channel equalization * @dp: DisplayPort IP core structure * * Return: 0 if channel equalization train is done successfully, or * corresponding error code. */ static int xilinx_drm_dp_link_train_ce(struct xilinx_drm_dp *dp) { u8 link_status[DP_LINK_STATUS_SIZE]; u8 lane_cnt = dp->mode.lane_cnt; u32 pat, tries; int ret; bool ce_done; if (dp->config.dp_version == DP_V1_2 && dp->dpcd[DP_DPCD_REV] >= DP_V1_2 && dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED) pat = DP_TRAINING_PATTERN_3; else pat = DP_TRAINING_PATTERN_2; ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, pat | DP_LINK_SCRAMBLING_DISABLE); if (ret < 0) return ret; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_TRAINING_PATTERN_SET, pat); for (tries = 0; tries < DP_MAX_TRAINING_TRIES; tries++) { ret = xilinx_drm_dp_update_vs_emph(dp); if (ret) return ret; drm_dp_link_train_channel_eq_delay(dp->dpcd); ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status); if (ret < 0) return ret; ce_done = drm_dp_channel_eq_ok(link_status, lane_cnt); if (ce_done) break; xilinx_drm_dp_adjust_train(dp, link_status); } if (!ce_done) return -EIO; return 0; }
void radeon_dp_set_rx_power_state(struct drm_connector *connector, u8 power_state) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *dig_connector; if (!radeon_connector->con_priv) return; dig_connector = radeon_connector->con_priv; /* power up/down the sink */ if (dig_connector->dpcd[0] >= 0x11) { drm_dp_dpcd_writeb(&radeon_connector->ddc_bus->aux, DP_SET_POWER, power_state); usleep_range(1000, 2000); } }
/** * drm_dp_cec_irq() - handle CEC interrupt, if any * @aux: DisplayPort AUX channel * * Should be called when handling an IRQ_HPD request. If CEC-tunneling-over-AUX * is present, then it will check for a CEC_IRQ and handle it accordingly. */ void drm_dp_cec_irq(struct drm_dp_aux *aux) { u8 cec_irq; int ret; mutex_lock(&aux->cec.lock); if (!aux->cec.adap) goto unlock; ret = drm_dp_dpcd_readb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1, &cec_irq); if (ret < 0 || !(cec_irq & DP_CEC_IRQ)) goto unlock; drm_dp_cec_handle_irq(aux); drm_dp_dpcd_writeb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1, DP_CEC_IRQ); unlock: mutex_unlock(&aux->cec.lock); }
static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info) { udelay(400); /* disable the training pattern on the sink */ drm_dp_dpcd_writeb(dp_info->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); /* disable the training pattern on the source */ if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) atombios_dig_encoder_setup(dp_info->encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0); else radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_COMPLETE, dp_info->dp_clock, dp_info->enc_id, 0); return 0; }
static int drm_dp_cec_adap_monitor_all_enable(struct cec_adapter *adap, bool enable) { struct drm_dp_aux *aux = cec_get_drvdata(adap); ssize_t err; u8 val; if (!(adap->capabilities & CEC_CAP_MONITOR_ALL)) return 0; err = drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_CONTROL, &val); if (err >= 0) { if (enable) val |= DP_CEC_SNOOPING_ENABLE; else val &= ~DP_CEC_SNOOPING_ENABLE; err = drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_CONTROL, val); } return (enable && err < 0) ? err : 0; }
static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable) { uint8_t reg_val = 0; if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, ®_val) < 0) { DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n", DP_EDP_DISPLAY_CONTROL_REGISTER); return; } if (enable) reg_val |= DP_EDP_BACKLIGHT_ENABLE; else reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE); if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, reg_val) != 1) { DRM_DEBUG_KMS("Failed to %s aux backlight\n", enable ? "enable" : "disable"); } }
static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(dp_info->encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; u8 tmp; /* power up the sink */ radeon_dp_set_rx_power_state(dp_info->connector, DP_SET_POWER_D0); /* possibly enable downspread on the sink */ if (dp_info->dpcd[3] & 0x1) drm_dp_dpcd_writeb(dp_info->aux, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); else drm_dp_dpcd_writeb(dp_info->aux, DP_DOWNSPREAD_CTRL, 0); if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) && (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { drm_dp_dpcd_writeb(dp_info->aux, DP_EDP_CONFIGURATION_SET, 1); } /* set the lane count on the sink */ tmp = dp_info->dp_lane_count; if (drm_dp_enhanced_frame_cap(dp_info->dpcd)) tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_writeb(dp_info->aux, DP_LANE_COUNT_SET, tmp); /* set the link rate on the sink */ tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock); drm_dp_dpcd_writeb(dp_info->aux, DP_LINK_BW_SET, tmp); /* start training on the source */ if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) atombios_dig_encoder_setup(dp_info->encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0); else radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_START, dp_info->dp_clock, dp_info->enc_id, 0); /* disable the training pattern on the sink */ drm_dp_dpcd_writeb(dp_info->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); return 0; }
static void drm_dp_cec_handle_irq(struct drm_dp_aux *aux) { struct cec_adapter *adap = aux->cec.adap; u8 flags; if (drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_IRQ_FLAGS, &flags) < 0) return; if (flags & DP_CEC_RX_MESSAGE_INFO_VALID) drm_dp_cec_received(aux); if (flags & DP_CEC_TX_MESSAGE_SENT) cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK); else if (flags & DP_CEC_TX_LINE_ERROR) cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR | CEC_TX_STATUS_MAX_RETRIES); else if (flags & (DP_CEC_TX_ADDRESS_NACK_ERROR | DP_CEC_TX_DATA_NACK_ERROR)) cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK | CEC_TX_STATUS_MAX_RETRIES); drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_IRQ_FLAGS, flags); }
/** * drm_dp_cec_irq() - handle CEC interrupt, if any * @aux: DisplayPort AUX channel * * Should be called when handling an IRQ_HPD request. If CEC-tunneling-over-AUX * is present, then it will check for a CEC_IRQ and handle it accordingly. */ void drm_dp_cec_irq(struct drm_dp_aux *aux) { u8 cec_irq; int ret; /* No transfer function was set, so not a DP connector */ if (!aux->transfer) return; mutex_lock(&aux->cec.lock); if (!aux->cec.adap) goto unlock; ret = drm_dp_dpcd_readb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1, &cec_irq); if (ret < 0 || !(cec_irq & DP_CEC_IRQ)) goto unlock; drm_dp_cec_handle_irq(aux); drm_dp_dpcd_writeb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1, DP_CEC_IRQ); unlock: mutex_unlock(&aux->cec.lock); }
static void vlv_psr_enable_sink(struct intel_dp *intel_dp) { drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE); }
/** * xilinx_drm_dp_link_train - Train the link * @dp: DisplayPort IP core structure * * Return: 0 if all trains are done successfully, or corresponding error code. */ static int xilinx_drm_dp_train(struct xilinx_drm_dp *dp) { u32 reg; u8 bw_code = dp->mode.bw_code; u8 lane_cnt = dp->mode.lane_cnt; u8 aux_lane_cnt = lane_cnt; bool enhanced; int ret; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_LANE_CNT_SET, lane_cnt); enhanced = drm_dp_enhanced_frame_cap(dp->dpcd); if (enhanced) { xilinx_drm_writel(dp->iomem, XILINX_DP_TX_ENHANCED_FRAME_EN, 1); aux_lane_cnt |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } if (dp->dpcd[3] & 0x1) { xilinx_drm_writel(dp->iomem, XILINX_DP_TX_DOWNSPREAD_CTL, 1); drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); } else { xilinx_drm_writel(dp->iomem, XILINX_DP_TX_DOWNSPREAD_CTL, 0); drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, 0); } ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, aux_lane_cnt); if (ret < 0) { DRM_ERROR("failed to set lane count\n"); return ret; } ret = drm_dp_dpcd_writeb(&dp->aux, DP_MAIN_LINK_CHANNEL_CODING_SET, DP_SET_ANSI_8B10B); if (ret < 0) { DRM_ERROR("failed to set ANSI 8B/10B encoding\n"); return ret; } ret = drm_dp_dpcd_writeb(&dp->aux, DP_LINK_BW_SET, bw_code); if (ret < 0) { DRM_ERROR("failed to set DP bandwidth\n"); return ret; } xilinx_drm_writel(dp->iomem, XILINX_DP_TX_LINK_BW_SET, bw_code); switch (bw_code) { case DP_LINK_BW_1_62: reg = XILINX_DP_TX_PHY_CLOCK_FEEDBACK_SETTING_162; break; case DP_LINK_BW_2_7: reg = XILINX_DP_TX_PHY_CLOCK_FEEDBACK_SETTING_270; break; case DP_LINK_BW_5_4: default: reg = XILINX_DP_TX_PHY_CLOCK_FEEDBACK_SETTING_540; break; } xilinx_drm_writel(dp->iomem, XILINX_DP_TX_PHY_CLOCK_FEEDBACK_SETTING, reg); ret = xilinx_drm_dp_phy_ready(dp); if (ret < 0) return ret; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_SCRAMBLING_DISABLE, 1); memset(dp->train_set, 0, 4); ret = xilinx_drm_dp_link_train_cr(dp); if (ret) { DRM_ERROR("failed to train clock recovery\n"); return ret; } ret = xilinx_drm_dp_link_train_ce(dp); if (ret) { DRM_ERROR("failed to train channel eq\n"); return ret; } xilinx_drm_writel(dp->iomem, XILINX_DP_TX_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); if (ret < 0) { DRM_ERROR("failed to disable training pattern\n"); return ret; } xilinx_drm_writel(dp->iomem, XILINX_DP_TX_SCRAMBLING_DISABLE, 0); 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; }