43,158 → 43,242 |
"0dB", "3.5dB", "6dB", "9.5dB" |
}; |
|
static const int dp_clocks[] = { |
54000, /* 1 lane, 1.62 Ghz */ |
90000, /* 1 lane, 2.70 Ghz */ |
108000, /* 2 lane, 1.62 Ghz */ |
180000, /* 2 lane, 2.70 Ghz */ |
216000, /* 4 lane, 1.62 Ghz */ |
360000, /* 4 lane, 2.70 Ghz */ |
/***** radeon AUX functions *****/ |
union aux_channel_transaction { |
PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; |
PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; |
}; |
|
static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int); |
static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, |
u8 *send, int send_bytes, |
u8 *recv, int recv_size, |
u8 delay, u8 *ack) |
{ |
struct drm_device *dev = chan->dev; |
struct radeon_device *rdev = dev->dev_private; |
union aux_channel_transaction args; |
int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); |
unsigned char *base; |
int recv_bytes; |
|
/* common helper functions */ |
static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock) |
memset(&args, 0, sizeof(args)); |
|
base = (unsigned char *)rdev->mode_info.atom_context->scratch; |
|
memcpy(base, send, send_bytes); |
|
args.v1.lpAuxRequest = 0; |
args.v1.lpDataOut = 16; |
args.v1.ucDataOutLen = 0; |
args.v1.ucChannelID = chan->rec.i2c_id; |
args.v1.ucDelay = delay / 10; |
if (ASIC_IS_DCE4(rdev)) |
args.v2.ucHPD_ID = chan->rec.hpd; |
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
|
*ack = args.v1.ucReplyStatus; |
|
/* timeout */ |
if (args.v1.ucReplyStatus == 1) { |
DRM_DEBUG_KMS("dp_aux_ch timeout\n"); |
return -ETIMEDOUT; |
} |
|
/* flags not zero */ |
if (args.v1.ucReplyStatus == 2) { |
DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); |
return -EBUSY; |
} |
|
/* error */ |
if (args.v1.ucReplyStatus == 3) { |
DRM_DEBUG_KMS("dp_aux_ch error\n"); |
return -EIO; |
} |
|
recv_bytes = args.v1.ucDataOutLen; |
if (recv_bytes > recv_size) |
recv_bytes = recv_size; |
|
if (recv && recv_size) |
memcpy(recv, base + 16, recv_bytes); |
|
return recv_bytes; |
} |
|
static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, |
u16 address, u8 *send, u8 send_bytes, u8 delay) |
{ |
int i; |
u8 max_link_bw; |
u8 max_lane_count; |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
int ret; |
u8 msg[20]; |
int msg_bytes = send_bytes + 4; |
u8 ack; |
|
if (!dpcd) |
return 0; |
if (send_bytes > 16) |
return -1; |
|
max_link_bw = dpcd[DP_MAX_LINK_RATE]; |
max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; |
msg[0] = address; |
msg[1] = address >> 8; |
msg[2] = AUX_NATIVE_WRITE << 4; |
msg[3] = (msg_bytes << 4) | (send_bytes - 1); |
memcpy(&msg[4], send, send_bytes); |
|
switch (max_link_bw) { |
case DP_LINK_BW_1_62: |
default: |
for (i = 0; i < num_dp_clocks; i++) { |
if (i % 2) |
continue; |
switch (max_lane_count) { |
case 1: |
if (i > 1) |
return 0; |
while (1) { |
ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, |
msg, msg_bytes, NULL, 0, delay, &ack); |
if (ret < 0) |
return ret; |
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) |
break; |
case 2: |
if (i > 3) |
return 0; |
break; |
case 4: |
default: |
break; |
} |
if (dp_clocks[i] > mode_clock) { |
if (i < 2) |
return 1; |
else if (i < 4) |
return 2; |
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) |
udelay(400); |
else |
return 4; |
return -EIO; |
} |
|
return send_bytes; |
} |
break; |
case DP_LINK_BW_2_7: |
for (i = 0; i < num_dp_clocks; i++) { |
switch (max_lane_count) { |
case 1: |
if (i > 1) |
return 0; |
break; |
case 2: |
if (i > 3) |
return 0; |
break; |
case 4: |
default: |
break; |
} |
if (dp_clocks[i] > mode_clock) { |
if (i < 2) |
return 1; |
else if (i < 4) |
return 2; |
|
static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, |
u16 address, u8 *recv, int recv_bytes, u8 delay) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
u8 msg[4]; |
int msg_bytes = 4; |
u8 ack; |
int ret; |
|
msg[0] = address; |
msg[1] = address >> 8; |
msg[2] = AUX_NATIVE_READ << 4; |
msg[3] = (msg_bytes << 4) | (recv_bytes - 1); |
|
while (1) { |
ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, |
msg, msg_bytes, recv, recv_bytes, delay, &ack); |
if (ret == 0) |
return -EPROTO; |
if (ret < 0) |
return ret; |
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) |
return ret; |
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) |
udelay(400); |
else |
return 4; |
return -EIO; |
} |
} |
break; |
|
static void radeon_write_dpcd_reg(struct radeon_connector *radeon_connector, |
u16 reg, u8 val) |
{ |
radeon_dp_aux_native_write(radeon_connector, reg, &val, 1, 0); |
} |
|
return 0; |
static u8 radeon_read_dpcd_reg(struct radeon_connector *radeon_connector, |
u16 reg) |
{ |
u8 val = 0; |
|
radeon_dp_aux_native_read(radeon_connector, reg, &val, 1, 0); |
|
return val; |
} |
|
static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock) |
int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, |
u8 write_byte, u8 *read_byte) |
{ |
int i; |
u8 max_link_bw; |
u8 max_lane_count; |
struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter; |
u16 address = algo_data->address; |
u8 msg[5]; |
u8 reply[2]; |
unsigned retry; |
int msg_bytes; |
int reply_bytes = 1; |
int ret; |
u8 ack; |
|
if (!dpcd) |
return 0; |
/* Set up the command byte */ |
if (mode & MODE_I2C_READ) |
msg[2] = AUX_I2C_READ << 4; |
else |
msg[2] = AUX_I2C_WRITE << 4; |
|
max_link_bw = dpcd[DP_MAX_LINK_RATE]; |
max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; |
if (!(mode & MODE_I2C_STOP)) |
msg[2] |= AUX_I2C_MOT << 4; |
|
switch (max_link_bw) { |
case DP_LINK_BW_1_62: |
default: |
for (i = 0; i < num_dp_clocks; i++) { |
if (i % 2) |
continue; |
switch (max_lane_count) { |
case 1: |
if (i > 1) |
return 0; |
msg[0] = address; |
msg[1] = address >> 8; |
|
switch (mode) { |
case MODE_I2C_WRITE: |
msg_bytes = 5; |
msg[3] = msg_bytes << 4; |
msg[4] = write_byte; |
break; |
case 2: |
if (i > 3) |
return 0; |
case MODE_I2C_READ: |
msg_bytes = 4; |
msg[3] = msg_bytes << 4; |
break; |
case 4: |
default: |
msg_bytes = 4; |
msg[3] = 3 << 4; |
break; |
} |
if (dp_clocks[i] > mode_clock) |
return 162000; |
|
for (retry = 0; retry < 4; retry++) { |
ret = radeon_process_aux_ch(auxch, |
msg, msg_bytes, reply, reply_bytes, 0, &ack); |
if (ret < 0) { |
DRM_DEBUG_KMS("aux_ch failed %d\n", ret); |
return ret; |
} |
|
switch (ack & AUX_NATIVE_REPLY_MASK) { |
case AUX_NATIVE_REPLY_ACK: |
/* I2C-over-AUX Reply field is only valid |
* when paired with AUX ACK. |
*/ |
break; |
case DP_LINK_BW_2_7: |
for (i = 0; i < num_dp_clocks; i++) { |
switch (max_lane_count) { |
case 1: |
if (i > 1) |
return 0; |
case AUX_NATIVE_REPLY_NACK: |
DRM_DEBUG_KMS("aux_ch native nack\n"); |
return -EREMOTEIO; |
case AUX_NATIVE_REPLY_DEFER: |
DRM_DEBUG_KMS("aux_ch native defer\n"); |
udelay(400); |
continue; |
default: |
DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack); |
return -EREMOTEIO; |
} |
|
switch (ack & AUX_I2C_REPLY_MASK) { |
case AUX_I2C_REPLY_ACK: |
if (mode == MODE_I2C_READ) |
*read_byte = reply[0]; |
return ret; |
case AUX_I2C_REPLY_NACK: |
DRM_DEBUG_KMS("aux_i2c nack\n"); |
return -EREMOTEIO; |
case AUX_I2C_REPLY_DEFER: |
DRM_DEBUG_KMS("aux_i2c defer\n"); |
udelay(400); |
break; |
case 2: |
if (i > 3) |
return 0; |
break; |
case 4: |
default: |
break; |
DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack); |
return -EREMOTEIO; |
} |
if (dp_clocks[i] > mode_clock) |
return (i % 2) ? 270000 : 162000; |
} |
} |
|
return 0; |
DRM_ERROR("aux i2c too many retries, giving up\n"); |
return -EREMOTEIO; |
} |
|
int dp_mode_valid(u8 dpcd[DP_DPCD_SIZE], int mode_clock) |
{ |
int lanes = dp_lanes_for_mode_clock(dpcd, mode_clock); |
int bw = dp_lanes_for_mode_clock(dpcd, mode_clock); |
/***** general DP utility functions *****/ |
|
if ((lanes == 0) || (bw == 0)) |
return MODE_CLOCK_HIGH; |
|
return MODE_OK; |
} |
|
static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) |
{ |
return link_status[r - DP_LANE0_1_STATUS]; |
242,7 → 326,7 |
return true; |
} |
|
static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], |
static u8 dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], |
int lane) |
|
{ |
255,7 → 339,7 |
return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; |
} |
|
static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], |
static u8 dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], |
int lane) |
{ |
int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); |
267,22 → 351,8 |
return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; |
} |
|
/* XXX fix me -- chip specific */ |
#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 |
static u8 dp_pre_emphasis_max(u8 voltage_swing) |
{ |
switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { |
case DP_TRAIN_VOLTAGE_SWING_400: |
return DP_TRAIN_PRE_EMPHASIS_6; |
case DP_TRAIN_VOLTAGE_SWING_600: |
return DP_TRAIN_PRE_EMPHASIS_6; |
case DP_TRAIN_VOLTAGE_SWING_800: |
return DP_TRAIN_PRE_EMPHASIS_3_5; |
case DP_TRAIN_VOLTAGE_SWING_1200: |
default: |
return DP_TRAIN_PRE_EMPHASIS_0; |
} |
} |
#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 |
|
static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], |
int lane_count, |
296,7 → 366,7 |
u8 this_v = dp_get_adjust_request_voltage(link_status, lane); |
u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane); |
|
DRM_DEBUG("requested signal parameters: lane %d voltage %s pre_emph %s\n", |
DRM_DEBUG_KMS("requested signal parameters: lane %d voltage %s pre_emph %s\n", |
lane, |
voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT], |
pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); |
308,12 → 378,12 |
} |
|
if (v >= DP_VOLTAGE_MAX) |
v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; |
v |= DP_TRAIN_MAX_SWING_REACHED; |
|
if (p >= dp_pre_emphasis_max(v)) |
p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; |
if (p >= DP_PRE_EMPHASIS_MAX) |
p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; |
|
DRM_DEBUG("using signal parameters: voltage %s pre_emph %s\n", |
DRM_DEBUG_KMS("using signal parameters: voltage %s pre_emph %s\n", |
voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT], |
pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); |
|
321,110 → 391,109 |
train_set[lane] = v | p; |
} |
|
union aux_channel_transaction { |
PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; |
PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; |
}; |
/* convert bits per color to bits per pixel */ |
/* get bpc from the EDID */ |
static int convert_bpc_to_bpp(int bpc) |
{ |
if (bpc == 0) |
return 24; |
else |
return bpc * 3; |
} |
|
/* radeon aux chan functions */ |
bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, |
int num_bytes, u8 *read_byte, |
u8 read_buf_len, u8 delay) |
/* get the max pix clock supported by the link rate and lane num */ |
static int dp_get_max_dp_pix_clock(int link_rate, |
int lane_num, |
int bpp) |
{ |
struct drm_device *dev = chan->dev; |
struct radeon_device *rdev = dev->dev_private; |
union aux_channel_transaction args; |
int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); |
unsigned char *base; |
int retry_count = 0; |
|
memset(&args, 0, sizeof(args)); |
|
base = (unsigned char *)rdev->mode_info.atom_context->scratch; |
|
retry: |
memcpy(base, req_bytes, num_bytes); |
|
args.v1.lpAuxRequest = 0; |
args.v1.lpDataOut = 16; |
args.v1.ucDataOutLen = 0; |
args.v1.ucChannelID = chan->rec.i2c_id; |
args.v1.ucDelay = delay / 10; |
if (ASIC_IS_DCE4(rdev)) |
args.v2.ucHPD_ID = chan->rec.hpd_id; |
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
|
if (args.v1.ucReplyStatus && !args.v1.ucDataOutLen) { |
if (args.v1.ucReplyStatus == 0x20 && retry_count++ < 10) |
goto retry; |
DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n", |
req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], |
chan->rec.i2c_id, args.v1.ucReplyStatus, retry_count); |
return false; |
return (link_rate * lane_num * 8) / bpp; |
} |
|
if (args.v1.ucDataOutLen && read_byte && read_buf_len) { |
if (read_buf_len < args.v1.ucDataOutLen) { |
DRM_ERROR("Buffer to small for return answer %d %d\n", |
read_buf_len, args.v1.ucDataOutLen); |
return false; |
} |
static int dp_get_max_link_rate(u8 dpcd[DP_DPCD_SIZE]) |
{ |
int len = min(read_buf_len, args.v1.ucDataOutLen); |
memcpy(read_byte, base + 16, len); |
switch (dpcd[DP_MAX_LINK_RATE]) { |
case DP_LINK_BW_1_62: |
default: |
return 162000; |
case DP_LINK_BW_2_7: |
return 270000; |
case DP_LINK_BW_5_4: |
return 540000; |
} |
} |
return true; |
|
static u8 dp_get_max_lane_number(u8 dpcd[DP_DPCD_SIZE]) |
{ |
return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; |
} |
|
bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address, |
uint8_t send_bytes, uint8_t *send) |
static u8 dp_get_dp_link_rate_coded(int link_rate) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
u8 msg[20]; |
u8 msg_len, dp_msg_len; |
bool ret; |
switch (link_rate) { |
case 162000: |
default: |
return DP_LINK_BW_1_62; |
case 270000: |
return DP_LINK_BW_2_7; |
case 540000: |
return DP_LINK_BW_5_4; |
} |
} |
|
dp_msg_len = 4; |
msg[0] = address; |
msg[1] = address >> 8; |
msg[2] = AUX_NATIVE_WRITE << 4; |
dp_msg_len += send_bytes; |
msg[3] = (dp_msg_len << 4) | (send_bytes - 1); |
/***** radeon specific DP functions *****/ |
|
if (send_bytes > 16) |
return false; |
/* First get the min lane# when low rate is used according to pixel clock |
* (prefer low rate), second check max lane# supported by DP panel, |
* if the max lane# < low rate lane# then use max lane# instead. |
*/ |
static int radeon_dp_get_dp_lane_number(struct drm_connector *connector, |
u8 dpcd[DP_DPCD_SIZE], |
int pix_clock) |
{ |
int bpp = convert_bpc_to_bpp(connector->display_info.bpc); |
int max_link_rate = dp_get_max_link_rate(dpcd); |
int max_lane_num = dp_get_max_lane_number(dpcd); |
int lane_num; |
int max_dp_pix_clock; |
|
memcpy(&msg[4], send, send_bytes); |
msg_len = 4 + send_bytes; |
ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0); |
return ret; |
for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) { |
max_dp_pix_clock = dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp); |
if (pix_clock <= max_dp_pix_clock) |
break; |
} |
|
bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16_t address, |
uint8_t delay, uint8_t expected_bytes, |
uint8_t *read_p) |
return lane_num; |
} |
|
static int radeon_dp_get_dp_link_clock(struct drm_connector *connector, |
u8 dpcd[DP_DPCD_SIZE], |
int pix_clock) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
u8 msg[20]; |
u8 msg_len, dp_msg_len; |
bool ret = false; |
msg_len = 4; |
dp_msg_len = 4; |
msg[0] = address; |
msg[1] = address >> 8; |
msg[2] = AUX_NATIVE_READ << 4; |
msg[3] = (dp_msg_len) << 4; |
msg[3] |= expected_bytes - 1; |
int bpp = convert_bpc_to_bpp(connector->display_info.bpc); |
int lane_num, max_pix_clock; |
|
ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay); |
return ret; |
if (radeon_connector_encoder_is_dp_bridge(connector)) |
return 270000; |
|
lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock); |
max_pix_clock = dp_get_max_dp_pix_clock(162000, lane_num, bpp); |
if (pix_clock <= max_pix_clock) |
return 162000; |
max_pix_clock = dp_get_max_dp_pix_clock(270000, lane_num, bpp); |
if (pix_clock <= max_pix_clock) |
return 270000; |
if (radeon_connector_is_dp12_capable(connector)) { |
max_pix_clock = dp_get_max_dp_pix_clock(540000, lane_num, bpp); |
if (pix_clock <= max_pix_clock) |
return 540000; |
} |
|
/* radeon dp functions */ |
static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, |
uint8_t ucconfig, uint8_t lane_num) |
return dp_get_max_link_rate(dpcd); |
} |
|
static u8 radeon_dp_encoder_service(struct radeon_device *rdev, |
int action, int dp_clock, |
u8 ucconfig, u8 lane_num) |
{ |
DP_ENCODER_SERVICE_PARAMETERS args; |
int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); |
454,18 → 523,15 |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
u8 msg[25]; |
int ret; |
int ret, i; |
|
ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg); |
if (ret) { |
ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, 8, 0); |
if (ret > 0) { |
memcpy(dig_connector->dpcd, msg, 8); |
{ |
int i; |
DRM_DEBUG("DPCD: "); |
DRM_DEBUG_KMS("DPCD: "); |
for (i = 0; i < 8; i++) |
DRM_DEBUG("%02x ", msg[i]); |
DRM_DEBUG("\n"); |
} |
DRM_DEBUG_KMS("%02x ", msg[i]); |
DRM_DEBUG_KMS("\n"); |
return true; |
} |
dig_connector->dpcd[0] = 0; |
472,338 → 538,384 |
return false; |
} |
|
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); |
} |
|
void radeon_dp_set_link_config(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
struct radeon_connector *radeon_connector; |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct radeon_connector_atom_dig *dig_connector; |
|
if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) && |
(connector->connector_type != DRM_MODE_CONNECTOR_eDP)) |
return; |
|
radeon_connector = to_radeon_connector(connector); |
if (!radeon_connector->con_priv) |
return; |
dig_connector = radeon_connector->con_priv; |
|
if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || |
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { |
dig_connector->dp_clock = |
dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock); |
radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); |
dig_connector->dp_lane_count = |
dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock); |
radeon_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock); |
} |
} |
|
int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector, |
int radeon_dp_mode_valid_helper(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct radeon_connector_atom_dig *dig_connector; |
int dp_clock; |
|
return dp_mode_valid(dig_connector->dpcd, mode->clock); |
if (!radeon_connector->con_priv) |
return MODE_CLOCK_HIGH; |
dig_connector = radeon_connector->con_priv; |
|
dp_clock = |
radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); |
|
if ((dp_clock == 540000) && |
(!radeon_connector_is_dp12_capable(connector))) |
return MODE_CLOCK_HIGH; |
|
return MODE_OK; |
} |
|
static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, |
static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector, |
u8 link_status[DP_LINK_STATUS_SIZE]) |
{ |
int ret; |
ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, 100, |
DP_LINK_STATUS_SIZE, link_status); |
if (!ret) { |
ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, |
link_status, DP_LINK_STATUS_SIZE, 100); |
if (ret <= 0) { |
DRM_ERROR("displayport link status failed\n"); |
return false; |
} |
|
DRM_DEBUG("link status %02x %02x %02x %02x %02x %02x\n", |
DRM_DEBUG_KMS("link status %02x %02x %02x %02x %02x %02x\n", |
link_status[0], link_status[1], link_status[2], |
link_status[3], link_status[4], link_status[5]); |
return true; |
} |
|
bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
struct radeon_dp_link_train_info { |
struct radeon_device *rdev; |
struct drm_encoder *encoder; |
struct drm_connector *connector; |
struct radeon_connector *radeon_connector; |
int enc_id; |
int dp_clock; |
int dp_lane_count; |
int rd_interval; |
bool tp3_supported; |
u8 dpcd[8]; |
u8 train_set[4]; |
u8 link_status[DP_LINK_STATUS_SIZE]; |
u8 tries; |
}; |
|
if (!atom_dp_get_link_status(radeon_connector, link_status)) |
return false; |
if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) |
return false; |
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 */ |
radeon_dp_aux_native_write(dp_info->radeon_connector, DP_TRAINING_LANE0_SET, |
dp_info->train_set, dp_info->dp_lane_count, 0); |
} |
|
static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state) |
static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
int rtp = 0; |
|
if (dig_connector->dpcd[0] >= 0x11) { |
radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1, |
&power_state); |
/* set training pattern on the source */ |
if (ASIC_IS_DCE4(dp_info->rdev)) { |
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; |
} |
|
static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread) |
{ |
radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1, |
&downspread); |
radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, |
dp_info->dp_clock, dp_info->enc_id, rtp); |
} |
|
static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector, |
u8 link_configuration[DP_LINK_CONFIGURATION_SIZE]) |
{ |
radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, 2, |
link_configuration); |
/* enable training pattern on the sink */ |
radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp); |
} |
|
static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector, |
struct drm_encoder *encoder, |
u8 train_set[4]) |
static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) |
{ |
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
int i; |
u8 tmp; |
|
for (i = 0; i < dig_connector->dp_lane_count; i++) |
atombios_dig_transmitter_setup(encoder, |
ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, |
i, train_set[i]); |
/* 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); |
|
radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET, |
dig_connector->dp_lane_count, train_set); |
} |
/* 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); |
|
static void dp_set_training(struct radeon_connector *radeon_connector, |
u8 training) |
{ |
radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET, |
1, &training); |
} |
radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector); |
|
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; |
/* 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); |
|
if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) && |
(connector->connector_type != DRM_MODE_CONNECTOR_eDP)) |
return; |
/* 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); |
|
if (!radeon_encoder->enc_priv) |
return; |
dig = radeon_encoder->enc_priv; |
/* start training on the source */ |
if (ASIC_IS_DCE4(dp_info->rdev)) |
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); |
|
radeon_connector = to_radeon_connector(connector); |
if (!radeon_connector->con_priv) |
return; |
dig_connector = radeon_connector->con_priv; |
/* disable the training pattern on the sink */ |
radeon_write_dpcd_reg(dp_info->radeon_connector, |
DP_TRAINING_PATTERN_SET, |
DP_TRAINING_PATTERN_DISABLE); |
|
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; |
return 0; |
} |
|
memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); |
if (dig_connector->dp_clock == 270000) |
link_configuration[0] = DP_LINK_BW_2_7; |
static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info) |
{ |
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)) |
atombios_dig_encoder_setup(dp_info->encoder, |
ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0); |
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; |
radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_COMPLETE, |
dp_info->dp_clock, dp_info->enc_id, 0); |
|
/* 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); |
return 0; |
} |
|
/* set initial vs/emph */ |
memset(train_set, 0, 4); |
static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info) |
{ |
bool clock_recovery; |
u8 voltage; |
int i; |
|
radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1); |
memset(dp_info->train_set, 0, 4); |
radeon_dp_update_vs_emph(dp_info); |
|
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; |
dp_info->tries = 0; |
voltage = 0xff; |
for (;;) { |
while (1) { |
if (dp_info->rd_interval == 0) |
udelay(100); |
if (!atom_dp_get_link_status(radeon_connector, link_status)) |
else |
mdelay(dp_info->rd_interval * 4); |
|
if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) |
break; |
|
if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) { |
if (dp_clock_recovery_ok(dp_info->link_status, dp_info->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) |
for (i = 0; i < dp_info->dp_lane_count; i++) { |
if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) |
break; |
} |
if (i == dig_connector->dp_lane_count) { |
if (i == dp_info->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) { |
if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { |
++dp_info->tries; |
if (dp_info->tries == 5) { |
DRM_ERROR("clock recovery tried 5 times\n"); |
break; |
} |
} else |
tries = 0; |
dp_info->tries = 0; |
|
voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; |
voltage = dp_info->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); |
dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set); |
|
radeon_dp_update_vs_emph(dp_info); |
} |
if (!clock_recovery) |
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) >> |
return -1; |
} else { |
DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n", |
dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, |
(dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> |
DP_TRAIN_PRE_EMPHASIS_SHIFT); |
return 0; |
} |
} |
|
static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info) |
{ |
bool channel_eq; |
|
/* 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); |
if (dp_info->tp3_supported) |
radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_3); |
else |
radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, |
dig_connector->dp_clock, enc_id, 1); |
radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_2); |
|
/* channel equalization loop */ |
tries = 0; |
dp_info->tries = 0; |
channel_eq = false; |
for (;;) { |
while (1) { |
if (dp_info->rd_interval == 0) |
udelay(400); |
if (!atom_dp_get_link_status(radeon_connector, link_status)) |
else |
mdelay(dp_info->rd_interval * 4); |
|
if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) |
break; |
|
if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) { |
if (dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) { |
channel_eq = true; |
break; |
} |
|
/* Try 5 times */ |
if (tries > 5) { |
if (dp_info->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); |
dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set); |
|
tries++; |
radeon_dp_update_vs_emph(dp_info); |
dp_info->tries++; |
} |
|
if (!channel_eq) |
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) |
return -1; |
} else { |
DRM_DEBUG_KMS("channel eq at voltage %d pre-emphasis %d\n", |
dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, |
(dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) |
>> DP_TRAIN_PRE_EMPHASIS_SHIFT); |
|
/* disable the training pattern on the sink */ |
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); |
|
radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE, |
dig_connector->dp_clock, enc_id, 0); |
return 0; |
} |
} |
|
int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, |
uint8_t write_byte, uint8_t *read_byte) |
void radeon_dp_link_train(struct drm_encoder *encoder, |
struct drm_connector *connector) |
{ |
struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter; |
int ret = 0; |
uint16_t address = algo_data->address; |
uint8_t msg[5]; |
uint8_t reply[2]; |
int msg_len, dp_msg_len; |
int reply_bytes; |
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; |
struct radeon_dp_link_train_info dp_info; |
u8 tmp; |
|
/* Set up the command byte */ |
if (mode & MODE_I2C_READ) |
msg[2] = AUX_I2C_READ << 4; |
else |
msg[2] = AUX_I2C_WRITE << 4; |
if (!radeon_encoder->enc_priv) |
return; |
dig = radeon_encoder->enc_priv; |
|
if (!(mode & MODE_I2C_STOP)) |
msg[2] |= AUX_I2C_MOT << 4; |
radeon_connector = to_radeon_connector(connector); |
if (!radeon_connector->con_priv) |
return; |
dig_connector = radeon_connector->con_priv; |
|
msg[0] = address; |
msg[1] = address >> 8; |
if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) && |
(dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP)) |
return; |
|
reply_bytes = 1; |
dp_info.enc_id = 0; |
if (dig->dig_encoder) |
dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER; |
else |
dp_info.enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER; |
if (dig->linkb) |
dp_info.enc_id |= ATOM_DP_CONFIG_LINK_B; |
else |
dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A; |
|
msg_len = 4; |
dp_msg_len = 3; |
switch (mode) { |
case MODE_I2C_WRITE: |
msg[4] = write_byte; |
msg_len++; |
dp_msg_len += 2; |
break; |
case MODE_I2C_READ: |
dp_msg_len += 1; |
break; |
default: |
break; |
} |
dp_info.rd_interval = radeon_read_dpcd_reg(radeon_connector, DP_TRAINING_AUX_RD_INTERVAL); |
tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT); |
if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) |
dp_info.tp3_supported = true; |
else |
dp_info.tp3_supported = false; |
|
msg[3] = (dp_msg_len) << 4; |
ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0); |
memcpy(dp_info.dpcd, dig_connector->dpcd, 8); |
dp_info.rdev = rdev; |
dp_info.encoder = encoder; |
dp_info.connector = connector; |
dp_info.radeon_connector = radeon_connector; |
dp_info.dp_lane_count = dig_connector->dp_lane_count; |
dp_info.dp_clock = dig_connector->dp_clock; |
|
if (ret) { |
if (read_byte) |
*read_byte = reply[0]; |
return reply_bytes; |
if (radeon_dp_link_train_init(&dp_info)) |
goto done; |
if (radeon_dp_link_train_cr(&dp_info)) |
goto done; |
if (radeon_dp_link_train_ce(&dp_info)) |
goto done; |
done: |
if (radeon_dp_link_train_finish(&dp_info)) |
return; |
} |
return -EREMOTEIO; |
} |
|