diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2012-03-04 16:01:21 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2012-03-04 16:01:21 +0000 |
commit | fd076122ee1256ee925ccbea7b526c913c4ff089 (patch) | |
tree | c89b3fbb5ef98f3a4d619451ecad4efdd4767301 /driver/xf86-video-ati/src/atombios_output.c | |
parent | 737151a789a0028daa367ded1f192d5fb9fb108d (diff) |
Re-update xf86-video-ati to 6.14.3.
Because of ati.c revision 1.5, it will not be selected by
autoconfiguration code, so people who want to run this version of the
driver on their radeon cards need an explicit "Device" section in
xorg.conf.
Diffstat (limited to 'driver/xf86-video-ati/src/atombios_output.c')
-rw-r--r-- | driver/xf86-video-ati/src/atombios_output.c | 2026 |
1 files changed, 1578 insertions, 448 deletions
diff --git a/driver/xf86-video-ati/src/atombios_output.c b/driver/xf86-video-ati/src/atombios_output.c index 30ef6418b..bfaeb6ba3 100644 --- a/driver/xf86-video-ati/src/atombios_output.c +++ b/driver/xf86-video-ati/src/atombios_output.c @@ -34,12 +34,13 @@ #include "config.h" #endif /* DPMS */ -#ifdef HAVE_X11_EXTENSIONS_DPMSCONST_H +#ifdef HAVE_XEXTPROTO_71 #include <X11/extensions/dpmsconst.h> #else #define DPMS_SERVER #include <X11/extensions/dpms.h> #endif + #include <unistd.h> #include "radeon.h" @@ -64,6 +65,114 @@ const char *device_name[12] = { "DFP5", }; +#define AUX_NATIVE_WRITE 0x8 +#define AUX_NATIVE_READ 0x9 + +#define AUX_I2C_WRITE 0x0 +#define AUX_I2C_READ 0x1 +#define AUX_I2C_STATUS 0x2 +#define AUX_I2C_MOT 0x4 + +#define DP_DPCD_REV 0x0 +#define DP_MAX_LINK_RATE 0x1 +#define DP_MAX_LANE_COUNT 0x2 +#define DP_MAX_DOWNSPREAD 0x3 +#define DP_NORP 0x4 +#define DP_DOWNSTREAMPORT_PRESENT 0x5 +#define DP_MAIN_LINK_CHANNEL_CONFIG 0x6 +#define DP_DP11_DOWNSTREAM_PORT_COUNT 0x7 + +/* from intel i830_dp.h */ +#define DP_LINK_BW_SET 0x100 +//# define DP_LINK_BW_1_62 0x06 +//# define DP_LINK_BW_2_7 0x0a +#define DP_LANE_COUNT_SET 0x101 +# define DP_LANE_COUNT_MASK 0x0f +# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7) + +#define DP_TRAINING_PATTERN_SET 0x102 + +# define DP_TRAINING_PATTERN_DISABLE 0 +# define DP_TRAINING_PATTERN_1 1 +# define DP_TRAINING_PATTERN_2 2 +# define DP_TRAINING_PATTERN_MASK 0x3 + +# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2) +# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2) +# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2) +# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2) +# define DP_LINK_QUAL_PATTERN_MASK (3 << 2) +# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4) +# define DP_LINK_SCRAMBLING_DISABLE (1 << 5) + +# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6) +# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6) +# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6) +# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6) + +#define DP_TRAINING_LANE0_SET 0x103 +#define DP_TRAINING_LANE1_SET 0x104 +#define DP_TRAINING_LANE2_SET 0x105 +#define DP_TRAINING_LANE3_SET 0x106 +# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3 +# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0 +# define DP_TRAIN_MAX_SWING_REACHED (1 << 2) +# define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0) +# define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0) +# define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0) +# define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0) + +# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3) +# define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3) +# define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3) +# define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3) +# define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3) + +# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3 +# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5) +#define DP_DOWNSPREAD_CTRL 0x107 +# define DP_SPREAD_AMP_0_5 (1 << 4) + +#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 +# define DP_SET_ANSI_8B10B (1 << 0) + +#define DP_LANE0_1_STATUS 0x202 +#define DP_LANE2_3_STATUS 0x203 + +# define DP_LANE_CR_DONE (1 << 0) +# define DP_LANE_CHANNEL_EQ_DONE (1 << 1) +# define DP_LANE_SYMBOL_LOCKED (1 << 2) + +#define DP_LANE_ALIGN_STATUS_UPDATED 0x204 +#define DP_INTERLANE_ALIGN_DONE (1 << 0) +#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DP_LINK_STATUS_UPDATED (1 << 7) + +#define DP_SINK_STATUS 0x205 + +#define DP_RECEIVE_PORT_0_STATUS (1 << 0) +#define DP_RECEIVE_PORT_1_STATUS (1 << 1) + +#define DP_ADJUST_REQUEST_LANE0_1 0x206 +#define DP_ADJUST_REQUEST_LANE2_3 0x207 + +#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03 +#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0 +#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c +#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2 +#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30 +#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4 +#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 +#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 + +#define DP_LINK_STATUS_SIZE 6 +#define DP_LINK_CONFIGURATION_SIZE 9 + +#define DP_SET_POWER_D0 0x1 +#define DP_SET_POWER_D3 0x2 + +static void do_displayport_link_train(xf86OutputPtr output); + static int atombios_output_dac_setup(xf86OutputPtr output, int action) { @@ -407,7 +516,7 @@ atombios_maybe_hdmi_mode(xf86OutputPtr output) { #ifndef EDID_COMPLETE_RAWDATA /* there's no getting this right unless we have complete EDID */ - return ATOM_ENCODER_MODE_HDMI; + return ATOM_ENCODER_MODE_DVI; #else if (output && xf86MonitorIsHDMI(output->MonInfo)) return ATOM_ENCODER_MODE_HDMI; @@ -419,6 +528,8 @@ atombios_maybe_hdmi_mode(xf86OutputPtr output) int atombios_get_encoder_mode(xf86OutputPtr output) { + ScrnInfoPtr pScrn = output->scrn; + RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; /* DVI should really be atombios_maybe_hdmi_mode() as well */ @@ -435,16 +546,24 @@ atombios_get_encoder_mode(xf86OutputPtr output) break; case CONNECTOR_HDMI_TYPE_A: case CONNECTOR_HDMI_TYPE_B: - return atombios_maybe_hdmi_mode(output); + if (IS_DCE4_VARIANT) + return ATOM_ENCODER_MODE_DVI; + else + return atombios_maybe_hdmi_mode(output); break; case CONNECTOR_LVDS: return ATOM_ENCODER_MODE_LVDS; break; case CONNECTOR_DISPLAY_PORT: + case CONNECTOR_EDP: if (radeon_output->MonType == MT_DP) return ATOM_ENCODER_MODE_DP; - else - return atombios_maybe_hdmi_mode(output); + else { + if (IS_DCE4_VARIANT) + return ATOM_ENCODER_MODE_DVI; + else + return atombios_maybe_hdmi_mode(output); + } break; case CONNECTOR_DVI_A: case CONNECTOR_VGA: @@ -463,128 +582,243 @@ atombios_get_encoder_mode(xf86OutputPtr output) } static const int dp_clocks[] = { - 16200, - 27000, - 32400, - 54000, - 0, - 0, - 64800, - 108000, + 5400, // 1 lane, 1.62 Ghz + 9000, // 1 lane, 2.70 Ghz + 10800, // 2 lane, 1.62 Ghz + 18000, // 2 lane, 2.70 Ghz + 21600, // 4 lane, 1.62 Ghz + 36000, // 4 lane, 2.70 Ghz }; static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int); +# define DP_LINK_BW_1_62 0x06 +# define DP_LINK_BW_2_7 0x0a +static int radeon_dp_max_lane_count(xf86OutputPtr output); + static int -dp_lanes_for_mode_clock(int mode_clock) +dp_lanes_for_mode_clock(xf86OutputPtr output, int mode_clock) { + RADEONOutputPrivatePtr radeon_output = output->driver_private; int i; - - for (i = 0; i < num_dp_clocks; i++) - if (dp_clocks[i] > (mode_clock / 10)) - return (i / 2) + 1; + int max_link_bw = radeon_output->dpcd[1]; + int max_lane_count = radeon_dp_max_lane_count(output); + + 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; + break; + case 2: + if (i > 3) + return 0; + break; + case 4: + default: + break; + } + if (dp_clocks[i] > (mode_clock/10)) { + 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++) { + 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/10)) { + if (i < 2) + return 1; + else if (i < 4) + return 2; + else + return 4; + } + } + break; + } return 0; } static int -dp_link_clock_for_mode_clock(int mode_clock) +dp_link_clock_for_mode_clock(xf86OutputPtr output, int mode_clock) { + RADEONOutputPrivatePtr radeon_output = output->driver_private; int i; + int max_link_bw = radeon_output->dpcd[1]; + int max_lane_count = radeon_dp_max_lane_count(output); - for (i = 0; i < num_dp_clocks; i++) - if (dp_clocks[i] > (mode_clock / 10)) - return (dp_clocks[i % 2]); + 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; + break; + case 2: + if (i > 3) + return 0; + break; + case 4: + default: + break; + } + if (dp_clocks[i] > (mode_clock/10)) + return 16200; + } + 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/10)) + return (i % 2) ? 27000 : 16200; + } + break; + } return 0; } +/* + * DIG Encoder/Transmitter Setup + * + * DCE 3.0/3.1 + * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA. + * Supports up to 3 digital outputs + * - 2 DIG encoder blocks. + * DIG1 can drive UNIPHY link A or link B + * DIG2 can drive UNIPHY link B or LVTMA + * + * DCE 3.2 + * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B). + * Supports up to 5 digital outputs + * - 2 DIG encoder blocks. + * DIG1/2 can drive UNIPHY0/1/2 link A or link B + * + * DCE 4.0 + * - 3 DIG transmitter blocks UNPHY0/1/2 (links A and B). + * Supports up to 6 digital outputs + * - 6 DIG encoder blocks. + * - DIG to PHY mapping is hardcoded + * DIG1 drives UNIPHY0 link A, A+B + * DIG2 drives UNIPHY0 link B + * DIG3 drives UNIPHY1 link A, A+B + * DIG4 drives UNIPHY1 link B + * DIG5 drives UNIPHY2 link A, A+B + * DIG6 drives UNIPHY2 link B + * + * Routing + * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links) + * Examples: + * crtc0 -> dig2 -> LVTMA links A+B + * crtc1 -> dig1 -> UNIPHY0 link B + * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS + * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI + */ + +union dig_encoder_control { + DIG_ENCODER_CONTROL_PS_ALLOCATION v1; + DIG_ENCODER_CONTROL_PARAMETERS_V2 v2; + DIG_ENCODER_CONTROL_PARAMETERS_V3 v3; +}; + static int atombios_output_dig_encoder_setup(xf86OutputPtr output, int action) { RADEONOutputPrivatePtr radeon_output = output->driver_private; RADEONInfoPtr info = RADEONPTR(output->scrn); radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output); - DIG_ENCODER_CONTROL_PS_ALLOCATION disp_data; + union dig_encoder_control disp_data; AtomBiosArgRec data; unsigned char *space; - int index = 0, major, minor, num = 0; + int index = 0, major, minor; int clock = radeon_output->pixel_clock; - int dig_block = radeon_output->dig_block; if (radeon_encoder == NULL) return ATOM_NOT_IMPLEMENTED; memset(&disp_data,0, sizeof(disp_data)); - if (IS_DCE32_VARIANT) { - if (dig_block) - index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl); - else - index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); - num = dig_block + 1; - } else { - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); - num = 1; - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl); - num = 2; - break; - } - } + if (IS_DCE4_VARIANT) + index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl); + else if (radeon_output->dig_encoder) + index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl); + else + index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); atombios_get_command_table_version(info->atomBIOS, index, &major, &minor); - disp_data.ucAction = action; - disp_data.usPixelClock = cpu_to_le16(clock / 10); + disp_data.v1.ucAction = action; + disp_data.v1.usPixelClock = cpu_to_le16(clock / 10); + disp_data.v1.ucEncoderMode = atombios_get_encoder_mode(output); + + if (disp_data.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) { + if (dp_link_clock_for_mode_clock(output, clock) == 27000) + disp_data.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; + disp_data.v1.ucLaneNum = dp_lanes_for_mode_clock(output, clock); + } else if (clock > 165000) + disp_data.v1.ucLaneNum = 8; + else + disp_data.v1.ucLaneNum = 4; - if (IS_DCE32_VARIANT) { + if (IS_DCE4_VARIANT) { + disp_data.v3.acConfig.ucDigSel = radeon_output->dig_encoder; + disp_data.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR; + } else { switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - disp_data.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1; + disp_data.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - disp_data.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + disp_data.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - disp_data.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3; + disp_data.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3; break; } - } else { - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - disp_data.ucConfig = ATOM_ENCODER_CONFIG_UNIPHY; - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - disp_data.ucConfig = ATOM_ENCODER_CONFIG_LVTMA; - break; - } - } - - disp_data.ucEncoderMode = atombios_get_encoder_mode(output); - - if (disp_data.ucEncoderMode == ATOM_ENCODER_MODE_DP) { if (radeon_output->linkb) - disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; + disp_data.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; else - disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; - - if (dp_link_clock_for_mode_clock(clock) == 27000) - disp_data.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; - - disp_data.ucLaneNum = dp_lanes_for_mode_clock(clock); - } else if (clock > 165000) { - disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B; - disp_data.ucLaneNum = 8; - } else { - if (radeon_output->linkb) - disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; - else - disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; - - disp_data.ucLaneNum = 4; + disp_data.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; } data.exec.index = index; @@ -592,11 +826,11 @@ atombios_output_dig_encoder_setup(xf86OutputPtr output, int action) data.exec.pspace = &disp_data; if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) { - ErrorF("Output DIG%d encoder setup success\n", num); + ErrorF("Output DIG%d encoder setup success\n", radeon_output->dig_encoder); return ATOM_SUCCESS; } - ErrorF("Output DIG%d setup failed\n", num); + ErrorF("Output DIG%d setup failed\n", radeon_output->dig_encoder); return ATOM_NOT_IMPLEMENTED; } @@ -604,10 +838,11 @@ atombios_output_dig_encoder_setup(xf86OutputPtr output, int action) union dig_transmitter_control { DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1; DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3; }; static int -atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action) +atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action, uint8_t lane_num, uint8_t lane_set) { RADEONOutputPrivatePtr radeon_output = output->driver_private; RADEONInfoPtr info = RADEONPTR(output->scrn); @@ -618,14 +853,13 @@ atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action) int index = 0, num = 0; int major, minor; int clock = radeon_output->pixel_clock; - int dig_block = radeon_output->dig_block; if (radeon_encoder == NULL) return ATOM_NOT_IMPLEMENTED; memset(&disp_data,0, sizeof(disp_data)); - if (IS_DCE32_VARIANT) + if (IS_DCE32_VARIANT || IS_DCE4_VARIANT) index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); else { switch (radeon_encoder->encoder_id) { @@ -641,21 +875,70 @@ atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action) atombios_get_command_table_version(info->atomBIOS, index, &major, &minor); disp_data.v1.ucAction = action; + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + disp_data.v1.usInitInfo = radeon_output->connector_object_id; + } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { + disp_data.v1.asMode.ucLaneSel = lane_num; + disp_data.v1.asMode.ucLaneSet = lane_set; + } else { + if (radeon_output->MonType == MT_DP) + disp_data.v1.usPixelClock = + cpu_to_le16(dp_link_clock_for_mode_clock(output, clock)); + else if (clock > 165000) + disp_data.v1.usPixelClock = cpu_to_le16((clock / 2) / 10); + else + disp_data.v1.usPixelClock = cpu_to_le16(clock / 10); + } - if (IS_DCE32_VARIANT) { - if (radeon_output->MonType == MT_DP) { - disp_data.v2.usPixelClock = - cpu_to_le16(dp_link_clock_for_mode_clock(clock)); - disp_data.v2.acConfig.fDPConnector = 1; - } else if (clock > 165000) { - disp_data.v2.usPixelClock = cpu_to_le16((clock * 10 * 2) / 100); - disp_data.v2.acConfig.fDualLinkConnector = 1; - } else { - disp_data.v2.usPixelClock = cpu_to_le16((clock * 10 * 4) / 100); + if (IS_DCE4_VARIANT) { + if (radeon_output->MonType == MT_DP) + disp_data.v3.ucLaneNum = dp_lanes_for_mode_clock(output, clock); + else if (clock > 165000) + disp_data.v3.ucLaneNum = 8; + else + disp_data.v3.ucLaneNum = 4; + + if (radeon_output->linkb) { + disp_data.v3.acConfig.ucLinkSel = 1; + disp_data.v2.acConfig.ucEncoderSel = 1; } - if (dig_block) + + // select the PLL for the UNIPHY + if (radeon_output->MonType == MT_DP && info->dp_extclk) + disp_data.v3.acConfig.ucRefClkSource = 2; /* ext clk */ + else + disp_data.v3.acConfig.ucRefClkSource = radeon_output->pll_id; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + disp_data.v3.acConfig.ucTransmitterSel = 0; + num = 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + disp_data.v3.acConfig.ucTransmitterSel = 1; + num = 1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + disp_data.v3.acConfig.ucTransmitterSel = 2; + num = 2; + break; + } + + if (radeon_output->MonType == MT_DP) + disp_data.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */ + else if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) { + if (radeon_output->coherent_mode) + disp_data.v3.acConfig.fCoherentMode = 1; + if (clock > 165000) + disp_data.v3.acConfig.fDualLinkConnector = 1; + } + } else if (IS_DCE32_VARIANT) { + if (radeon_output->dig_encoder) disp_data.v2.acConfig.ucEncoderSel = 1; + if (radeon_output->linkb) + disp_data.v2.acConfig.ucLinkSel = 1; + switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: disp_data.v2.acConfig.ucTransmitterSel = 0; @@ -671,36 +954,31 @@ atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action) break; } - if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) { - if (radeon_output->coherent_mode) { + if (radeon_output->MonType == MT_DP) + disp_data.v2.acConfig.fCoherentMode = 1; /* DP requires coherent */ + else if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) { + if (radeon_output->coherent_mode) disp_data.v2.acConfig.fCoherentMode = 1; - xf86DrvMsg(output->scrn->scrnIndex, X_INFO, "UNIPHY%d transmitter: Coherent Mode enabled\n",disp_data.v2.acConfig.ucTransmitterSel); - } else - xf86DrvMsg(output->scrn->scrnIndex, X_INFO, "UNIPHY%d transmitter: Coherent Mode disabled\n",disp_data.v2.acConfig.ucTransmitterSel); + if (clock > 165000) + disp_data.v2.acConfig.fDualLinkConnector = 1; } } else { disp_data.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL; - if (radeon_output->MonType == MT_DP) - disp_data.v1.usPixelClock = - cpu_to_le16(dp_link_clock_for_mode_clock(clock)); + if (radeon_output->dig_encoder) + disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER; else - disp_data.v1.usPixelClock = cpu_to_le16((clock) / 10); + disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER; switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER; if (info->IsIGP) { if (clock > 165000) { - disp_data.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK | - ATOM_TRANSMITTER_CONFIG_LINKA_B); - if (radeon_output->igp_lane_info & 0x3) disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7; else if (radeon_output->igp_lane_info & 0xc) disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15; } else { - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA; if (radeon_output->igp_lane_info & 0x1) disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3; else if (radeon_output->igp_lane_info & 0x2) @@ -710,46 +988,21 @@ atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action) else if (radeon_output->igp_lane_info & 0x8) disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15; } - } else { - if (clock > 165000) - disp_data.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK | - ATOM_TRANSMITTER_CONFIG_LINKA_B | - ATOM_TRANSMITTER_CONFIG_LANE_0_7); - else { - /* XXX */ - if (radeon_output->linkb) - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3; - else - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3; - } - } - break; - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER; - if (clock > 165000) - disp_data.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK | - ATOM_TRANSMITTER_CONFIG_LINKA_B | - ATOM_TRANSMITTER_CONFIG_LANE_0_7); - else { - /* XXX */ - if (radeon_output->linkb) - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3; - else - disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3; } break; } + if (radeon_output->linkb) + disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB; + else + disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA; - if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) { - if (radeon_output->coherent_mode && - radeon_output->MonType != MT_DP) { + if (radeon_output->MonType == MT_DP) + disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; /* DP requires coherent */ + else if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) { + if (radeon_output->coherent_mode) disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; - xf86DrvMsg(output->scrn->scrnIndex, X_INFO, - "DIG%d transmitter: Coherent Mode enabled\n", num); - } else { - xf86DrvMsg(output->scrn->scrnIndex, X_INFO, - "DIG%d transmitter: Coherent Mode disabled\n", num); - } + if (clock > 165000) + disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK; } } @@ -770,234 +1023,238 @@ atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action) } -static void atom_rv515_force_tv_scaler(ScrnInfoPtr pScrn) +static void atom_rv515_force_tv_scaler(ScrnInfoPtr pScrn, RADEONCrtcPrivatePtr radeon_crtc) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; - - OUTREG(0x659C,0x0); - OUTREG(0x6594,0x705); - OUTREG(0x65A4,0x10001); - OUTREG(0x65D8,0x0); - OUTREG(0x65B0,0x0); - OUTREG(0x65C0,0x0); - OUTREG(0x65D4,0x0); - OUTREG(0x6578,0x0); - OUTREG(0x657C,0x841880A8); - OUTREG(0x6578,0x1); - OUTREG(0x657C,0x84208680); - OUTREG(0x6578,0x2); - OUTREG(0x657C,0xBFF880B0); - OUTREG(0x6578,0x100); - OUTREG(0x657C,0x83D88088); - OUTREG(0x6578,0x101); - OUTREG(0x657C,0x84608680); - OUTREG(0x6578,0x102); - OUTREG(0x657C,0xBFF080D0); - OUTREG(0x6578,0x200); - OUTREG(0x657C,0x83988068); - OUTREG(0x6578,0x201); - OUTREG(0x657C,0x84A08680); - OUTREG(0x6578,0x202); - OUTREG(0x657C,0xBFF080F8); - OUTREG(0x6578,0x300); - OUTREG(0x657C,0x83588058); - OUTREG(0x6578,0x301); - OUTREG(0x657C,0x84E08660); - OUTREG(0x6578,0x302); - OUTREG(0x657C,0xBFF88120); - OUTREG(0x6578,0x400); - OUTREG(0x657C,0x83188040); - OUTREG(0x6578,0x401); - OUTREG(0x657C,0x85008660); - OUTREG(0x6578,0x402); - OUTREG(0x657C,0xBFF88150); - OUTREG(0x6578,0x500); - OUTREG(0x657C,0x82D88030); - OUTREG(0x6578,0x501); - OUTREG(0x657C,0x85408640); - OUTREG(0x6578,0x502); - OUTREG(0x657C,0xBFF88180); - OUTREG(0x6578,0x600); - OUTREG(0x657C,0x82A08018); - OUTREG(0x6578,0x601); - OUTREG(0x657C,0x85808620); - OUTREG(0x6578,0x602); - OUTREG(0x657C,0xBFF081B8); - OUTREG(0x6578,0x700); - OUTREG(0x657C,0x82608010); - OUTREG(0x6578,0x701); - OUTREG(0x657C,0x85A08600); - OUTREG(0x6578,0x702); - OUTREG(0x657C,0x800081F0); - OUTREG(0x6578,0x800); - OUTREG(0x657C,0x8228BFF8); - OUTREG(0x6578,0x801); - OUTREG(0x657C,0x85E085E0); - OUTREG(0x6578,0x802); - OUTREG(0x657C,0xBFF88228); - OUTREG(0x6578,0x10000); - OUTREG(0x657C,0x82A8BF00); - OUTREG(0x6578,0x10001); - OUTREG(0x657C,0x82A08CC0); - OUTREG(0x6578,0x10002); - OUTREG(0x657C,0x8008BEF8); - OUTREG(0x6578,0x10100); - OUTREG(0x657C,0x81F0BF28); - OUTREG(0x6578,0x10101); - OUTREG(0x657C,0x83608CA0); - OUTREG(0x6578,0x10102); - OUTREG(0x657C,0x8018BED0); - OUTREG(0x6578,0x10200); - OUTREG(0x657C,0x8148BF38); - OUTREG(0x6578,0x10201); - OUTREG(0x657C,0x84408C80); - OUTREG(0x6578,0x10202); - OUTREG(0x657C,0x8008BEB8); - OUTREG(0x6578,0x10300); - OUTREG(0x657C,0x80B0BF78); - OUTREG(0x6578,0x10301); - OUTREG(0x657C,0x85008C20); - OUTREG(0x6578,0x10302); - OUTREG(0x657C,0x8020BEA0); - OUTREG(0x6578,0x10400); - OUTREG(0x657C,0x8028BF90); - OUTREG(0x6578,0x10401); - OUTREG(0x657C,0x85E08BC0); - OUTREG(0x6578,0x10402); - OUTREG(0x657C,0x8018BE90); - OUTREG(0x6578,0x10500); - OUTREG(0x657C,0xBFB8BFB0); - OUTREG(0x6578,0x10501); - OUTREG(0x657C,0x86C08B40); - OUTREG(0x6578,0x10502); - OUTREG(0x657C,0x8010BE90); - OUTREG(0x6578,0x10600); - OUTREG(0x657C,0xBF58BFC8); - OUTREG(0x6578,0x10601); - OUTREG(0x657C,0x87A08AA0); - OUTREG(0x6578,0x10602); - OUTREG(0x657C,0x8010BE98); - OUTREG(0x6578,0x10700); - OUTREG(0x657C,0xBF10BFF0); - OUTREG(0x6578,0x10701); - OUTREG(0x657C,0x886089E0); - OUTREG(0x6578,0x10702); - OUTREG(0x657C,0x8018BEB0); - OUTREG(0x6578,0x10800); - OUTREG(0x657C,0xBED8BFE8); - OUTREG(0x6578,0x10801); - OUTREG(0x657C,0x89408940); - OUTREG(0x6578,0x10802); - OUTREG(0x657C,0xBFE8BED8); - OUTREG(0x6578,0x20000); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x20001); - OUTREG(0x657C,0x90008000); - OUTREG(0x6578,0x20002); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x20003); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x20100); - OUTREG(0x657C,0x80108000); - OUTREG(0x6578,0x20101); - OUTREG(0x657C,0x8FE0BF70); - OUTREG(0x6578,0x20102); - OUTREG(0x657C,0xBFE880C0); - OUTREG(0x6578,0x20103); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x20200); - OUTREG(0x657C,0x8018BFF8); - OUTREG(0x6578,0x20201); - OUTREG(0x657C,0x8F80BF08); - OUTREG(0x6578,0x20202); - OUTREG(0x657C,0xBFD081A0); - OUTREG(0x6578,0x20203); - OUTREG(0x657C,0xBFF88000); - OUTREG(0x6578,0x20300); - OUTREG(0x657C,0x80188000); - OUTREG(0x6578,0x20301); - OUTREG(0x657C,0x8EE0BEC0); - OUTREG(0x6578,0x20302); - OUTREG(0x657C,0xBFB082A0); - OUTREG(0x6578,0x20303); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x20400); - OUTREG(0x657C,0x80188000); - OUTREG(0x6578,0x20401); - OUTREG(0x657C,0x8E00BEA0); - OUTREG(0x6578,0x20402); - OUTREG(0x657C,0xBF8883C0); - OUTREG(0x6578,0x20403); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x20500); - OUTREG(0x657C,0x80188000); - OUTREG(0x6578,0x20501); - OUTREG(0x657C,0x8D00BE90); - OUTREG(0x6578,0x20502); - OUTREG(0x657C,0xBF588500); - OUTREG(0x6578,0x20503); - OUTREG(0x657C,0x80008008); - OUTREG(0x6578,0x20600); - OUTREG(0x657C,0x80188000); - OUTREG(0x6578,0x20601); - OUTREG(0x657C,0x8BC0BE98); - OUTREG(0x6578,0x20602); - OUTREG(0x657C,0xBF308660); - OUTREG(0x6578,0x20603); - OUTREG(0x657C,0x80008008); - OUTREG(0x6578,0x20700); - OUTREG(0x657C,0x80108000); - OUTREG(0x6578,0x20701); - OUTREG(0x657C,0x8A80BEB0); - OUTREG(0x6578,0x20702); - OUTREG(0x657C,0xBF0087C0); - OUTREG(0x6578,0x20703); - OUTREG(0x657C,0x80008008); - OUTREG(0x6578,0x20800); - OUTREG(0x657C,0x80108000); - OUTREG(0x6578,0x20801); - OUTREG(0x657C,0x8920BED0); - OUTREG(0x6578,0x20802); - OUTREG(0x657C,0xBED08920); - OUTREG(0x6578,0x20803); - OUTREG(0x657C,0x80008010); - OUTREG(0x6578,0x30000); - OUTREG(0x657C,0x90008000); - OUTREG(0x6578,0x30001); - OUTREG(0x657C,0x80008000); - OUTREG(0x6578,0x30100); - OUTREG(0x657C,0x8FE0BF90); - OUTREG(0x6578,0x30101); - OUTREG(0x657C,0xBFF880A0); - OUTREG(0x6578,0x30200); - OUTREG(0x657C,0x8F60BF40); - OUTREG(0x6578,0x30201); - OUTREG(0x657C,0xBFE88180); - OUTREG(0x6578,0x30300); - OUTREG(0x657C,0x8EC0BF00); - OUTREG(0x6578,0x30301); - OUTREG(0x657C,0xBFC88280); - OUTREG(0x6578,0x30400); - OUTREG(0x657C,0x8DE0BEE0); - OUTREG(0x6578,0x30401); - OUTREG(0x657C,0xBFA083A0); - OUTREG(0x6578,0x30500); - OUTREG(0x657C,0x8CE0BED0); - OUTREG(0x6578,0x30501); - OUTREG(0x657C,0xBF7884E0); - OUTREG(0x6578,0x30600); - OUTREG(0x657C,0x8BA0BED8); - OUTREG(0x6578,0x30601); - OUTREG(0x657C,0xBF508640); - OUTREG(0x6578,0x30700); - OUTREG(0x657C,0x8A60BEE8); - OUTREG(0x6578,0x30701); - OUTREG(0x657C,0xBF2087A0); - OUTREG(0x6578,0x30800); - OUTREG(0x657C,0x8900BF00); - OUTREG(0x6578,0x30801); - OUTREG(0x657C,0xBF008900); + int index_reg = 0x6578, data_reg = 0x657c; + + index_reg += radeon_crtc->crtc_offset; + data_reg += radeon_crtc->crtc_offset; + + OUTREG(0x659C + radeon_crtc->crtc_offset, 0x0); + OUTREG(0x6594 + radeon_crtc->crtc_offset, 0x705); + OUTREG(0x65A4 + radeon_crtc->crtc_offset, 0x10001); + OUTREG(0x65D8 + radeon_crtc->crtc_offset, 0x0); + OUTREG(0x65B0 + radeon_crtc->crtc_offset, 0x0); + OUTREG(0x65C0 + radeon_crtc->crtc_offset, 0x0); + OUTREG(0x65D4 + radeon_crtc->crtc_offset, 0x0); + OUTREG(index_reg,0x0); + OUTREG(data_reg,0x841880A8); + OUTREG(index_reg,0x1); + OUTREG(data_reg,0x84208680); + OUTREG(index_reg,0x2); + OUTREG(data_reg,0xBFF880B0); + OUTREG(index_reg,0x100); + OUTREG(data_reg,0x83D88088); + OUTREG(index_reg,0x101); + OUTREG(data_reg,0x84608680); + OUTREG(index_reg,0x102); + OUTREG(data_reg,0xBFF080D0); + OUTREG(index_reg,0x200); + OUTREG(data_reg,0x83988068); + OUTREG(index_reg,0x201); + OUTREG(data_reg,0x84A08680); + OUTREG(index_reg,0x202); + OUTREG(data_reg,0xBFF080F8); + OUTREG(index_reg,0x300); + OUTREG(data_reg,0x83588058); + OUTREG(index_reg,0x301); + OUTREG(data_reg,0x84E08660); + OUTREG(index_reg,0x302); + OUTREG(data_reg,0xBFF88120); + OUTREG(index_reg,0x400); + OUTREG(data_reg,0x83188040); + OUTREG(index_reg,0x401); + OUTREG(data_reg,0x85008660); + OUTREG(index_reg,0x402); + OUTREG(data_reg,0xBFF88150); + OUTREG(index_reg,0x500); + OUTREG(data_reg,0x82D88030); + OUTREG(index_reg,0x501); + OUTREG(data_reg,0x85408640); + OUTREG(index_reg,0x502); + OUTREG(data_reg,0xBFF88180); + OUTREG(index_reg,0x600); + OUTREG(data_reg,0x82A08018); + OUTREG(index_reg,0x601); + OUTREG(data_reg,0x85808620); + OUTREG(index_reg,0x602); + OUTREG(data_reg,0xBFF081B8); + OUTREG(index_reg,0x700); + OUTREG(data_reg,0x82608010); + OUTREG(index_reg,0x701); + OUTREG(data_reg,0x85A08600); + OUTREG(index_reg,0x702); + OUTREG(data_reg,0x800081F0); + OUTREG(index_reg,0x800); + OUTREG(data_reg,0x8228BFF8); + OUTREG(index_reg,0x801); + OUTREG(data_reg,0x85E085E0); + OUTREG(index_reg,0x802); + OUTREG(data_reg,0xBFF88228); + OUTREG(index_reg,0x10000); + OUTREG(data_reg,0x82A8BF00); + OUTREG(index_reg,0x10001); + OUTREG(data_reg,0x82A08CC0); + OUTREG(index_reg,0x10002); + OUTREG(data_reg,0x8008BEF8); + OUTREG(index_reg,0x10100); + OUTREG(data_reg,0x81F0BF28); + OUTREG(index_reg,0x10101); + OUTREG(data_reg,0x83608CA0); + OUTREG(index_reg,0x10102); + OUTREG(data_reg,0x8018BED0); + OUTREG(index_reg,0x10200); + OUTREG(data_reg,0x8148BF38); + OUTREG(index_reg,0x10201); + OUTREG(data_reg,0x84408C80); + OUTREG(index_reg,0x10202); + OUTREG(data_reg,0x8008BEB8); + OUTREG(index_reg,0x10300); + OUTREG(data_reg,0x80B0BF78); + OUTREG(index_reg,0x10301); + OUTREG(data_reg,0x85008C20); + OUTREG(index_reg,0x10302); + OUTREG(data_reg,0x8020BEA0); + OUTREG(index_reg,0x10400); + OUTREG(data_reg,0x8028BF90); + OUTREG(index_reg,0x10401); + OUTREG(data_reg,0x85E08BC0); + OUTREG(index_reg,0x10402); + OUTREG(data_reg,0x8018BE90); + OUTREG(index_reg,0x10500); + OUTREG(data_reg,0xBFB8BFB0); + OUTREG(index_reg,0x10501); + OUTREG(data_reg,0x86C08B40); + OUTREG(index_reg,0x10502); + OUTREG(data_reg,0x8010BE90); + OUTREG(index_reg,0x10600); + OUTREG(data_reg,0xBF58BFC8); + OUTREG(index_reg,0x10601); + OUTREG(data_reg,0x87A08AA0); + OUTREG(index_reg,0x10602); + OUTREG(data_reg,0x8010BE98); + OUTREG(index_reg,0x10700); + OUTREG(data_reg,0xBF10BFF0); + OUTREG(index_reg,0x10701); + OUTREG(data_reg,0x886089E0); + OUTREG(index_reg,0x10702); + OUTREG(data_reg,0x8018BEB0); + OUTREG(index_reg,0x10800); + OUTREG(data_reg,0xBED8BFE8); + OUTREG(index_reg,0x10801); + OUTREG(data_reg,0x89408940); + OUTREG(index_reg,0x10802); + OUTREG(data_reg,0xBFE8BED8); + OUTREG(index_reg,0x20000); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x20001); + OUTREG(data_reg,0x90008000); + OUTREG(index_reg,0x20002); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x20003); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x20100); + OUTREG(data_reg,0x80108000); + OUTREG(index_reg,0x20101); + OUTREG(data_reg,0x8FE0BF70); + OUTREG(index_reg,0x20102); + OUTREG(data_reg,0xBFE880C0); + OUTREG(index_reg,0x20103); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x20200); + OUTREG(data_reg,0x8018BFF8); + OUTREG(index_reg,0x20201); + OUTREG(data_reg,0x8F80BF08); + OUTREG(index_reg,0x20202); + OUTREG(data_reg,0xBFD081A0); + OUTREG(index_reg,0x20203); + OUTREG(data_reg,0xBFF88000); + OUTREG(index_reg,0x20300); + OUTREG(data_reg,0x80188000); + OUTREG(index_reg,0x20301); + OUTREG(data_reg,0x8EE0BEC0); + OUTREG(index_reg,0x20302); + OUTREG(data_reg,0xBFB082A0); + OUTREG(index_reg,0x20303); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x20400); + OUTREG(data_reg,0x80188000); + OUTREG(index_reg,0x20401); + OUTREG(data_reg,0x8E00BEA0); + OUTREG(index_reg,0x20402); + OUTREG(data_reg,0xBF8883C0); + OUTREG(index_reg,0x20403); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x20500); + OUTREG(data_reg,0x80188000); + OUTREG(index_reg,0x20501); + OUTREG(data_reg,0x8D00BE90); + OUTREG(index_reg,0x20502); + OUTREG(data_reg,0xBF588500); + OUTREG(index_reg,0x20503); + OUTREG(data_reg,0x80008008); + OUTREG(index_reg,0x20600); + OUTREG(data_reg,0x80188000); + OUTREG(index_reg,0x20601); + OUTREG(data_reg,0x8BC0BE98); + OUTREG(index_reg,0x20602); + OUTREG(data_reg,0xBF308660); + OUTREG(index_reg,0x20603); + OUTREG(data_reg,0x80008008); + OUTREG(index_reg,0x20700); + OUTREG(data_reg,0x80108000); + OUTREG(index_reg,0x20701); + OUTREG(data_reg,0x8A80BEB0); + OUTREG(index_reg,0x20702); + OUTREG(data_reg,0xBF0087C0); + OUTREG(index_reg,0x20703); + OUTREG(data_reg,0x80008008); + OUTREG(index_reg,0x20800); + OUTREG(data_reg,0x80108000); + OUTREG(index_reg,0x20801); + OUTREG(data_reg,0x8920BED0); + OUTREG(index_reg,0x20802); + OUTREG(data_reg,0xBED08920); + OUTREG(index_reg,0x20803); + OUTREG(data_reg,0x80008010); + OUTREG(index_reg,0x30000); + OUTREG(data_reg,0x90008000); + OUTREG(index_reg,0x30001); + OUTREG(data_reg,0x80008000); + OUTREG(index_reg,0x30100); + OUTREG(data_reg,0x8FE0BF90); + OUTREG(index_reg,0x30101); + OUTREG(data_reg,0xBFF880A0); + OUTREG(index_reg,0x30200); + OUTREG(data_reg,0x8F60BF40); + OUTREG(index_reg,0x30201); + OUTREG(data_reg,0xBFE88180); + OUTREG(index_reg,0x30300); + OUTREG(data_reg,0x8EC0BF00); + OUTREG(index_reg,0x30301); + OUTREG(data_reg,0xBFC88280); + OUTREG(index_reg,0x30400); + OUTREG(data_reg,0x8DE0BEE0); + OUTREG(index_reg,0x30401); + OUTREG(data_reg,0xBFA083A0); + OUTREG(index_reg,0x30500); + OUTREG(data_reg,0x8CE0BED0); + OUTREG(index_reg,0x30501); + OUTREG(data_reg,0xBF7884E0); + OUTREG(index_reg,0x30600); + OUTREG(data_reg,0x8BA0BED8); + OUTREG(index_reg,0x30601); + OUTREG(data_reg,0xBF508640); + OUTREG(index_reg,0x30700); + OUTREG(data_reg,0x8A60BEE8); + OUTREG(index_reg,0x30701); + OUTREG(data_reg,0xBF2087A0); + OUTREG(index_reg,0x30800); + OUTREG(data_reg,0x8900BF00); + OUTREG(index_reg,0x30801); + OUTREG(data_reg,0xBF008900); } static int @@ -1186,7 +1443,7 @@ atombios_output_scaler_setup(xf86OutputPtr output) if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT) && info->ChipFamily >= CHIP_FAMILY_RV515 && info->ChipFamily <= CHIP_FAMILY_RV570) { ErrorF("forcing TV scaler\n"); - atom_rv515_force_tv_scaler(output->scrn); + atom_rv515_force_tv_scaler(output->scrn, radeon_crtc); } ErrorF("scaler %d setup success\n", radeon_crtc->crtc_id); return ATOM_SUCCESS; @@ -1208,6 +1465,8 @@ atombios_output_dpms(xf86OutputPtr output, int mode) unsigned char *space; int index = 0; Bool is_dig = FALSE; + unsigned char *RADEONMMIO = info->MMIO; + uint32_t reg = 0; if (radeon_encoder == NULL) return; @@ -1239,51 +1498,83 @@ atombios_output_dpms(xf86OutputPtr output, int mode) break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); - else + if (IS_DCE32_VARIANT) index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); + else { + if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); + else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); + else + index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); + } break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) - index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); - else + if (IS_DCE32_VARIANT) index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); + else { + if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); + else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); + else + index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); + } break; } switch (mode) { case DPMSModeOn: radeon_encoder->devices |= radeon_output->active_device; - if (is_dig) - atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT); + if (is_dig) { + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); + if (((radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) || + (radeon_output->ConnectorType == CONNECTOR_EDP)) && + (radeon_output->MonType == MT_DP)) { + do_displayport_link_train(output); + if (IS_DCE4_VARIANT) + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_VIDEO_ON); + } + } else { disp_data.ucAction = ATOM_ENABLE; data.exec.index = index; data.exec.dataSpace = (void *)&space; data.exec.pspace = &disp_data; + /* workaround for DVOOutputControl on some RS690 systems */ + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) { + reg = INREG(RADEON_BIOS_3_SCRATCH); + OUTREG(RADEON_BIOS_3_SCRATCH, reg & ~ATOM_S3_DFP2I_ACTIVE); + } if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) ErrorF("Output %s enable success\n", device_name[radeon_get_device_index(radeon_output->active_device)]); else ErrorF("Output %s enable failed\n", device_name[radeon_get_device_index(radeon_output->active_device)]); + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) + OUTREG(RADEON_BIOS_3_SCRATCH, reg); } + /* at least for TV atom fails to reassociate the correct crtc source at dpms on */ + if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) + atombios_set_output_crtc_source(output); break; case DPMSModeStandby: case DPMSModeSuspend: case DPMSModeOff: radeon_encoder->devices &= ~(radeon_output->active_device); if (!radeon_encoder->devices) { - if (is_dig) - atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT); - else { + if (is_dig) { + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); + if (((radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) || + (radeon_output->ConnectorType == CONNECTOR_EDP)) && + (radeon_output->MonType == MT_DP)) { + if (IS_DCE4_VARIANT) + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_VIDEO_OFF); + } + } else { disp_data.ucAction = ATOM_DISABLE; data.exec.index = index; data.exec.dataSpace = (void *)&space; @@ -1302,7 +1593,12 @@ atombios_output_dpms(xf86OutputPtr output, int mode) } } -static void +union crtc_source_param { + SELECT_CRTC_SOURCE_PS_ALLOCATION v1; + SELECT_CRTC_SOURCE_PARAMETERS_V2 v2; +}; + +void atombios_set_output_crtc_source(xf86OutputPtr output) { RADEONOutputPrivatePtr radeon_output = output->driver_private; @@ -1311,16 +1607,15 @@ atombios_set_output_crtc_source(xf86OutputPtr output) radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output); AtomBiosArgRec data; unsigned char *space; - SELECT_CRTC_SOURCE_PS_ALLOCATION crtc_src_param; - SELECT_CRTC_SOURCE_PARAMETERS_V2 crtc_src_param2; + union crtc_source_param args; int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source); int major, minor; if (radeon_encoder == NULL) return; - memset(&crtc_src_param, 0, sizeof(crtc_src_param)); - memset(&crtc_src_param2, 0, sizeof(crtc_src_param2)); + memset(&args, 0, sizeof(args)); + atombios_get_command_table_version(info->atomBIOS, index, &major, &minor); /*ErrorF("select crtc source table is %d %d\n", major, minor);*/ @@ -1332,89 +1627,101 @@ atombios_set_output_crtc_source(xf86OutputPtr output) case 1: default: if (IS_AVIVO_VARIANT) - crtc_src_param.ucCRTC = radeon_crtc->crtc_id; + args.v1.ucCRTC = radeon_crtc->crtc_id; else { if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) - crtc_src_param.ucCRTC = radeon_crtc->crtc_id; + args.v1.ucCRTC = radeon_crtc->crtc_id; else - crtc_src_param.ucCRTC = radeon_crtc->crtc_id << 2; + args.v1.ucCRTC = radeon_crtc->crtc_id << 2; } switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - crtc_src_param.ucDevice = ATOM_DEVICE_DFP1_INDEX; + args.v1.ucDevice = ATOM_DEVICE_DFP1_INDEX; break; case ENCODER_OBJECT_ID_INTERNAL_LVDS: case ENCODER_OBJECT_ID_INTERNAL_LVTM1: if (radeon_output->active_device & ATOM_DEVICE_LCD1_SUPPORT) - crtc_src_param.ucDevice = ATOM_DEVICE_LCD1_INDEX; + args.v1.ucDevice = ATOM_DEVICE_LCD1_INDEX; else - crtc_src_param.ucDevice = ATOM_DEVICE_DFP3_INDEX; + args.v1.ucDevice = ATOM_DEVICE_DFP3_INDEX; break; case ENCODER_OBJECT_ID_INTERNAL_DVO1: case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - crtc_src_param.ucDevice = ATOM_DEVICE_DFP2_INDEX; + args.v1.ucDevice = ATOM_DEVICE_DFP2_INDEX; break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) - crtc_src_param.ucDevice = ATOM_DEVICE_TV1_INDEX; + args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) - crtc_src_param.ucDevice = ATOM_DEVICE_CV_INDEX; + args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; else - crtc_src_param.ucDevice = ATOM_DEVICE_CRT1_INDEX; + args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX; break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) - crtc_src_param.ucDevice = ATOM_DEVICE_TV1_INDEX; + args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) - crtc_src_param.ucDevice = ATOM_DEVICE_CV_INDEX; + args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; else - crtc_src_param.ucDevice = ATOM_DEVICE_CRT2_INDEX; + args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX; break; } - data.exec.pspace = &crtc_src_param; - /*ErrorF("device sourced: 0x%x\n", crtc_src_param.ucDevice);*/ + /*ErrorF("device sourced: 0x%x\n", args.v1.ucDevice);*/ break; case 2: - crtc_src_param2.ucCRTC = radeon_crtc->crtc_id; - crtc_src_param2.ucEncodeMode = atombios_get_encoder_mode(output); + args.v2.ucCRTC = radeon_crtc->crtc_id; + args.v2.ucEncodeMode = atombios_get_encoder_mode(output); switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - if (IS_DCE32_VARIANT) { - if (radeon_crtc->crtc_id) - crtc_src_param2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; - else - crtc_src_param2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; - } else - crtc_src_param2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; - break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - crtc_src_param2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; + switch (radeon_output->dig_encoder) { + case 0: + args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; + break; + case 1: + args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; + break; + case 2: + args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID; + break; + case 3: + args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID; + break; + case 4: + args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID; + break; + case 5: + args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID; + break; + } + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID; break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) - crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) - crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; else - crtc_src_param2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID; + args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID; break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) - crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) - crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; else - crtc_src_param2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID; + args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID; break; } - data.exec.pspace = &crtc_src_param2; - /*ErrorF("device sourced: 0x%x\n", crtc_src_param2.ucEncoderID);*/ + /*ErrorF("device sourced: 0x%x\n", args.v2.ucEncoderID);*/ break; } break; @@ -1423,6 +1730,7 @@ atombios_set_output_crtc_source(xf86OutputPtr output) exit(-1); } + data.exec.pspace = &args; data.exec.index = index; data.exec.dataSpace = (void *)&space; @@ -1460,29 +1768,128 @@ atombios_apply_output_quirks(xf86OutputPtr output, DisplayModePtr mode) } /* set scaler clears this on some chips */ - if (IS_AVIVO_VARIANT && (mode->Flags & V_INTERLACE)) - OUTREG(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, AVIVO_D1MODE_INTERLEAVE_EN); + if (!(radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))) { + if (IS_AVIVO_VARIANT && (mode->Flags & V_INTERLACE)) + OUTREG(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, AVIVO_D1MODE_INTERLEAVE_EN); + } + + if (IS_DCE32_VARIANT && + (!IS_DCE4_VARIANT) && + (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT))) { + radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output); + if (radeon_encoder == NULL) + return; + /* XXX: need to sort out why transmitter control table sometimes sets this to a + * different golden value. + */ + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY2) { + OUTREG(0x7ec4, 0x00824002); + } + } } void +atombios_pick_dig_encoder(xf86OutputPtr output) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(output->scrn); + RADEONOutputPrivatePtr radeon_output = output->driver_private; + RADEONInfoPtr info = RADEONPTR(output->scrn); + radeon_encoder_ptr radeon_encoder = NULL; + Bool is_lvtma = FALSE; + int i, mode; + uint32_t dig_enc_use_mask = 0; + + /* non digital encoders don't need a dig block */ + mode = atombios_get_encoder_mode(output); + if (mode == ATOM_ENCODER_MODE_CRT || + mode == ATOM_ENCODER_MODE_TV || + mode == ATOM_ENCODER_MODE_CV) + return; + + if (IS_DCE4_VARIANT) { + radeon_encoder = radeon_get_encoder(output); + + if (IS_DCE41_VARIANT) { + if (radeon_output->linkb) + radeon_output->dig_encoder = 1; + else + radeon_output->dig_encoder = 0; + } else { + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + if (radeon_output->linkb) + radeon_output->dig_encoder = 1; + else + radeon_output->dig_encoder = 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + if (radeon_output->linkb) + radeon_output->dig_encoder = 3; + else + radeon_output->dig_encoder = 2; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + if (radeon_output->linkb) + radeon_output->dig_encoder = 5; + else + radeon_output->dig_encoder = 4; + break; + default: + ErrorF("Unknown encoder\n"); + break; + } + } + return; + } + + if (IS_DCE32_VARIANT) { + RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private; + radeon_output->dig_encoder = radeon_crtc->crtc_id; + return; + } + + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr test = xf86_config->output[i]; + RADEONOutputPrivatePtr radeon_test = test->driver_private; + radeon_encoder = radeon_get_encoder(test); + + if (!radeon_encoder || !test->crtc) + continue; + + if (output == test && radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) + is_lvtma = TRUE; + if (output != test && (radeon_test->dig_encoder >= 0)) + dig_enc_use_mask |= (1 << radeon_test->dig_encoder); + + } + if (is_lvtma) { + if (dig_enc_use_mask & 0x2) + ErrorF("Need digital encoder 2 for LVTMA and it isn't free - stealing\n"); + radeon_output->dig_encoder = 1; + return; + } + if (!(dig_enc_use_mask & 1)) + radeon_output->dig_encoder = 0; + else + radeon_output->dig_encoder = 1; +} +void atombios_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) { RADEONOutputPrivatePtr radeon_output = output->driver_private; - RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private; radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output); RADEONInfoPtr info = RADEONPTR(output->scrn); if (radeon_encoder == NULL) - return; + return; radeon_output->pixel_clock = adjusted_mode->Clock; - radeon_output->dig_block = radeon_crtc->crtc_id; atombios_output_overscan_setup(output, mode, adjusted_mode); atombios_output_scaler_setup(output); atombios_set_output_crtc_source(output); - if (IS_AVIVO_VARIANT) { + if (IS_AVIVO_VARIANT && !IS_DCE4_VARIANT) { if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT)) atombios_output_yuv_setup(output, TRUE); else @@ -1501,13 +1908,21 @@ atombios_output_mode_set(xf86OutputPtr output, case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: /* disable encoder and transmitter */ - atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE); - atombios_output_dig_encoder_setup(output, ATOM_DISABLE); - /* setup and enable the encoder and transmitter */ - atombios_output_dig_encoder_setup(output, ATOM_ENABLE); - atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_SETUP); - atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE); + if (IS_DCE4_VARIANT) { + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_SETUP); + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_INIT, 0, 0); + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); + } else { + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + atombios_output_dig_encoder_setup(output, ATOM_DISABLE); + atombios_output_dig_encoder_setup(output, ATOM_ENABLE); + + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_INIT, 0, 0); + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0); + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); + } break; case ENCODER_OBJECT_ID_INTERNAL_DDI: atombios_output_ddia_setup(output, ATOM_ENABLE); @@ -1521,8 +1936,12 @@ atombios_output_mode_set(xf86OutputPtr output, case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: atombios_output_dac_setup(output, ATOM_ENABLE); - if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) - atombios_output_tv_setup(output, ATOM_ENABLE); + if (radeon_output->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) { + if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) + atombios_output_tv_setup(output, ATOM_ENABLE); + else + atombios_output_tv_setup(output, ATOM_DISABLE); + } break; } atombios_apply_output_quirks(output, adjusted_mode); @@ -1604,7 +2023,7 @@ atombios_dac_detect(xf86OutputPtr output) RADEONOutputPrivatePtr radeon_output = output->driver_private; RADEONMonitorType MonType = MT_NONE; AtomBiosResult ret; - uint32_t bios_0_scratch; + RADEONSavePtr save = info->ModeReg; if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) { if (xf86ReturnOptValBool(info->Options, OPTION_FORCE_TVOUT, FALSE)) { @@ -1618,24 +2037,24 @@ atombios_dac_detect(xf86OutputPtr output) ret = atom_bios_dac_load_detect(info->atomBIOS, output); if (ret == ATOM_SUCCESS) { if (info->ChipFamily >= CHIP_FAMILY_R600) - bios_0_scratch = INREG(R600_BIOS_0_SCRATCH); + save->bios_0_scratch = INREG(R600_BIOS_0_SCRATCH); else - bios_0_scratch = INREG(RADEON_BIOS_0_SCRATCH); - /*ErrorF("DAC connect %08X\n", (unsigned int)bios_0_scratch);*/ + save->bios_0_scratch = INREG(RADEON_BIOS_0_SCRATCH); + /*ErrorF("DAC connect %08X\n", (unsigned int)save->bios_0_scratch);*/ if (radeon_output->devices & ATOM_DEVICE_CRT1_SUPPORT) { - if (bios_0_scratch & ATOM_S0_CRT1_MASK) + if (save->bios_0_scratch & ATOM_S0_CRT1_MASK) MonType = MT_CRT; } else if (radeon_output->devices & ATOM_DEVICE_CRT2_SUPPORT) { - if (bios_0_scratch & ATOM_S0_CRT2_MASK) + if (save->bios_0_scratch & ATOM_S0_CRT2_MASK) MonType = MT_CRT; } else if (radeon_output->devices & ATOM_DEVICE_CV_SUPPORT) { - if (bios_0_scratch & (ATOM_S0_CV_MASK | ATOM_S0_CV_MASK_A)) + if (save->bios_0_scratch & (ATOM_S0_CV_MASK | ATOM_S0_CV_MASK_A)) MonType = MT_CV; } else if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) { - if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) + if (save->bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) MonType = MT_CTV; - else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) + else if (save->bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) MonType = MT_STV; } } @@ -1643,3 +2062,714 @@ atombios_dac_detect(xf86OutputPtr output) return MonType; } + +static inline int atom_dp_get_encoder_id(xf86OutputPtr output) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + int ret = 0; + if (radeon_output->dig_encoder) + ret |= ATOM_DP_CONFIG_DIG2_ENCODER; + else + ret |= ATOM_DP_CONFIG_DIG1_ENCODER; + if (radeon_output->linkb) + ret |= ATOM_DP_CONFIG_LINK_B; + else + ret |= ATOM_DP_CONFIG_LINK_A; + return ret; +} + +union aux_channel_transaction { + PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; + PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; +}; + +Bool +RADEONProcessAuxCH(xf86OutputPtr output, uint8_t *req_bytes, uint8_t num_bytes, + uint8_t *read_byte, uint8_t read_buf_len, uint8_t delay) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + RADEONInfoPtr info = RADEONPTR(output->scrn); + union aux_channel_transaction args; + AtomBiosArgRec data; + unsigned char *space; + unsigned char *base; + int retry_count = 0; + + memset(&args, 0, sizeof(args)); + if (info->atomBIOS->fbBase) + base = info->FB + info->atomBIOS->fbBase; + else if (info->atomBIOS->scratchBase) + base = (unsigned char *)info->atomBIOS->scratchBase; + else + return FALSE; + +retry: + memcpy(base, req_bytes, num_bytes); + + args.v1.lpAuxRequest = 0; + args.v1.lpDataOut = 16; + args.v1.ucDataOutLen = 0; + args.v1.ucChannelID = radeon_output->ucI2cId; + args.v1.ucDelay = delay / 10; /* 10 usec */ + if (IS_DCE4_VARIANT) + args.v2.ucHPD_ID = radeon_output->hpd_id; + + data.exec.index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); + data.exec.dataSpace = (void *)&space; + data.exec.pspace = &args; + + RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data); + if (args.v1.ucReplyStatus && !args.v1.ucDataOutLen) { + if (args.v1.ucReplyStatus == 0x20 && retry_count++ < 10) + goto retry; + ErrorF("failed to get auxch %02x%02x %02x %02x %02x after %d retries\n", + req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], args.v1.ucReplyStatus, retry_count); + return FALSE; + } + if (args.v1.ucDataOutLen && read_byte && read_buf_len) { + if (read_buf_len < args.v1.ucDataOutLen) { + ErrorF("%s: Buffer too small for return answer %d %d\n", __func__, read_buf_len, args.v1.ucDataOutLen); + return FALSE; + } + { + int len = read_buf_len < args.v1.ucDataOutLen ? read_buf_len : args.v1.ucDataOutLen; + memcpy(read_byte, base+16, len); + } + } + return TRUE; +} + +static int +RADEONDPEncoderService(xf86OutputPtr output, int action, uint8_t ucconfig, uint8_t lane_num) +{ + RADEONInfoPtr info = RADEONPTR(output->scrn); + DP_ENCODER_SERVICE_PARAMETERS args; + AtomBiosArgRec data; + unsigned char *space; + + memset(&args, 0, sizeof(args)); + + args.ucLinkClock = 0; + args.ucConfig = ucconfig; + args.ucAction = action; + args.ucLaneNum = lane_num; + args.ucStatus = 0; + + data.exec.index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); + data.exec.dataSpace = (void *)&space; + data.exec.pspace = &args; + + RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data); + + ErrorF("%s: %d %d\n", __func__, action, args.ucStatus); + return args.ucStatus; +} + +int RADEON_DP_GetSinkType(xf86OutputPtr output) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + + return RADEONDPEncoderService(output, ATOM_DP_ACTION_GET_SINK_TYPE, radeon_output->ucI2cId, 0); +} + +static Bool atom_dp_aux_native_write(xf86OutputPtr output, uint16_t address, + uint8_t send_bytes, uint8_t *send) +{ + uint8_t msg[20]; + uint8_t msg_len, dp_msg_len; + int 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 (0) + ErrorF("writing %02x %02x %02x, %d, %d\n", msg[0], msg[1], msg[3], send_bytes, dp_msg_len); + if (send_bytes > 16) + return FALSE; + + memcpy(&msg[4], send, send_bytes); + msg_len = 4 + send_bytes; + ret = RADEONProcessAuxCH(output, msg, msg_len, NULL, 0, 0); + return ret; +} + +static Bool atom_dp_aux_native_read(xf86OutputPtr output, uint16_t address, + uint8_t delay, + uint8_t expected_bytes, uint8_t *read_p) +{ + uint8_t msg[20]; + uint8_t msg_len, dp_msg_len; + int ret; + + 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; + + if (0) + ErrorF("reading %02x %02x %02x, %d, %d\n", msg[0], msg[1], msg[3], expected_bytes, dp_msg_len); + ret = RADEONProcessAuxCH(output, msg, msg_len, read_p, expected_bytes, delay); + return ret; +} + +/* fill out the DPCD structure */ +void RADEON_DP_GetDPCD(xf86OutputPtr output) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + uint8_t msg[25]; + int ret; + + ret = atom_dp_aux_native_read(output, DP_DPCD_REV, 0, 8, msg); + if (ret) { + memcpy(radeon_output->dpcd, msg, 8); + if (0) { + int i; + ErrorF("DPCD: "); + for (i = 0; i < 8; i++) + ErrorF("%02x ", radeon_output->dpcd[i]); + ErrorF("\n"); + } + ret = atom_dp_aux_native_read(output, DP_LINK_BW_SET, 0, 2, msg); + if (0) { + ErrorF("0x200: %02x %02x\n", msg[0], msg[1]); + } + return; + } + radeon_output->dpcd[0] = 0; + return; +} + + +enum dp_aux_i2c_mode { + dp_aux_i2c_start, + dp_aux_i2c_write, + dp_aux_i2c_read, + dp_aux_i2c_stop, +}; + + +static Bool atom_dp_aux_i2c_transaction(xf86OutputPtr output, uint16_t address, + enum dp_aux_i2c_mode mode, + uint8_t write_byte, uint8_t *read_byte) +{ + uint8_t msg[8], msg_len, dp_msg_len; + int ret; + int auxch_cmd = 0; + + memset(msg, 0, 8); + + if (mode != dp_aux_i2c_stop) + auxch_cmd = AUX_I2C_MOT; + + if (address & 1) + auxch_cmd |= AUX_I2C_READ; + else + auxch_cmd |= AUX_I2C_WRITE; + + msg[2] = auxch_cmd << 4; + + msg[4] = 0; + msg[0] = (address >> 1); + msg[1] = (address >> 9); + + msg_len = 4; + dp_msg_len = 3; + switch (mode) { + case dp_aux_i2c_read: + /* bottom bits is byte count - 1 so for 1 byte == 0 */ + dp_msg_len += 1; + break; + case dp_aux_i2c_write: + dp_msg_len += 2; + msg[4] = write_byte; + msg_len++; + break; + default: + break; + } + msg[3] = dp_msg_len << 4; + + ret = RADEONProcessAuxCH(output, msg, msg_len, read_byte, 1, 0); + return ret; +} + +static Bool +atom_dp_i2c_address(I2CDevPtr dev, I2CSlaveAddr addr) +{ + I2CBusPtr bus = dev->pI2CBus; + xf86OutputPtr output = bus->DriverPrivate.ptr; + RADEONOutputPrivatePtr radeon_output = output->driver_private; + int ret; + + radeon_output->dp_i2c_addr = addr; + radeon_output->dp_i2c_running = TRUE; + + /* call i2c start */ + ret = atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr, + dp_aux_i2c_start, 0, NULL); + + return ret; +} +static Bool +atom_dp_i2c_start(I2CBusPtr bus, int timeout) +{ + ErrorF("%s\n", __func__); + return TRUE; +} + +static void +atom_dp_i2c_stop(I2CDevPtr dev) +{ + I2CBusPtr bus = dev->pI2CBus; + xf86OutputPtr output = bus->DriverPrivate.ptr; + RADEONOutputPrivatePtr radeon_output = output->driver_private; + + if (radeon_output->dp_i2c_running) + atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr, + dp_aux_i2c_stop, 0, NULL); + radeon_output->dp_i2c_running = FALSE; +} + + +static Bool +atom_dp_i2c_put_byte(I2CDevPtr dev, I2CByte byte) +{ + I2CBusPtr bus = dev->pI2CBus; + xf86OutputPtr output = bus->DriverPrivate.ptr; + RADEONOutputPrivatePtr radeon_output = output->driver_private; + Bool ret; + + ret = (atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr, + dp_aux_i2c_write, byte, NULL)); + return ret; +} + +static Bool +atom_dp_i2c_get_byte(I2CDevPtr dev, I2CByte *byte_ret, Bool last) +{ + I2CBusPtr bus = dev->pI2CBus; + xf86OutputPtr output = bus->DriverPrivate.ptr; + RADEONOutputPrivatePtr radeon_output = output->driver_private; + Bool ret; + + ret = (atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr, + dp_aux_i2c_read, 0, byte_ret)); + return ret; +} + +Bool +RADEON_DP_I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, char *name, xf86OutputPtr output) +{ + I2CBusPtr pI2CBus; + + pI2CBus = xf86CreateI2CBusRec(); + if (!pI2CBus) return FALSE; + + pI2CBus->BusName = name; + pI2CBus->scrnIndex = pScrn->scrnIndex; + pI2CBus->I2CGetByte = atom_dp_i2c_get_byte; + pI2CBus->I2CPutByte = atom_dp_i2c_put_byte; + pI2CBus->I2CAddress = atom_dp_i2c_address; + pI2CBus->I2CStart = atom_dp_i2c_start; + pI2CBus->I2CStop = atom_dp_i2c_stop; + pI2CBus->DriverPrivate.ptr = output; + + /* + * These were set incorrectly in the server pre-1.3, Having + * duplicate settings is sub-optimal, but this lets the driver + * work with older servers + */ + pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ + pI2CBus->StartTimeout = 550; + pI2CBus->BitTimeout = 40; + pI2CBus->AcknTimeout = 40; + pI2CBus->RiseFallTime = 20; + + if (!xf86I2CBusInit(pI2CBus)) + return FALSE; + + *bus_ptr = pI2CBus; + return TRUE; +} + + +static uint8_t dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int r) +{ + return link_status[r - DP_LANE0_1_STATUS]; +} + +static uint8_t dp_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + uint8_t l = dp_link_status(link_status, i); + return (l >> s) & 0xf; +} + +static Bool dp_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +{ + int lane; + + uint8_t 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; +} + + +/* Check to see if channel eq is done on all channels */ +#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\ + DP_LANE_CHANNEL_EQ_DONE|\ + DP_LANE_SYMBOL_LOCKED) +static Bool +dp_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +{ + uint8_t lane_align; + uint8_t 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 & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) + return FALSE; + } + return TRUE; +} + +/* + * Fetch AUX CH registers 0x202 - 0x207 which contain + * link status information + */ +static Bool +atom_dp_get_link_status(xf86OutputPtr output, + uint8_t link_status[DP_LINK_STATUS_SIZE]) +{ + ScrnInfoPtr pScrn = output->scrn; + int ret; + ret = atom_dp_aux_native_read(output, DP_LANE0_1_STATUS, 100, + DP_LINK_STATUS_SIZE, link_status); + if (!ret) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "dp link status failed\n"); + return FALSE; + } + ErrorF("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 uint8_t +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); + uint8_t l = dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static uint8_t +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); + uint8_t l = dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +static char *voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static char *pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; + +/* + * These are source-specific values; current Intel hardware supports + * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB + */ +#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 + +static uint8_t +dp_pre_emphasis_max(uint8_t 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_set_training(xf86OutputPtr output, uint8_t training) +{ + atom_dp_aux_native_write(output, DP_TRAINING_PATTERN_SET, 1, &training); +} + +static void dp_set_power(xf86OutputPtr output, uint8_t power_state) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + + if (radeon_output->dpcd[0] >= 0x11) { + atom_dp_aux_native_write(output, 0x600, 1, &power_state); + } +} + +static void +dp_get_adjust_train(xf86OutputPtr output, + uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane_count, + uint8_t train_set[4]) +{ + ScrnInfoPtr pScrn = output->scrn; + uint8_t v = 0; + uint8_t p = 0; + int lane; + + for (lane = 0; lane < lane_count; lane++) { + uint8_t this_v = dp_get_adjust_request_voltage(link_status, lane); + uint8_t this_p = dp_get_adjust_request_pre_emphasis(link_status, lane); + + if (0) { + xf86DrvMsg(pScrn->scrnIndex, X_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; + + if (0) { + xf86DrvMsg(pScrn->scrnIndex, X_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; +} + +static int radeon_dp_max_lane_count(xf86OutputPtr output) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + int max_lane_count = 4; + + if (radeon_output->dpcd[0] >= 0x11) { + max_lane_count = radeon_output->dpcd[2] & 0x1f; + switch(max_lane_count) { + case 1: case 2: case 4: + break; + default: + max_lane_count = 4; + } + } + return max_lane_count; +} + +Bool radeon_dp_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + int clock = adjusted_mode->Clock; + + radeon_output->dp_lane_count = dp_lanes_for_mode_clock(output, clock); + radeon_output->dp_clock = dp_link_clock_for_mode_clock(output, clock); + if (!radeon_output->dp_lane_count || !radeon_output->dp_clock) + return FALSE; + return TRUE; +} + +static void dp_update_dpvs_emph(xf86OutputPtr output, uint8_t train_set[4]) +{ + RADEONOutputPrivatePtr radeon_output = output->driver_private; + int i; + for (i = 0; i < radeon_output->dp_lane_count; i++) + atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, i, train_set[i]); + + atom_dp_aux_native_write(output, DP_TRAINING_LANE0_SET, radeon_output->dp_lane_count, train_set); +} + +static void do_displayport_link_train(xf86OutputPtr output) +{ + ScrnInfoPtr pScrn = output->scrn; + RADEONInfoPtr info = RADEONPTR(pScrn); + RADEONOutputPrivatePtr radeon_output = output->driver_private; + int enc_id = atom_dp_get_encoder_id(output); + Bool clock_recovery; + uint8_t link_status[DP_LINK_STATUS_SIZE]; + uint8_t tries, voltage, ss_cntl; + uint8_t train_set[4]; + int i; + Bool channel_eq; + uint8_t dp_link_configuration[DP_LINK_CONFIGURATION_SIZE]; + + memset(train_set, 0, 4); + + /* set up link configuration */ + memset(dp_link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); + + if (radeon_output->dp_clock == 27000) + dp_link_configuration[0] = DP_LINK_BW_2_7; + else + dp_link_configuration[0] = DP_LINK_BW_1_62; + dp_link_configuration[1] = radeon_output->dp_lane_count; + + if (radeon_output->dpcd[0] >= 0x11) { + dp_link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + } + + /* power up to D0 */ + dp_set_power(output, DP_SET_POWER_D0); + + /* disable training */ + dp_set_training(output, DP_TRAINING_PATTERN_DISABLE); + + /* write link rate / num / eh framing */ + atom_dp_aux_native_write(output, DP_LINK_BW_SET, 2, + dp_link_configuration); + + /* write ss cntl */ + ss_cntl = 0; + atom_dp_aux_native_write(output, DP_DOWNSPREAD_CTRL, 1, + &ss_cntl); + + /* start local training start */ + if (IS_DCE4_VARIANT) { + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START); + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1); + } else { + RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_START, enc_id, 0); + RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, enc_id, 0); + } + + usleep(400); + dp_set_training(output, DP_TRAINING_PATTERN_1); + dp_update_dpvs_emph(output, train_set); + + /* loop around doing configuration reads and DP encoder setups */ + clock_recovery = FALSE; + tries = 0; + voltage = 0xff; + for (;;) { + usleep(100); + if (!atom_dp_get_link_status(output, link_status)) + break; + + if (dp_clock_recovery_ok(link_status, radeon_output->dp_lane_count)) { + clock_recovery = TRUE; + break; + } + + for (i = 0; i < radeon_output->dp_lane_count; i++) + if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + if (i == radeon_output->dp_lane_count) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "clock recovery reached max voltage\n"); + break; + } + + /* Check to see if we've tried the same voltage 5 times */ + if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + ++tries; + if (tries == 5) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "clock recovery tried 5 times\n"); + break; + } + } else + tries = 0; + + voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + dp_get_adjust_train(output, link_status, radeon_output->dp_lane_count, train_set); + dp_update_dpvs_emph(output, train_set); + + } + + if (!clock_recovery) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "clock recovery failed\n"); + + /* channel equalization */ + tries = 0; + channel_eq = FALSE; + dp_set_training(output, DP_TRAINING_PATTERN_2); + if (IS_DCE4_VARIANT) + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2); + else + RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, enc_id, 1); + + for (;;) { + usleep(400); + if (!atom_dp_get_link_status(output, link_status)) + break; + + if (dp_channel_eq_ok(link_status, radeon_output->dp_lane_count)) { + channel_eq = TRUE; + break; + } + + /* Try 5 times */ + if (tries > 5) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "channel eq failed: 5 tries\n"); + break; + } + + /* Compute new train_set as requested by target */ + dp_get_adjust_train(output, link_status, radeon_output->dp_lane_count, train_set); + dp_update_dpvs_emph(output, train_set); + + ++tries; + } + + if (!channel_eq) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "channel eq failed\n"); + + dp_set_training(output, DP_TRAINING_PATTERN_DISABLE); + if (IS_DCE4_VARIANT) + atombios_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE); + else + RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_COMPLETE, enc_id, 0); + +} + |