hrev43852 adds 5 changesets to branch 'master' old head: a9c45d3774815a1ab0b81985e088520bc4bec5fa new head: 83e3a8ea5013ca577e735016437d0b526c20b7db ---------------------------------------------------------------------------- 32ef94a: radeon_hd: AtomBIOS loop failure detection now time based * If the same call is made for 5 seconds straight, fail. * Resolves random AtomBIOS failures. * AtomBIOS failures now represent *real* bugs :) 119cd5e: radeon_hd: Add missing load detection on DIG encoders 48b430f: radeon_hd: Add missing AtomBIOS tables 6f7c0aa: radeon_hd: Whitespace cleanup, no functional change 83e3a8e: radeon_hd: Start work on proper DP link training * The AtomBIOS timeout fix has made my DP bridge stop working * The current DisplayPort code is a little lacking on DP link training... I think thats the cause. * This puts the first steps towards DP training in place. * I plan on trying to make some of this DP stuff common accelerant stuff after it works. [ Alexander von Gluck IV <kallisti5@xxxxxxxxxxx> ] ---------------------------------------------------------------------------- 9 files changed, 383 insertions(+), 51 deletions(-) .../private/graphics/radeon_hd/displayport_reg.h | 1 + src/add-ons/accelerants/radeon_hd/accelerant.h | 12 + .../accelerants/radeon_hd/atombios/atom-names.h | 9 +- .../accelerants/radeon_hd/atombios/atom.cpp | 47 ++- src/add-ons/accelerants/radeon_hd/display.cpp | 8 +- src/add-ons/accelerants/radeon_hd/displayport.cpp | 291 ++++++++++++++-- src/add-ons/accelerants/radeon_hd/displayport.h | 2 + src/add-ons/accelerants/radeon_hd/encoder.cpp | 62 ++++- src/add-ons/accelerants/radeon_hd/encoder.h | 2 + ############################################################################ Commit: 32ef94aa9145f192cbbee7a72d5fadd924246d21 URL: http://cgit.haiku-os.org/haiku/commit/?id=32ef94a Author: Alexander von Gluck IV <kallisti5@xxxxxxxxxxx> Date: Tue Mar 13 09:29:59 2012 UTC radeon_hd: AtomBIOS loop failure detection now time based * If the same call is made for 5 seconds straight, fail. * Resolves random AtomBIOS failures. * AtomBIOS failures now represent *real* bugs :) ---------------------------------------------------------------------------- diff --git a/src/add-ons/accelerants/radeon_hd/atombios/atom.cpp b/src/add-ons/accelerants/radeon_hd/atombios/atom.cpp index d23321f..fa8ced5 100644 --- a/src/add-ons/accelerants/radeon_hd/atombios/atom.cpp +++ b/src/add-ons/accelerants/radeon_hd/atombios/atom.cpp @@ -36,10 +36,10 @@ /* AtomBIOS loop detection - * Number of repeat AtomBIOS jmp operations - * before bailing due to stuck in a loop + * Number of seconds of identical jmp operations + * before detecting a fault. */ -#define ATOM_OP_JMP_TIMEOUT 512 +#define ATOM_OP_JMP_TIMEOUT 5 // *** Tracing #undef TRACE @@ -77,8 +77,9 @@ typedef struct { uint32 *ps, *ws; int ps_shift; uint16 start; - uint16 last_jump; - uint16 last_jump_count; + uint16 lastJump; + uint32 lastJumpCount; + bigtime_t jumpStart; bool abort; } atom_exec_context; @@ -541,7 +542,7 @@ atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) if (idx < ATOM_TABLE_NAMES_CNT) { TRACE("%s: table: %s (%d)\n", __func__, atom_table_names[idx], idx); } else { - TRACE("%s: table: unknown (%d)\n", __func__, idx); + ERROR("%s: table: unknown (%d)\n", __func__, idx); } if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) { @@ -659,17 +660,20 @@ atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) execute? "yes" : "no", target); if (execute) { - if (ctx->last_jump == (ctx->start + target)) { - if (ctx->last_jump_count > ATOM_OP_JMP_TIMEOUT) { - ERROR("%s: Error: AtomBIOS stuck in loop for more then %d" - " jumps... abort!\n", __func__, ATOM_OP_JMP_TIMEOUT); + // Time based jmp timeout + if (ctx->lastJump == (ctx->start + target)) { + bigtime_t loopDuration = system_time() - ctx->jumpStart; + if (loopDuration > ATOM_OP_JMP_TIMEOUT * 1000000) { + ERROR("%s: Error: AtomBIOS stuck in loop for more then %d " + "seconds. (%" B_PRIu32 " identical jmp op's)\n", __func__, + ATOM_OP_JMP_TIMEOUT, ctx->lastJumpCount); ctx->abort = true; - } else { - ctx->last_jump_count++; - } + } else + ctx->lastJumpCount++; } else { - ctx->last_jump = ctx->start + target; - ctx->last_jump_count = 1; + ctx->jumpStart = system_time(); + ctx->lastJump = ctx->start + target; + ctx->lastJumpCount = 1; } *ptr = ctx->start + target; } @@ -1137,8 +1141,10 @@ atom_execute_table_locked(atom_context *ctx, int index, uint32 * params) unsigned char op; atom_exec_context ectx; - if (!base) + if (!base) { + ERROR("%s: BUG: Table called doesn't exist in AtomBIOS!\n", __func__); return B_ERROR; + } len = CU16(base + ATOM_CT_SIZE_PTR); ws = CU8(base + ATOM_CT_WS_PTR); @@ -1150,8 +1156,9 @@ atom_execute_table_locked(atom_context *ctx, int index, uint32 * params) ectx.start = base; ectx.ps = params; ectx.abort = false; - ectx.last_jump = 0; - ectx.last_jump_count = 0; + ectx.lastJump = 0; + ectx.lastJumpCount = 0; + ectx.jumpStart = 0; if (ws) ectx.ws = (uint32*)malloc(4 * ws); else @@ -1212,8 +1219,8 @@ atom_execute_table(atom_context *ctx, int index, uint32 *params) if (index < ATOM_TABLE_NAMES_CNT) tableName = atom_table_names[index]; else - tableName = "Unknown"; - + tableName = "Unknown"; + ERROR("%s: AtomBIOS parser was aborted in table %s (0x%" B_PRIX8 ")\n", __func__, tableName, index); } ############################################################################ Commit: 119cd5e1c1a4d40cc19903094ec25f86d058a018 URL: http://cgit.haiku-os.org/haiku/commit/?id=119cd5e Author: Alexander von Gluck IV <kallisti5@xxxxxxxxxxx> Date: Tue Mar 13 10:09:13 2012 UTC radeon_hd: Add missing load detection on DIG encoders ---------------------------------------------------------------------------- diff --git a/src/add-ons/accelerants/radeon_hd/encoder.cpp b/src/add-ons/accelerants/radeon_hd/encoder.cpp index f27a624..4058590 100644 --- a/src/add-ons/accelerants/radeon_hd/encoder.cpp +++ b/src/add-ons/accelerants/radeon_hd/encoder.cpp @@ -904,6 +904,20 @@ encoder_analog_load_detect(uint32 connectorIndex) { TRACE("%s\n", __func__); + uint32 encoderID = gConnector[connectorIndex]->encoder.objectID; + + if (encoder_is_external(encoderID)) + return encoder_dig_load_detect(connectorIndex); + + return encoder_dac_load_detect(connectorIndex); +} + + +bool +encoder_dac_load_detect(uint32 connectorIndex) +{ + TRACE("%s\n", __func__); + uint32 encoderFlags = gConnector[connectorIndex]->encoder.flags; uint32 encoderID = gConnector[connectorIndex]->encoder.objectID; @@ -994,6 +1008,46 @@ encoder_analog_load_detect(uint32 connectorIndex) } +bool +encoder_dig_load_detect(uint32 connectorIndex) +{ + TRACE("%s\n", __func__); + radeon_shared_info &info = *gInfo->shared_info; + + if (info.dceMajor < 4) { + ERROR("%s: Strange: External DIG encoder on DCE < 4?\n", __func__); + return false; + } + + encoder_external_setup(connectorIndex, 0, + EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION); + + uint32 biosScratch0 = Read32(OUT, R600_BIOS_0_SCRATCH); + + uint32 encoderFlags = gConnector[connectorIndex]->encoder.flags; + + if ((encoderFlags & ATOM_DEVICE_CRT1_SUPPORT) != 0) + if ((biosScratch0 & ATOM_S0_CRT1_MASK) != 0) + return true; + if ((encoderFlags & ATOM_DEVICE_CRT2_SUPPORT) != 0) + if ((biosScratch0 & ATOM_S0_CRT2_MASK) != 0) + return true; + if ((encoderFlags & ATOM_DEVICE_CV_SUPPORT) != 0) + if ((biosScratch0 & (ATOM_S0_CV_MASK | ATOM_S0_CV_MASK_A)) != 0) + return true; + if ((encoderFlags & ATOM_DEVICE_TV1_SUPPORT) != 0) { + if ((biosScratch0 + & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) != 0) + return true; /* Composite connected */ + else if ((biosScratch0 + & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) != 0) + return true; /* S-Video connected */ + } + + return false; +} + + status_t transmitter_dig_setup(uint32 connectorIndex, uint32 pixelClock, uint8 laneNumber, uint8 laneSet, int command) diff --git a/src/add-ons/accelerants/radeon_hd/encoder.h b/src/add-ons/accelerants/radeon_hd/encoder.h index 429d1c1..2451c62 100644 --- a/src/add-ons/accelerants/radeon_hd/encoder.h +++ b/src/add-ons/accelerants/radeon_hd/encoder.h @@ -30,6 +30,8 @@ status_t encoder_tv_setup(uint32 connectorIndex, uint32 pixelClock, int command); bool encoder_analog_load_detect(uint32 connectorIndex); +bool encoder_dac_load_detect(uint32 connectorIndex); +bool encoder_dig_load_detect(uint32 connectorIndex); void encoder_output_lock(bool lock); status_t transmitter_dig_setup(uint32 connectorIndex, uint32 pixelClock, uint8 laneNumber, uint8 laneSet, int command); ############################################################################ Commit: 48b430fe0b42d443d47aebf6ed33dbe34e802715 URL: http://cgit.haiku-os.org/haiku/commit/?id=48b430f Author: Alexander von Gluck IV <kallisti5@xxxxxxxxxxx> Date: Tue Mar 13 10:49:10 2012 UTC radeon_hd: Add missing AtomBIOS tables ---------------------------------------------------------------------------- diff --git a/src/add-ons/accelerants/radeon_hd/atombios/atom-names.h b/src/add-ons/accelerants/radeon_hd/atombios/atom-names.h index fdc0b54..e837092 100644 --- a/src/add-ons/accelerants/radeon_hd/atombios/atom-names.h +++ b/src/add-ons/accelerants/radeon_hd/atombios/atom-names.h @@ -21,12 +21,13 @@ * * Author: Stanislaw Skowronek */ - #ifndef ATOM_NAMES_H #define ATOM_NAMES_H + #include "atom.h" + #define ATOM_OP_NAMES_CNT 123 const char *atom_op_names[ATOM_OP_NAMES_CNT] = { "RESERVED", "MOVE_REG", "MOVE_PS", "MOVE_WS", "MOVE_FB", "MOVE_PLL", @@ -53,7 +54,7 @@ const char *atom_op_names[ATOM_OP_NAMES_CNT] = { "DEBUG", "CTB_DS", }; -#define ATOM_TABLE_NAMES_CNT 74 +#define ATOM_TABLE_NAMES_CNT 80 const char *atom_table_names[ATOM_TABLE_NAMES_CNT] = { "ASIC_Init", "GetDisplaySurfaceSize", "ASIC_RegistersInit", "VRAM_BlockVenderDetection", "SetClocksRatio", "MemoryControllerInit", @@ -79,7 +80,9 @@ const char *atom_table_names[ATOM_TABLE_NAMES_CNT] = { "VRAM_GetCurrentInfoBlock", "DynamicMemorySettings", "MemoryTraining", "EnableLVDS_SS", "DFP1OutputControl", "SetVoltage", "CRT1OutputControl", "CRT2OutputControl", "SetupHWAssistedI2CStatus", "ClockSource", -"MemoryDeviceInit", "EnableYUV", +"MemoryDeviceInit", "EnableYUV", "DIG1EncoderControl", "DIG2EncoderControl", +"DIG1TransmitterControl (UNIPHY)", "DIG2TransmitterControl (LVTMA)", +"ProcessAuxChannelTransaction", "DPEncoderService", }; #define ATOM_IO_NAMES_CNT 5 ############################################################################ Commit: 6f7c0aadbe2aa26986c4f8ea701caa36ce7d421c URL: http://cgit.haiku-os.org/haiku/commit/?id=6f7c0aa Author: Alexander von Gluck IV <kallisti5@xxxxxxxxxxx> Date: Tue Mar 13 11:18:57 2012 UTC radeon_hd: Whitespace cleanup, no functional change ---------------------------------------------------------------------------- diff --git a/src/add-ons/accelerants/radeon_hd/display.cpp b/src/add-ons/accelerants/radeon_hd/display.cpp index 9a35988..c694b2e 100644 --- a/src/add-ons/accelerants/radeon_hd/display.cpp +++ b/src/add-ons/accelerants/radeon_hd/display.cpp @@ -477,7 +477,7 @@ display_crtc_dpms(uint8 crtcID, int mode) radeon_shared_info &info = *gInfo->shared_info; switch (mode) { - case B_DPMS_ON: + case B_DPMS_ON: TRACE("%s: crtc %" B_PRIu8 " dpms powerup\n", __func__, crtcID); if (gDisplay[crtcID]->attached == false) return; @@ -487,9 +487,9 @@ display_crtc_dpms(uint8 crtcID, int mode) display_crtc_memreq(crtcID, ATOM_ENABLE); display_crtc_blank(crtcID, ATOM_BLANKING_OFF); break; - case B_DPMS_STAND_BY: - case B_DPMS_SUSPEND: - case B_DPMS_OFF: + case B_DPMS_STAND_BY: + case B_DPMS_SUSPEND: + case B_DPMS_OFF: TRACE("%s: crtc %" B_PRIu8 " dpms powerdown\n", __func__, crtcID); if (gDisplay[crtcID]->attached == false) return; ############################################################################ Revision: hrev43852 Commit: 83e3a8ea5013ca577e735016437d0b526c20b7db URL: http://cgit.haiku-os.org/haiku/commit/?id=83e3a8e Author: Alexander von Gluck IV <kallisti5@xxxxxxxxxxx> Date: Wed Mar 14 10:18:41 2012 UTC radeon_hd: Start work on proper DP link training * The AtomBIOS timeout fix has made my DP bridge stop working * The current DisplayPort code is a little lacking on DP link training... I think thats the cause. * This puts the first steps towards DP training in place. * I plan on trying to make some of this DP stuff common accelerant stuff after it works. ---------------------------------------------------------------------------- diff --git a/headers/private/graphics/radeon_hd/displayport_reg.h b/headers/private/graphics/radeon_hd/displayport_reg.h index a13ca8c..9d44c1d 100644 --- a/headers/private/graphics/radeon_hd/displayport_reg.h +++ b/headers/private/graphics/radeon_hd/displayport_reg.h @@ -136,6 +136,7 @@ #define DP_INTERLANE_ALIGN_DONE (1 << 0) #define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) #define DP_LINK_STATUS_UPDATED (1 << 7) +#define DP_LINK_STATUS_SIZE 6 #define DP_SINK_STATUS 0x205 diff --git a/src/add-ons/accelerants/radeon_hd/accelerant.h b/src/add-ons/accelerants/radeon_hd/accelerant.h index 25898c7..b3bacb7 100644 --- a/src/add-ons/accelerants/radeon_hd/accelerant.h +++ b/src/add-ons/accelerants/radeon_hd/accelerant.h @@ -14,6 +14,7 @@ #include <edid.h> #include "atom.h" +#include "displayport_reg.h" #include "encoder.h" #include "mode.h" #include "pll.h" @@ -126,11 +127,22 @@ typedef struct { typedef struct { bool valid; + uint32 connectorIndex; + + uint32 auxPin; // normally GPIO pin on GPU uint8 config[8]; // DP configuration data uint8 sinkType; uint8 clock; int laneCount; + + bool trainingUseEncoder; + + uint8 trainingAttempts; + uint8 trainingSet[4]; + int trainingReadInterval; + uint8 linkStatus[DP_LINK_STATUS_SIZE]; + bool eDPOn; } dp_info; diff --git a/src/add-ons/accelerants/radeon_hd/displayport.cpp b/src/add-ons/accelerants/radeon_hd/displayport.cpp index 4b15a48..19bd914 100644 --- a/src/add-ons/accelerants/radeon_hd/displayport.cpp +++ b/src/add-ons/accelerants/radeon_hd/displayport.cpp @@ -11,7 +11,6 @@ #include <Debug.h> -#include "accelerant.h" #include "accelerant_protos.h" #include "connector.h" #include "mode.h" @@ -403,12 +402,15 @@ dp_setup_connectors() } uint32 gpioID = gConnector[index]->gpioID; - uint32 hwPin = gGPIOInfo[gpioID]->hwPin; + + uint32 auxPin = gGPIOInfo[gpioID]->hwPin; + gDPInfo[index]->auxPin = auxPin; + gDPInfo[index]->connectorIndex = index; uint8 auxMessage[25]; int result; - result = dp_aux_read(hwPin, DP_DPCD_REV, auxMessage, 8, 0); + result = dp_aux_read(auxPin, DP_DPCD_REV, auxMessage, 8, 0); if (result > 0) { gDPInfo[index]->valid = true; memcpy(gDPInfo[index]->config, auxMessage, 8); @@ -419,14 +421,255 @@ dp_setup_connectors() } +static bool +dp_get_link_status(dp_info* dp) +{ + int result = dp_aux_read(dp->auxPin, DP_LANE0_1_STATUS, + dp->linkStatus, DP_LINK_STATUS_SIZE, 100); + + if (result <= 0) { + ERROR("%s: DisplayPort link status failed\n", __func__); + return false; + } + + return true; +} + + +static uint8 +dp_get_lane_status(dp_info* dp, int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + uint8 l = dp->linkStatus[i - DP_LANE0_1_STATUS]; + return (l >> s) & 0xf; +} + + +static bool +dp_clock_recovery_ok(dp_info* dp) +{ + int lane; + uint8 laneStatus; + + for (lane = 0; lane < dp->laneCount; lane++) { + laneStatus = dp_get_lane_status(dp, lane); + if ((laneStatus & DP_LANE_CR_DONE) == 0) + return false; + } + return true; +} + + +static void +dp_update_vs_emph(dp_info* dp) +{ + // Set initial vs and emph on source + transmitter_dig_setup(dp->connectorIndex, dp->clock, 0, dp->trainingSet[0], + ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH); + + // Set vs and emph on the sink + dp_aux_write(dp->auxPin, DP_TRAINING_LANE0_SET, + dp->trainingSet, dp->laneCount, 0); +} + + +static uint8 +dp_get_adjust_request_voltage(dp_info* dp, int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = (((lane & 1) != 0) ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT + : DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + uint8 l = dp->linkStatus[i - DP_LANE0_1_STATUS]; + + return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + + +static uint8 +dp_get_adjust_request_pre_emphasis(dp_info* dp, int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = (((lane & 1) != 0) ? DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT + : DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + uint8 l = dp->linkStatus[i - DP_LANE0_1_STATUS]; + + return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + + +#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 + + +static void +dp_get_adjust_train(dp_info* dp) +{ + TRACE("%s\n", __func__); + + const char* voltageNames[] = { + "0.4V", "0.6V", "0.8V", "1.2V" + }; + const char* preEmphasisNames[] = { + "0dB", "3.5dB", "6dB", "9.5dB" + }; + + uint8 voltage = 0; + uint8 preEmphasis = 0; + int lane; + + for (lane = 0; lane < dp->laneCount; lane++) { + uint8 laneVoltage = dp_get_adjust_request_voltage(dp, lane); + uint8 lanePreEmphasis = dp_get_adjust_request_pre_emphasis(dp, lane); + + TRACE("%s: Requested %s at %s for lane %d\n", __func__, + preEmphasisNames[lanePreEmphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT], + voltageNames[laneVoltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + lane); + + if (laneVoltage > voltage) + voltage = laneVoltage; + if (lanePreEmphasis > preEmphasis) + preEmphasis = lanePreEmphasis; + } + + if (voltage >= DP_VOLTAGE_MAX) + voltage |= DP_TRAIN_MAX_SWING_REACHED; + + if (preEmphasis >= DP_PRE_EMPHASIS_MAX) + preEmphasis |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + for (lane = 0; lane < 4; lane++) + dp->trainingSet[lane] = voltage | preEmphasis; +} + + +static void +dp_set_tp(dp_info* dp, int trainingPattern) +{ + TRACE("%s\n", __func__); + + radeon_shared_info &info = *gInfo->shared_info; + + int rawTrainingPattern = 0; + + /* set training pattern on the source */ + if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { + switch (trainingPattern) { + case DP_TRAINING_PATTERN_1: + rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1; + break; + case DP_TRAINING_PATTERN_2: + rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2; + break; + case DP_TRAINING_PATTERN_3: + rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3; + break; + } + // TODO: PixelClock 0 ok? + encoder_dig_setup(dp->connectorIndex, 0, rawTrainingPattern); + } else { + ERROR("%s: TODO: dp_encoder_service\n", __func__); + return; + #if 0 + switch (trainingPattern) { + case DP_TRAINING_PATTERN_1: + rawTrainingPattern = 0; + break; + case DP_TRAINING_PATTERN_2: + rawTrainingPattern = 1; + break; + } + radeon_dp_encoder_service(dp_info->rdev, + ATOM_DP_ACTION_TRAINING_PATTERN_SEL, dp_info->dp_clock, + dp_info->enc_id, rawTrainingPattern); + #endif + } + + // Enable training pattern on the sink + dpcd_reg_write(dp->auxPin, DP_TRAINING_PATTERN_SET, trainingPattern); +} + + +status_t +dp_link_train_cr(dp_info* dp) +{ + TRACE("%s\n", __func__); + + // Display Port Clock Recovery Training + + bool clockRecovery = false; + uint8 voltage = 0xff; + int lane; + + dp_set_tp(dp, DP_TRAINING_PATTERN_1); + memset(dp->trainingSet, 0, 4); + dp_update_vs_emph(dp); + + while (1) { + if (dp->trainingReadInterval == 0) + snooze(100); + else + snooze(1000 * 4 * dp->trainingReadInterval); + + if (!dp_get_link_status(dp)) + break; + + if (dp_clock_recovery_ok(dp)) { + clockRecovery = true; + break; + } + + for (lane = 0; lane < dp->laneCount; lane++) { + if ((dp->trainingSet[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + } + + if (lane == dp->laneCount) { + ERROR("%s: clock recovery reached max voltage\n", __func__); + break; + } + + if ((dp->trainingSet[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + dp->trainingAttempts++; + if (dp->trainingAttempts >= 5) { + ERROR("%s: clock recovery tried 5 times\n", __func__); + break; + } + } else + dp->trainingAttempts = 0; + + voltage = dp->trainingSet[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + // Compute new trainingSet as requested by sink + dp_get_adjust_train(dp); + + dp_update_vs_emph(dp); + } + + if (!clockRecovery) { + ERROR("%s: clock recovery failed\n", __func__); + return B_ERROR; + } + + TRACE("%s: clock recovery at voltage %d pre-emphasis %d\n", + __func__, dp->trainingSet[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT); + return B_OK; +} + + status_t dp_link_train(uint8 crtcID, display_mode* mode) { TRACE("%s\n", __func__); uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; - if (gDPInfo[connectorIndex]->valid != true) { - ERROR("%s: started on non-DisplayPort connector #%" B_PRIu32 "\n", + dp_info* dp = gDPInfo[connectorIndex]; + + if (dp->valid != true) { + ERROR("%s: started on invalid DisplayPort connector #%" B_PRIu32 "\n", __func__, connectorIndex); return B_ERROR; } @@ -436,13 +679,13 @@ dp_link_train(uint8 crtcID, display_mode* mode) uint8 tableMajor; uint8 tableMinor; - bool dpUseEncoder = true; + dp->trainingUseEncoder = true; if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor) == B_OK) { if (tableMinor > 1) { // The AtomBIOS DPEncoderService greater then 1.1 can't program the // training pattern properly. - dpUseEncoder = false; + dp->trainingUseEncoder = false; } } @@ -461,22 +704,24 @@ dp_link_train(uint8 crtcID, display_mode* mode) else dpEncoderID |= ATOM_DP_CONFIG_LINK_A; - //uint8 dpReadInterval = dpcd_reg_read(hwPin, DP_TRAINING_AUX_RD_INTERVAL); + dp->trainingReadInterval + = dpcd_reg_read(hwPin, DP_TRAINING_AUX_RD_INTERVAL); + uint8 sandbox = dpcd_reg_read(hwPin, DP_MAX_LANE_COUNT); radeon_shared_info &info = *gInfo->shared_info; - bool dpTPS3Supported = false; - if (info.dceMajor >= 5 && (sandbox & DP_TPS3_SUPPORTED) != 0) - dpTPS3Supported = true; + //bool dpTPS3Supported = false; + //if (info.dceMajor >= 5 && (sandbox & DP_TPS3_SUPPORTED) != 0) + // dpTPS3Supported = true; - // DisplayPort training initialization + // *** DisplayPort link training initialization // Power up the DP sink - if (gDPInfo[connectorIndex]->config[0] >= 0x11) + if (dp->config[0] >= 0x11) dpcd_reg_write(hwPin, DP_SET_POWER, DP_SET_POWER_D0); // Possibly enable downspread on the sink - if ((gDPInfo[connectorIndex]->config[3] & 0x1) != 0) + if ((dp->config[3] & 0x1) != 0) dpcd_reg_write(hwPin, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); else dpcd_reg_write(hwPin, DP_DOWNSPREAD_CTRL, 0); @@ -484,16 +729,16 @@ dp_link_train(uint8 crtcID, display_mode* mode) encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, ATOM_ENCODER_CMD_SETUP_PANEL_MODE); - if (gDPInfo[connectorIndex]->config[0] >= 0x11) + if (dp->config[0] >= 0x11) sandbox |= DP_LANE_COUNT_ENHANCED_FRAME_EN; dpcd_reg_write(hwPin, DP_LANE_COUNT_SET, sandbox); // Set the link rate on the DP sink - sandbox = dp_get_link_clock_encode(gDPInfo[connectorIndex]->clock); + sandbox = dp_get_link_clock_encode(dp->clock); dpcd_reg_write(hwPin, DP_LINK_BW_SET, sandbox); // Start link training on source - if (info.dceMajor >= 4 || !dpUseEncoder) { + if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START); } else { @@ -501,21 +746,23 @@ dp_link_train(uint8 crtcID, display_mode* mode) __func__); } - /* disable the training pattern on the sink */ + // Disable the training pattern on the sink dpcd_reg_write(hwPin, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); - // TODO: dp_link_train_cr + dp_link_train_cr(dp); // TODO: dp_link_train_ce + + // *** DisplayPort link training finish snooze(400); - /* disable the training pattern on the sink */ + // Disable the training pattern on the sink dpcd_reg_write(hwPin, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); - /* disable the training pattern on the source */ - if (info.dceMajor >= 4 || !dpUseEncoder) { + // Disable the training pattern on the source + if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE); } else { diff --git a/src/add-ons/accelerants/radeon_hd/displayport.h b/src/add-ons/accelerants/radeon_hd/displayport.h index 5a705dc..dff3f36 100644 --- a/src/add-ons/accelerants/radeon_hd/displayport.h +++ b/src/add-ons/accelerants/radeon_hd/displayport.h @@ -13,6 +13,7 @@ #include <stdint.h> #include <SupportDefs.h> +#include "accelerant.h" #include "displayport_reg.h" @@ -32,6 +33,7 @@ uint32 dp_get_link_clock_decode(uint32 dpLinkClock); void dp_setup_connectors(); status_t dp_link_train(uint8 crtcID, display_mode* mode); +status_t dp_link_train_cr(dp_info* dp); #endif /* RADEON_HD_DISPLAYPORT_H */ diff --git a/src/add-ons/accelerants/radeon_hd/encoder.cpp b/src/add-ons/accelerants/radeon_hd/encoder.cpp index 4058590..4ec0991 100644 --- a/src/add-ons/accelerants/radeon_hd/encoder.cpp +++ b/src/add-ons/accelerants/radeon_hd/encoder.cpp @@ -1069,8 +1069,12 @@ transmitter_dig_setup(uint32 connectorIndex, uint32 pixelClock, index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl); break; default: - ERROR("%s: called on non-dig encoder!\n", __func__); - return B_ERROR; + // Multiple encoders can be wired to a single connector + // An example is UNIPHY -> DP -> TRAVIS -> LVDS + ERROR("%s: BUG: guessing UNIPHY as this isn't a dig encoder!\n", + __func__); + index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); + break; } if (index < 0) {