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 */ radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp); }
static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; if (!ASIC_IS_DCE4(rdev)) return; if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == ENCODER_OBJECT_ID_NUTMEG) panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == ENCODER_OBJECT_ID_TRAVIS) panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP); if (tmp & 1) panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; } atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP_PANEL_MODE, panel_mode); if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) && (panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { radeon_write_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_SET, 1); } }
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 */ if (dp_info->dpcd[0] >= 0x11) { radeon_write_dpcd_reg(dp_info->radeon_connector, DP_SET_POWER, DP_SET_POWER_D0); usleep_range(1000, 2000); } /* possibly enable downspread on the sink */ if (dp_info->dpcd[3] & 0x1) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); else radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, 0); if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) && (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { radeon_write_dpcd_reg(dp_info->radeon_connector, 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; radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp); /* set the link rate on the sink */ tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock); radeon_write_dpcd_reg(dp_info->radeon_connector, 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 */ radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); return 0; }
static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; if (!ASIC_IS_DCE4(rdev)) return; if (radeon_connector_encoder_is_dp_bridge(connector)) panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP_PANEL_MODE, panel_mode); }
static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) { u8 tmp; /* power up the sink */ if (dp_info->dpcd[0] >= 0x11) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_SET_POWER, DP_SET_POWER_D0); /* possibly enable downspread on the sink */ if (dp_info->dpcd[3] & 0x1) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); else radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, 0); radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector); /* set the lane count on the sink */ tmp = dp_info->dp_lane_count; if (dp_info->dpcd[0] >= 0x11) tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN; radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp); /* set the link rate on the sink */ tmp = dp_get_dp_link_rate_coded(dp_info->dp_clock); radeon_write_dpcd_reg(dp_info->radeon_connector, 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 */ radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); return 0; }
static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info) { DRM_UDELAY(400); /* disable the training pattern on the sink */ radeon_write_dpcd_reg(dp_info->radeon_connector, 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; }
void dp_link_train(struct drm_encoder *encoder, struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig; struct radeon_connector *radeon_connector; struct radeon_connector_atom_dig *dig_connector; int enc_id = 0; bool clock_recovery, channel_eq; u8 link_status[DP_LINK_STATUS_SIZE]; u8 link_configuration[DP_LINK_CONFIGURATION_SIZE]; u8 tries, voltage; u8 train_set[4]; int i; if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) && (connector->connector_type != DRM_MODE_CONNECTOR_eDP)) return; if (!radeon_encoder->enc_priv) return; dig = radeon_encoder->enc_priv; radeon_connector = to_radeon_connector(connector); if (!radeon_connector->con_priv) return; dig_connector = radeon_connector->con_priv; if (dig->dig_encoder) enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER; else enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER; if (dig_connector->linkb) enc_id |= ATOM_DP_CONFIG_LINK_B; else enc_id |= ATOM_DP_CONFIG_LINK_A; memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); if (dig_connector->dp_clock == 270000) link_configuration[0] = DP_LINK_BW_2_7; else link_configuration[0] = DP_LINK_BW_1_62; link_configuration[1] = dig_connector->dp_lane_count; if (dig_connector->dpcd[0] >= 0x11) link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; /* power up the sink */ dp_set_power(radeon_connector, DP_SET_POWER_D0); /* disable the training pattern on the sink */ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE); /* set link bw and lanes on the sink */ dp_set_link_bw_lanes(radeon_connector, link_configuration); /* disable downspread on the sink */ dp_set_downspread(radeon_connector, 0); if (ASIC_IS_DCE4(rdev)) { /* start training on the source */ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START); /* set training pattern 1 on the source */ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1); } else { /* start training on the source */ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START, dig_connector->dp_clock, enc_id, 0); /* set training pattern 1 on the source */ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, dig_connector->dp_clock, enc_id, 0); } /* set initial vs/emph */ memset(train_set, 0, 4); udelay(400); /* set training pattern 1 on the sink */ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1); dp_update_dpvs_emph(radeon_connector, encoder, train_set); /* clock recovery loop */ clock_recovery = false; tries = 0; voltage = 0xff; for (;;) { udelay(100); if (!atom_dp_get_link_status(radeon_connector, link_status)) break; if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) { clock_recovery = true; break; } for (i = 0; i < dig_connector->dp_lane_count; i++) { if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; } if (i == dig_connector->dp_lane_count) { DRM_ERROR("clock recovery reached max voltage\n"); break; } if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { ++tries; if (tries == 5) { DRM_ERROR("clock recovery tried 5 times\n"); break; } } else tries = 0; voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Compute new train_set as requested by sink */ dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set); dp_update_dpvs_emph(radeon_connector, encoder, train_set); } if (!clock_recovery) DRM_ERROR("clock recovery failed\n"); else DRM_DEBUG("clock recovery at voltage %d pre-emphasis %d\n", train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT); /* set training pattern 2 on the sink */ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2); /* set training pattern 2 on the source */ if (ASIC_IS_DCE4(rdev)) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2); else radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, dig_connector->dp_clock, enc_id, 1); /* channel equalization loop */ tries = 0; channel_eq = false; for (;;) { udelay(400); if (!atom_dp_get_link_status(radeon_connector, link_status)) break; if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) { channel_eq = true; break; } /* Try 5 times */ if (tries > 5) { DRM_ERROR("channel eq failed: 5 tries\n"); break; } /* Compute new train_set as requested by sink */ dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set); dp_update_dpvs_emph(radeon_connector, encoder, train_set); tries++; } if (!channel_eq) DRM_ERROR("channel eq failed\n"); else DRM_DEBUG("channel eq at voltage %d pre-emphasis %d\n", train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT); /* disable the training pattern on the sink */ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE); /* disable the training pattern on the source */ if (ASIC_IS_DCE4(rdev)) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE); else radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE, dig_connector->dp_clock, enc_id, 0); }