From 746c1aa4d100f7441423050f34be79f401fbf7d4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 8 Dec 2009 07:07:28 +1000 Subject: drm/radeon/kms: initial radeon displayport porting This is enough to retrieve EDID and DPCP. Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 275 +++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 drivers/gpu/drm/radeon/atombios_dp.c (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c new file mode 100644 index 00000000000..a4bc8011338 --- /dev/null +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -0,0 +1,275 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon.h" + +#include "atom.h" +#include "atom-bits.h" +#include "drm_dp_helper.h" + +#define DP_LINK_STATUS_SIZE 6 + +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) +{ + struct drm_device *dev = chan->dev; + struct radeon_device *rdev = dev->dev_private; + PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); + unsigned char *base; + + memset(&args, 0, sizeof(args)); + + base = (unsigned char *)rdev->mode_info.atom_context->scratch; + + memcpy(base, req_bytes, num_bytes); + + args.lpAuxRequest = 0; + args.lpDataOut = 16; + args.ucDataOutLen = 0; + args.ucChannelID = chan->i2c_id; + args.ucDelay = delay; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + if (args.ucReplyStatus) { + DRM_ERROR("failed to get auxch %02x%02x %02x %02x 0x%02x %02x\n", + req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], + chan->i2c_id, args.ucReplyStatus); + return false; + } + + if (args.ucDataOutLen && read_byte && read_buf_len) { + if (read_buf_len < args.ucDataOutLen) { + DRM_ERROR("Buffer to small for return answer %d %d\n", + read_buf_len, args.ucDataOutLen); + return false; + } + { + int len = min(read_buf_len, args.ucDataOutLen); + memcpy(read_byte, base + 16, len); + } + } + return true; +} + +int radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, + uint8_t ucconfig, uint8_t lane_num) +{ + DP_ENCODER_SERVICE_PARAMETERS args; + int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); + + memset(&args, 0, sizeof(args)); + args.ucLinkClock = dp_clock / 10; + args.ucConfig = ucconfig; + args.ucAction = action; + args.ucLaneNum = lane_num; + args.ucStatus = 0; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return args.ucStatus; +} + +int radeon_dp_getsinktype(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + + return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0, + radeon_dig_connector->uc_i2c_id, 0); +} + +union dig_transmitter_control { + DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2; +}; + +bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address, + uint8_t send_bytes, uint8_t *send) +{ + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + u8 msg[20]; + u8 msg_len, dp_msg_len; + bool ret; + + 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); + + if (send_bytes > 16) + return false; + + memcpy(&msg[4], send, send_bytes); + msg_len = 4 + send_bytes; + ret = radeon_process_aux_ch(radeon_dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0); + return ret; +} + +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) +{ + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + 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; + + ret = radeon_process_aux_ch(radeon_dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay); + return ret; +} + +void radeon_dp_getdpcp(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + u8 msg[25]; + int ret; + + ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCP_REV, 0, 8, msg); + if (ret) { + memcpy(radeon_dig_connector->dpcp, msg, 8); + { + int i; + printk("DPCP: "); + for (i = 0; i < 8; i++) + printk("%02x ", msg[i]); + printk("\n"); + } + } + radeon_dig_connector->dpcp[0] = 0; + return; +} + +static bool atom_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) { + DRM_ERROR("displayport link status failed\n"); + return false; + } + + DRM_INFO("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; +} + +static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state) +{ + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + if (radeon_dig_connector->dpcp[0] >= 0x11) { + radeon_dp_aux_native_write(radeon_connector, 0x600, 1, + &power_state); + } +} + +static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector, + u8 train_set[4]) +{ + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + +// radeon_dp_digtransmitter_setup_vsemph(); + radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET, + 0/* lc */, train_set); +} + +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); +} + +int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, + uint8_t write_byte, uint8_t *read_byte) +{ + 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; + + /* Set up the command byte */ + if (mode & MODE_I2C_READ) + msg[2] = AUX_I2C_READ << 4; + else + msg[2] = AUX_I2C_WRITE << 4; + + if (!(mode & MODE_I2C_STOP)) + msg[2] |= AUX_I2C_MOT << 4; + + msg[0] = address; + msg[1] = address >> 8; + + reply_bytes = 1; + + 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; + } + + msg[3] = (dp_msg_len) << 4; + ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0); + + if (ret) { + if (read_byte) + *read_byte = reply[0]; + return reply_bytes; + } + return -EREMOTEIO; +} -- cgit v1.2.3 From 1a66c95a64c9ae0bc8382254f544b24b23f498ec Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Nov 2009 19:40:13 -0500 Subject: drm/radeon/kms: DP fixes and cleanup from the ddx - dpcp -> dpcd - fix up dig encoder routing - aux transaction table takes delay in 10 usec units Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index a4bc8011338..d1c144be973 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -34,7 +34,7 @@ #define DP_LINK_STATUS_SIZE 6 bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, - int num_bytes, u8 *read_byte, + int num_bytes, u8 *read_byte, u8 read_buf_len, u8 delay) { struct drm_device *dev = chan->dev; @@ -42,9 +42,9 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); unsigned char *base; - + memset(&args, 0, sizeof(args)); - + base = (unsigned char *)rdev->mode_info.atom_context->scratch; memcpy(base, req_bytes, num_bytes); @@ -53,7 +53,7 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, args.lpDataOut = 16; args.ucDataOutLen = 0; args.ucChannelID = chan->i2c_id; - args.ucDelay = delay; + args.ucDelay = delay / 10; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); @@ -158,24 +158,24 @@ bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16 return ret; } -void radeon_dp_getdpcp(struct radeon_connector *radeon_connector) +void radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; u8 msg[25]; int ret; - ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCP_REV, 0, 8, msg); + ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg); if (ret) { - memcpy(radeon_dig_connector->dpcp, msg, 8); - { + memcpy(radeon_dig_connector->dpcd, msg, 8); + { int i; - printk("DPCP: "); + printk("DPCD: "); for (i = 0; i < 8; i++) printk("%02x ", msg[i]); printk("\n"); } } - radeon_dig_connector->dpcp[0] = 0; + radeon_dig_connector->dpcd[0] = 0; return; } @@ -199,8 +199,8 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state) { struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - if (radeon_dig_connector->dpcp[0] >= 0x11) { - radeon_dp_aux_native_write(radeon_connector, 0x600, 1, + if (radeon_dig_connector->dpcd[0] >= 0x11) { + radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1, &power_state); } } -- cgit v1.2.3 From 6a93cb250a60af1bb7b4070949f8546a2fdc52ef Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 23 Nov 2009 17:39:28 -0500 Subject: drm/radeon/kms: i2c reorg - keep the atom i2c id in the i2c rec - fix gpio regs for GPIO and MDGPIO on pre-avivo chips - track whether the i2c line is hw capable - track whether the i2c line uses the multimedia i2c block Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index d1c144be973..fc5a2df4544 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -52,7 +52,7 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, args.lpAuxRequest = 0; args.lpDataOut = 16; args.ucDataOutLen = 0; - args.ucChannelID = chan->i2c_id; + args.ucChannelID = chan->rec.i2c_id; args.ucDelay = delay / 10; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); @@ -60,7 +60,7 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, if (args.ucReplyStatus) { DRM_ERROR("failed to get auxch %02x%02x %02x %02x 0x%02x %02x\n", req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], - chan->i2c_id, args.ucReplyStatus); + chan->rec.i2c_id, args.ucReplyStatus); return false; } @@ -102,7 +102,7 @@ int radeon_dp_getsinktype(struct radeon_connector *radeon_connector) struct radeon_device *rdev = dev->dev_private; return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0, - radeon_dig_connector->uc_i2c_id, 0); + radeon_dig_connector->dp_i2c_bus->rec.i2c_id, 0); } union dig_transmitter_control { -- cgit v1.2.3 From 4143e919ea999c9356ae4f71b5a3a80e075290d5 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 23 Nov 2009 18:02:35 -0500 Subject: drm/radeon/kms: store sink type in atom dig connector This will be used laster when the encoder and transmitters are set up. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index fc5a2df4544..e761fefaacb 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -78,8 +78,8 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, return true; } -int radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, - uint8_t ucconfig, uint8_t lane_num) +static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, + uint8_t ucconfig, uint8_t lane_num) { DP_ENCODER_SERVICE_PARAMETERS args; int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); @@ -95,7 +95,7 @@ int radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clo return args.ucStatus; } -int radeon_dp_getsinktype(struct radeon_connector *radeon_connector) +u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; struct drm_device *dev = radeon_connector->base.dev; -- cgit v1.2.3 From f92a8b6758bdc0f277c4f42aa7d736a205ac9ded Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 23 Nov 2009 18:40:40 -0500 Subject: drm/radeon/kms: handle dp sinks in atom encoder/transmitter tables Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index e761fefaacb..76eb5c8a701 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -33,6 +33,75 @@ #define DP_LINK_STATUS_SIZE 6 +/* move these to drm_dp_helper.c/h */ + +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 +}; + +static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int); + +int dp_lanes_for_mode_clock(int max_link_bw, int mode_clock) +{ + int i; + + switch (max_link_bw) { + case DP_LINK_BW_1_62: + default: + for (i = 0; i < num_dp_clocks; i++) { + if (i % 2) + continue; + if (dp_clocks[i] > mode_clock) { + if (i < 2) + return 1; + else if (i < 4) + return 2; + else + return 4; + } + } + break; + case DP_LINK_BW_2_7: + for (i = 0; i < num_dp_clocks; i++) { + if (dp_clocks[i] > mode_clock) { + if (i < 2) + return 1; + else if (i < 4) + return 2; + else + return 4; + } + } + break; + } + + return 0; +} + +int dp_link_clock_for_mode_clock(int max_link_bw, int mode_clock) +{ + int i; + + switch (max_link_bw) { + case DP_LINK_BW_1_62: + default: + return 162000; + break; + case DP_LINK_BW_2_7: + for (i = 0; i < num_dp_clocks; i++) { + if (dp_clocks[i] > mode_clock) + return (i % 2) ? 270000 : 162000; + } + } + + return 0; +} + 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) -- cgit v1.2.3 From 5801ead6bd6bddf5505d6eab55f84d8ee8106cd8 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 24 Nov 2009 13:32:59 -0500 Subject: drm/radeon/kms: add support for DP modesetting Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 541 +++++++++++++++++++++++++++++++---- 1 file changed, 488 insertions(+), 53 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 76eb5c8a701..ebaf3f8cd60 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -31,9 +31,20 @@ #include "atom-bits.h" #include "drm_dp_helper.h" -#define DP_LINK_STATUS_SIZE 6 - /* move these to drm_dp_helper.c/h */ +#define DP_LINK_CONFIGURATION_SIZE 9 +#define DP_LINK_STATUS_SIZE 6 +#define DP_DPCD_SIZE 8 + +static char *voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static char *pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; +static char *link_train_names[] = { + "pattern 1", "pattern 2", "idle", "off" +}; static const int dp_clocks[] = { 54000, // 1 lane, 1.62 Ghz @@ -46,9 +57,18 @@ static const int dp_clocks[] = { static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int); -int dp_lanes_for_mode_clock(int max_link_bw, int mode_clock) +/* common helper functions */ +static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock) { int i; + u8 max_link_bw; + u8 max_lane_count; + + if (!dpcd) + return 0; + + max_link_bw = dpcd[DP_MAX_LINK_RATE]; + max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; switch (max_link_bw) { case DP_LINK_BW_1_62: @@ -56,6 +76,19 @@ int dp_lanes_for_mode_clock(int max_link_bw, int mode_clock) for (i = 0; i < num_dp_clocks; i++) { if (i % 2) continue; + 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; @@ -68,6 +101,19 @@ int dp_lanes_for_mode_clock(int max_link_bw, int mode_clock) 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; @@ -83,17 +129,56 @@ int dp_lanes_for_mode_clock(int max_link_bw, int mode_clock) return 0; } -int dp_link_clock_for_mode_clock(int max_link_bw, int mode_clock) +static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock) { int i; + u8 max_link_bw; + u8 max_lane_count; + + if (!dpcd) + return 0; + + max_link_bw = dpcd[DP_MAX_LINK_RATE]; + max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; switch (max_link_bw) { case DP_LINK_BW_1_62: default: - return 162000; + for (i = 0; i < num_dp_clocks; i++) { + if (i % 2) + continue; + 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) + return 162000; + } 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) return (i % 2) ? 270000 : 162000; } @@ -102,6 +187,145 @@ int dp_link_clock_for_mode_clock(int max_link_bw, int mode_clock) return 0; } +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); + + 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]; +} + +static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + u8 l = dp_link_status(link_status, i); + return (l >> s) & 0xf; +} + +static bool dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return false; + } + return true; +} + +static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + u8 lane_align; + u8 lane_status; + int lane; + + lane_align = dp_link_status(link_status, + DP_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) + return false; + } + return true; +} + +static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) + +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + u8 l = dp_link_status(link_status, i); + + 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], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + u8 l = dp_link_status(link_status, i); + + 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; + } +} + +static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count, + u8 train_set[4]) +{ + u8 v = 0; + u8 p = 0; + int lane; + + for (lane = 0; lane < lane_count; lane++) { + u8 this_v = dp_get_adjust_request_voltage(link_status, lane); + u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane); + + DRM_INFO("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]); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= DP_VOLTAGE_MAX) + v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; + + if (p >= dp_pre_emphasis_max(v)) + p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + DRM_INFO("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]); + + for (lane = 0; lane < 4; lane++) + train_set[lane] = v | p; +} + + +/* 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) @@ -147,44 +371,10 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, return true; } -static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, - uint8_t ucconfig, uint8_t lane_num) -{ - DP_ENCODER_SERVICE_PARAMETERS args; - int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); - - memset(&args, 0, sizeof(args)); - args.ucLinkClock = dp_clock / 10; - args.ucConfig = ucconfig; - args.ucAction = action; - args.ucLaneNum = lane_num; - args.ucStatus = 0; - - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - return args.ucStatus; -} - -u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) -{ - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - struct drm_device *dev = radeon_connector->base.dev; - struct radeon_device *rdev = dev->dev_private; - - return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0, - radeon_dig_connector->dp_i2c_bus->rec.i2c_id, 0); -} - -union dig_transmitter_control { - DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1; - DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2; -}; - bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address, uint8_t send_bytes, uint8_t *send) { - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - struct drm_device *dev = radeon_connector->base.dev; - struct radeon_device *rdev = dev->dev_private; + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; u8 msg[20]; u8 msg_len, dp_msg_len; bool ret; @@ -201,7 +391,7 @@ bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint1 memcpy(&msg[4], send, send_bytes); msg_len = 4 + send_bytes; - ret = radeon_process_aux_ch(radeon_dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0); + ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0); return ret; } @@ -209,9 +399,7 @@ bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16 uint8_t delay, uint8_t expected_bytes, uint8_t *read_p) { - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - struct drm_device *dev = radeon_connector->base.dev; - struct radeon_device *rdev = dev->dev_private; + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; u8 msg[20]; u8 msg_len, dp_msg_len; bool ret = false; @@ -223,19 +411,47 @@ bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16 msg[3] = (dp_msg_len) << 4; msg[3] |= expected_bytes - 1; - ret = radeon_process_aux_ch(radeon_dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay); + ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay); return ret; } +/* 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) +{ + DP_ENCODER_SERVICE_PARAMETERS args; + int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); + + memset(&args, 0, sizeof(args)); + args.ucLinkClock = dp_clock / 10; + args.ucConfig = ucconfig; + args.ucAction = action; + args.ucLaneNum = lane_num; + args.ucStatus = 0; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return args.ucStatus; +} + +u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + + return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0, + dig_connector->dp_i2c_bus->rec.i2c_id, 0); +} + void radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; u8 msg[25]; int ret; ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg); if (ret) { - memcpy(radeon_dig_connector->dpcd, msg, 8); + memcpy(dig_connector->dpcd, msg, 8); { int i; printk("DPCD: "); @@ -244,10 +460,38 @@ void radeon_dp_getdpcd(struct radeon_connector *radeon_connector) printk("\n"); } } - radeon_dig_connector->dpcd[0] = 0; + dig_connector->dpcd[0] = 0; return; } +void radeon_dp_set_link_config(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct radeon_connector *radeon_connector; + struct radeon_connector_atom_dig *dig_connector; + + if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) + return; + + radeon_connector = to_radeon_connector(connector); + if (!radeon_connector->con_priv) + return; + dig_connector = radeon_connector->con_priv; + + dig_connector->dp_clock = + dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock); + dig_connector->dp_lane_count = + dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock); +} + +int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector, + struct drm_display_mode *mode) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + + return dp_mode_valid(dig_connector->dpcd, mode->clock); +} + static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, u8 link_status[DP_LINK_STATUS_SIZE]) { @@ -267,21 +511,41 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state) { - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - if (radeon_dig_connector->dpcd[0] >= 0x11) { + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + + if (dig_connector->dpcd[0] >= 0x11) { radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1, &power_state); } } +static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread) +{ + radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1, + &downspread); +} + +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); +} + static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector, + struct drm_encoder *encoder, u8 train_set[4]) { - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + int i; + + for (i = 0; i < dig_connector->dp_lane_count; i++) + atombios_dig_transmitter_setup(encoder, + ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, + i, train_set[i]); -// radeon_dp_digtransmitter_setup_vsemph(); radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET, - 0/* lc */, train_set); + dig_connector->dp_lane_count, train_set); } static void dp_set_training(struct radeon_connector *radeon_connector, @@ -291,6 +555,176 @@ static void dp_set_training(struct radeon_connector *radeon_connector, 1, &training); } +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) + 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 (ASIC_IS_DCE32(rdev)) { + if (dig->dig_block) + 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; + } else { + if (dig_connector->linkb) + enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER | ATOM_DP_CONFIG_LINK_B; + else + enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER | 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); + /* 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); + dp_update_dpvs_emph(radeon_connector, encoder, train_set); + udelay(400); + /* set training pattern 1 on the sink */ + dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1); + + /* 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_INFO("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 */ + 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_INFO("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); + + radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE, + dig_connector->dp_clock, enc_id, 0); +} + int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, uint8_t write_byte, uint8_t *read_byte) { @@ -342,3 +776,4 @@ int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, } return -EREMOTEIO; } + -- cgit v1.2.3 From 54d9cb47dd6a754e434e5adeccb3a1e2835594fd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 26 Nov 2009 08:49:17 +1000 Subject: drm/radeon/kms/dp: fix return in dpcd retrival. Not returning here caused us to get a display port version of 0 for everything this caused power up to not get sent which ends up in a black screen. Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index ebaf3f8cd60..65c82395c8e 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -459,6 +459,7 @@ void radeon_dp_getdpcd(struct radeon_connector *radeon_connector) printk("%02x ", msg[i]); printk("\n"); } + return; } dig_connector->dpcd[0] = 0; return; -- cgit v1.2.3 From 5fbfce7fc906c4a9e3d5e0872e5d6affaca54761 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 26 Nov 2009 08:55:18 +1000 Subject: drm/radeon/kms: make displayport work by reorganising vsemph setup. This fix reorganises the initial DP link training slightly, and actually makes DP work under kms here. Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 65c82395c8e..28741d40bf6 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -627,11 +627,12 @@ void dp_link_train(struct drm_encoder *encoder, /* set initial vs/emph */ memset(train_set, 0, 4); - dp_update_dpvs_emph(radeon_connector, encoder, train_set); 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; -- cgit v1.2.3 From e8696330e2a95e1b5872550dcf3ed04aecaf96b3 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 26 Nov 2009 08:57:23 +1000 Subject: drm/radeon/kms: drop unused array to fix warning. Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 28741d40bf6..e1cbd5049f8 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -42,9 +42,6 @@ static char *voltage_names[] = { static char *pre_emph_names[] = { "0dB", "3.5dB", "6dB", "9.5dB" }; -static char *link_train_names[] = { - "pattern 1", "pattern 2", "idle", "off" -}; static const int dp_clocks[] = { 54000, // 1 lane, 1.62 Ghz -- cgit v1.2.3 From 9fa05c98d69eb77c82e59b5e434ca63bba230ba0 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 27 Nov 2009 13:01:46 -0500 Subject: drm/radeon/kms: fix DP detect only return connected if there is actually a monitor connected. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index e1cbd5049f8..75977a46ba1 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -440,7 +440,7 @@ u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) dig_connector->dp_i2c_bus->rec.i2c_id, 0); } -void radeon_dp_getdpcd(struct radeon_connector *radeon_connector) +bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; u8 msg[25]; @@ -456,10 +456,10 @@ void radeon_dp_getdpcd(struct radeon_connector *radeon_connector) printk("%02x ", msg[i]); printk("\n"); } - return; + return true; } dig_connector->dpcd[0] = 0; - return; + return false; } void radeon_dp_set_link_config(struct drm_connector *connector, -- cgit v1.2.3 From 53c1e09fea4cf3fc0ec1f735a5fcab78c43cb55d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 27 Nov 2009 13:14:37 -0500 Subject: drm/radeon/kms: clean up DP debugging Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 56 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 75977a46ba1..784ba80afcb 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -44,12 +44,12 @@ static char *pre_emph_names[] = { }; 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 + 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 */ }; static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int); @@ -296,10 +296,10 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], u8 this_v = dp_get_adjust_request_voltage(link_status, lane); u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane); - DRM_INFO("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]); + DRM_DEBUG("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]); if (this_v > v) v = this_v; @@ -313,9 +313,9 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], if (p >= dp_pre_emphasis_max(v)) p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; - DRM_INFO("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]); + DRM_DEBUG("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]); for (lane = 0; lane < 4; lane++) train_set[lane] = v | p; @@ -348,7 +348,7 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); if (args.ucReplyStatus) { - DRM_ERROR("failed to get auxch %02x%02x %02x %02x 0x%02x %02x\n", + DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x\n", req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], chan->rec.i2c_id, args.ucReplyStatus); return false; @@ -451,10 +451,10 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) memcpy(dig_connector->dpcd, msg, 8); { int i; - printk("DPCD: "); + DRM_DEBUG("DPCD: "); for (i = 0; i < 8; i++) - printk("%02x ", msg[i]); - printk("\n"); + DRM_DEBUG("%02x ", msg[i]); + DRM_DEBUG("\n"); } return true; } @@ -501,9 +501,9 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, return false; } - DRM_INFO("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]); + DRM_DEBUG("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; } @@ -671,10 +671,10 @@ void dp_link_train(struct drm_encoder *encoder, if (!clock_recovery) DRM_ERROR("clock recovery failed\n"); else - DRM_INFO("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); + 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 */ @@ -712,10 +712,10 @@ void dp_link_train(struct drm_encoder *encoder, if (!channel_eq) DRM_ERROR("channel eq failed\n"); else - DRM_INFO("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); + 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); -- cgit v1.2.3 From d4877cf2293f5463f531769fd12300cb3417c778 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 4 Dec 2009 16:56:37 -0500 Subject: drm/radeon/kms: enable hpd support This enabled interrupt driven hpd support for all radeon chips. Assuming the hpd pin is wired up correctly, the driver will generate uevents on digital monitor connect and disconnect and retrain DP monitors automatically. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atombios_dp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/gpu/drm/radeon/atombios_dp.c') diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 784ba80afcb..0d63c4436e7 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -507,6 +507,18 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, return true; } +bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + u8 link_status[DP_LINK_STATUS_SIZE]; + + 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 dp_set_power(struct radeon_connector *radeon_connector, u8 power_state) { struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; -- cgit v1.2.3