status_t dp_link_train(uint8 crtcID) { TRACE("%s\n", __func__); uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; dp_info* dp = &gConnector[connectorIndex]->dpInfo; display_mode* mode = &gDisplay[crtcID]->currentMode; if (dp->valid != true) { ERROR("%s: started on invalid DisplayPort connector #%" B_PRIu32 "\n", __func__, connectorIndex); return B_ERROR; } int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); // Table version uint8 tableMajor; uint8 tableMinor; 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. dp->trainingUseEncoder = false; } } uint32 linkEnumeration = gConnector[connectorIndex]->encoder.linkEnumeration; uint32 gpioID = gConnector[connectorIndex]->gpioID; uint32 hwPin = gGPIOInfo[gpioID]->hwPin; uint32 dpEncoderID = 0; if (encoder_pick_dig(connectorIndex) > 0) dpEncoderID |= ATOM_DP_CONFIG_DIG2_ENCODER; else dpEncoderID |= ATOM_DP_CONFIG_DIG1_ENCODER; if (linkEnumeration == GRAPH_OBJECT_ENUM_ID2) dpEncoderID |= ATOM_DP_CONFIG_LINK_B; else dpEncoderID |= ATOM_DP_CONFIG_LINK_A; 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; // *** DisplayPort link training initialization // Power up the DP sink if (dp->config[0] >= DP_DPCD_REV_11) dpcd_reg_write(hwPin, DP_SET_POWER, DP_SET_POWER_D0); // Possibly enable downspread on the sink if ((dp->config[3] & 0x1) != 0) dpcd_reg_write(hwPin, DP_DOWNSPREAD_CTRL, DP_DOWNSPREAD_CTRL_AMP_EN); else dpcd_reg_write(hwPin, DP_DOWNSPREAD_CTRL, 0); encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, ATOM_ENCODER_CMD_SETUP_PANEL_MODE); // TODO: Doesn't this overwrite important dpcd info? sandbox = dp->laneCount; if ((dp->config[0] >= DP_DPCD_REV_11) && (dp->config[2] & DP_ENHANCED_FRAME_CAP_EN)) sandbox |= DP_ENHANCED_FRAME_EN; dpcd_reg_write(hwPin, DP_LANE_COUNT, sandbox); // Set the link rate on the DP sink sandbox = dp_encode_link_rate(dp->linkRate); dpcd_reg_write(hwPin, DP_LINK_RATE, sandbox); // Start link training on source if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START); } else { ERROR("%s: TODO: cannot use AtomBIOS DPEncoderService on card!\n", __func__); } // Disable the training pattern on the sink dpcd_reg_write(hwPin, DP_TRAIN, DP_TRAIN_PATTERN_DISABLED); dp_link_train_cr(connectorIndex); dp_link_train_ce(connectorIndex); // *** DisplayPort link training finish snooze(400); // Disable the training pattern on the sink dpcd_reg_write(hwPin, DP_TRAIN, DP_TRAIN_PATTERN_DISABLED); // 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 { ERROR("%s: TODO: cannot use AtomBIOS DPEncoderService on card!\n", __func__); } return B_OK; }
void radeon_dp_link_train(struct drm_encoder *encoder, struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig; struct radeon_connector *radeon_connector; struct radeon_connector_atom_dig *dig_connector; struct radeon_dp_link_train_info dp_info; int index; u8 tmp, frev, crev; if (!radeon_encoder->enc_priv) return; dig = radeon_encoder->enc_priv; radeon_connector = to_radeon_connector(connector); if (!radeon_connector->con_priv) return; dig_connector = radeon_connector->con_priv; if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) && (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP)) return; /* DPEncoderService newer than 1.1 can't program properly the * training pattern. When facing such version use the * DIGXEncoderControl (X== 1 | 2) */ dp_info.use_dpencoder = true; index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); if (atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) { if (crev > 1) { dp_info.use_dpencoder = false; } } dp_info.enc_id = 0; if (dig->dig_encoder) dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER; else dp_info.enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER; if (dig->linkb) dp_info.enc_id |= ATOM_DP_CONFIG_LINK_B; else dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A; tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT); if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) dp_info.tp3_supported = true; else dp_info.tp3_supported = false; memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE); dp_info.rdev = rdev; dp_info.encoder = encoder; dp_info.connector = connector; dp_info.radeon_connector = radeon_connector; dp_info.dp_lane_count = dig_connector->dp_lane_count; dp_info.dp_clock = dig_connector->dp_clock; if (radeon_dp_link_train_init(&dp_info)) goto done; if (radeon_dp_link_train_cr(&dp_info)) goto done; if (radeon_dp_link_train_ce(&dp_info)) goto done; done: if (radeon_dp_link_train_finish(&dp_info)) return; }
status_t pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID) { uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; pll_info *pll = &gConnector[connectorIndex]->encoder.pll; pll->pixelClock = pixelClock; pll->id = pllID; pll_setup_flags(pll, crtcID); // set up any special flags pll_adjust(pll, crtcID); // get any needed clock adjustments, set reference/post dividers pll_compute(pll); // compute dividers int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); union set_pixel_clock args; memset(&args, 0, sizeof(args)); uint8 tableMajor; uint8 tableMinor; atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor); uint32 bitsPerChannel = 8; // TODO: Digital Depth, EDID 1.4+ on digital displays // isn't in Haiku edid common code? switch (tableMinor) { case 1: args.v1.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v1.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); args.v1.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v1.ucFracFbDiv = pll->feedbackDivFrac; args.v1.ucPostDiv = pll->postDiv; args.v1.ucPpll = pll->id; args.v1.ucCRTC = crtcID; args.v1.ucRefDivSrc = 1; break; case 2: args.v2.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v2.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); args.v2.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v2.ucFracFbDiv = pll->feedbackDivFrac; args.v2.ucPostDiv = pll->postDiv; args.v2.ucPpll = pll->id; args.v2.ucCRTC = crtcID; args.v2.ucRefDivSrc = 1; break; case 3: args.v3.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v3.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); args.v3.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v3.ucFracFbDiv = pll->feedbackDivFrac; args.v3.ucPostDiv = pll->postDiv; args.v3.ucPpll = pll->id; args.v3.ucMiscInfo = (pll->id << 2); // if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) // args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; args.v3.ucTransmitterId = gConnector[connectorIndex]->encoder.objectID; args.v3.ucEncoderMode = display_get_encoder_mode(connectorIndex); break; case 5: args.v5.ucCRTC = crtcID; args.v5.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v5.ucRefDiv = pll->referenceDiv; args.v5.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v5.ulFbDivDecFrac = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000); args.v5.ucPostDiv = pll->postDiv; args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ // if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) // args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC; switch (bitsPerChannel) { case 8: default: args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; break; case 10: args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; break; } args.v5.ucTransmitterID = gConnector[connectorIndex]->encoder.objectID; args.v5.ucEncoderMode = display_get_encoder_mode(connectorIndex); args.v5.ucPpll = pllID; break; case 6: args.v6.ulDispEngClkFreq = B_HOST_TO_LENDIAN_INT32(crtcID << 24 | pll->pixelClock / 10); args.v6.ucRefDiv = pll->referenceDiv; args.v6.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v6.ulFbDivDecFrac = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000); args.v6.ucPostDiv = pll->postDiv; args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */ // if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) // args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; switch (bitsPerChannel) { case 8: default: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; break; case 10: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP; break; case 12: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP; break; case 16: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; break; } args.v6.ucTransmitterID = gConnector[connectorIndex]->encoder.objectID; args.v6.ucEncoderMode = display_get_encoder_mode(connectorIndex); args.v6.ucPpll = pllID; break; default: TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " TODO\n", __func__, tableMajor, tableMinor); return B_ERROR; } TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n", __func__, pll->pixelClock, pixelClock); return atom_execute_table(gAtomContext, index, (uint32*)&args); }
status_t pll_adjust(pll_info *pll, uint8 crtcID) { // TODO: PLL flags radeon_shared_info &info = *gInfo->shared_info; uint32 pixelClock = pll->pixelClock; // original as pixel_clock will be adjusted uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; uint32 encoderID = gConnector[connectorIndex]->encoder.objectID; uint32 encoderMode = display_get_encoder_mode(connectorIndex); if (info.device_chipset >= (RADEON_R600 | 0x20)) { union adjust_pixel_clock args; uint8 tableMajor; uint8 tableMinor; int index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor) != B_OK) { return B_ERROR; } memset(&args, 0, sizeof(args)); switch (tableMajor) { case 1: switch (tableMinor) { case 1: case 2: args.v1.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10); args.v1.ucTransmitterID = encoderID; args.v1.ucEncodeMode = encoderMode; // TODO: SS and SS % > 0 if (0) { args.v1.ucConfig |= ADJUST_DISPLAY_CONFIG_SS_ENABLE; } atom_execute_table(gAtomContext, index, (uint32*)&args); // get returned adjusted clock pll->pixelClock = B_LENDIAN_TO_HOST_INT16(args.v1.usPixelClock); pll->pixelClock *= 10; break; case 3: args.v3.sInput.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10); args.v3.sInput.ucTransmitterID = encoderID; args.v3.sInput.ucEncodeMode = encoderMode; args.v3.sInput.ucDispPllConfig = 0; // TODO: SS and SS % > 0 if (0) { args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_SS_ENABLE; } // TODO: if ATOM_DEVICE_DFP_SUPPORT // TODO: display port DP // TODO: is DP? args.v3.sInput.ucExtTransmitterID = 0; atom_execute_table(gAtomContext, index, (uint32*)&args); // get returned adjusted clock pll->pixelClock = B_LENDIAN_TO_HOST_INT32( args.v3.sOutput.ulDispPllFreq); pll->pixelClock *= 10; // convert to kHz for storage if (args.v3.sOutput.ucRefDiv) { pll->flags |= PLL_USE_FRAC_FB_DIV; pll->flags |= PLL_USE_REF_DIV; pll->referenceDiv = args.v3.sOutput.ucRefDiv; } if (args.v3.sOutput.ucPostDiv) { pll->flags |= PLL_USE_FRAC_FB_DIV; pll->flags |= PLL_USE_POST_DIV; pll->postDiv = args.v3.sOutput.ucPostDiv; } break; default: TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " unknown\n", __func__, tableMajor, tableMinor); return B_ERROR; } break; default: TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " unknown\n", __func__, tableMajor, tableMinor); return B_ERROR; } } TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__, pixelClock, pll->pixelClock); return B_OK; }
status_t pll_external_set(uint32 clock) { TRACE("%s: set external pll clock to %" B_PRIu32 "\n", __func__, clock); if (clock == 0) ERROR("%s: Warning: default display clock is 0?\n", __func__); // also known as PLL display engineering uint8 tableMajor; uint8 tableMinor; int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor); TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); union setPixelClock { SET_PIXEL_CLOCK_PS_ALLOCATION base; PIXEL_CLOCK_PARAMETERS v1; PIXEL_CLOCK_PARAMETERS_V2 v2; PIXEL_CLOCK_PARAMETERS_V3 v3; PIXEL_CLOCK_PARAMETERS_V5 v5; PIXEL_CLOCK_PARAMETERS_V6 v6; }; union setPixelClock args; memset(&args, 0, sizeof(args)); radeon_shared_info &info = *gInfo->shared_info; uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor; switch (tableMajor) { case 1: switch(tableMinor) { case 5: // If the default DC PLL clock is specified, // SetPixelClock provides the dividers. args.v5.ucCRTC = ATOM_CRTC_INVALID; args.v5.usPixelClock = B_HOST_TO_LENDIAN_INT16(clock / 10); args.v5.ucPpll = ATOM_DCPLL; break; case 6: // If the default DC PLL clock is specified, // SetPixelClock provides the dividers. args.v6.ulDispEngClkFreq = B_HOST_TO_LENDIAN_INT32(clock / 10); if (dceVersion == 601) args.v6.ucPpll = ATOM_EXT_PLL1; else if (dceVersion >= 600) args.v6.ucPpll = ATOM_PPLL0; else args.v6.ucPpll = ATOM_DCPLL; break; default: ERROR("%s: Unknown table version %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); } break; default: ERROR("%s: Unknown table version %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); } return B_OK; }
status_t pll_set(display_mode* mode, uint8 crtcID) { uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; pll_info* pll = &gConnector[connectorIndex]->encoder.pll; uint32 dp_clock = gConnector[connectorIndex]->dpInfo.linkRate; bool ssEnabled = false; pll->pixelClock = mode->timing.pixel_clock; radeon_shared_info &info = *gInfo->shared_info; // Probe for PLL spread spectrum info; pll->ssPercentage = 0; pll->ssType = 0; pll->ssStep = 0; pll->ssDelay = 0; pll->ssRange = 0; pll->ssReferenceDiv = 0; switch (display_get_encoder_mode(connectorIndex)) { case ATOM_ENCODER_MODE_DP_MST: case ATOM_ENCODER_MODE_DP: if (info.dceMajor >= 4) pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_DP); else { if (dp_clock == 162000) { ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID2); if (!ssEnabled) // id2 failed, try id1 ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); } else ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); } break; case ATOM_ENCODER_MODE_LVDS: if (info.dceMajor >= 4) ssEnabled = pll_asic_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); else ssEnabled = pll_ppll_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); break; case ATOM_ENCODER_MODE_DVI: if (info.dceMajor >= 4) ssEnabled = pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_TMDS); break; case ATOM_ENCODER_MODE_HDMI: if (info.dceMajor >= 4) ssEnabled = pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_HDMI); break; } pll_setup_flags(pll, crtcID); // set up any special flags pll_adjust(pll, mode, crtcID); // get any needed clock adjustments, set reference/post dividers pll_compute(pll); // compute dividers display_crtc_ss(pll, ATOM_DISABLE); // disable ss uint8 tableMajor; uint8 tableMinor; int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor); TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); uint32 bitsPerColor = 8; // TODO: Digital Depth, EDID 1.4+ on digital displays // isn't in Haiku edid common code? // Prepare arguments for AtomBIOS call union setPixelClock { SET_PIXEL_CLOCK_PS_ALLOCATION base; PIXEL_CLOCK_PARAMETERS v1; PIXEL_CLOCK_PARAMETERS_V2 v2; PIXEL_CLOCK_PARAMETERS_V3 v3; PIXEL_CLOCK_PARAMETERS_V5 v5; PIXEL_CLOCK_PARAMETERS_V6 v6; }; union setPixelClock args; memset(&args, 0, sizeof(args)); switch (tableMinor) { case 1: args.v1.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v1.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); args.v1.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v1.ucFracFbDiv = pll->feedbackDivFrac; args.v1.ucPostDiv = pll->postDiv; args.v1.ucPpll = pll->id; args.v1.ucCRTC = crtcID; args.v1.ucRefDivSrc = 1; break; case 2: args.v2.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v2.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); args.v2.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v2.ucFracFbDiv = pll->feedbackDivFrac; args.v2.ucPostDiv = pll->postDiv; args.v2.ucPpll = pll->id; args.v2.ucCRTC = crtcID; args.v2.ucRefDivSrc = 1; break; case 3: args.v3.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v3.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); args.v3.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v3.ucFracFbDiv = pll->feedbackDivFrac; args.v3.ucPostDiv = pll->postDiv; args.v3.ucPpll = pll->id; args.v3.ucMiscInfo = (pll->id << 2); if (pll->ssPercentage > 0 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) { args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; } args.v3.ucTransmitterId = gConnector[connectorIndex]->encoder.objectID; args.v3.ucEncoderMode = display_get_encoder_mode(connectorIndex); break; case 5: args.v5.ucCRTC = crtcID; args.v5.usPixelClock = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); args.v5.ucRefDiv = pll->referenceDiv; args.v5.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v5.ulFbDivDecFrac = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000); args.v5.ucPostDiv = pll->postDiv; args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ if (pll->ssPercentage > 0 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) { args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC; } switch (bitsPerColor) { case 8: default: args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; break; case 10: args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; break; } args.v5.ucTransmitterID = gConnector[connectorIndex]->encoder.objectID; args.v5.ucEncoderMode = display_get_encoder_mode(connectorIndex); args.v5.ucPpll = pll->id; break; case 6: args.v6.ulDispEngClkFreq = B_HOST_TO_LENDIAN_INT32(crtcID << 24 | pll->pixelClock / 10); args.v6.ucRefDiv = pll->referenceDiv; args.v6.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); args.v6.ulFbDivDecFrac = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000); args.v6.ucPostDiv = pll->postDiv; args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */ if (pll->ssPercentage > 0 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) { args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; } switch (bitsPerColor) { case 8: default: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; break; case 10: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP; break; case 12: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP; break; case 16: args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; break; } args.v6.ucTransmitterID = gConnector[connectorIndex]->encoder.objectID; args.v6.ucEncoderMode = display_get_encoder_mode(connectorIndex); args.v6.ucPpll = pll->id; break; default: TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " TODO\n", __func__, tableMajor, tableMinor); return B_ERROR; } TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n", __func__, pll->pixelClock, mode->timing.pixel_clock); status_t result = atom_execute_table(gAtomContext, index, (uint32*)&args); if (ssEnabled) display_crtc_ss(pll, ATOM_ENABLE); return result; }
status_t pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID) { radeon_shared_info &info = *gInfo->shared_info; uint32 pixelClock = pll->pixelClock; // original as pixel_clock will be adjusted uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; connector_info* connector = gConnector[connectorIndex]; uint32 encoderID = connector->encoder.objectID; uint32 encoderMode = display_get_encoder_mode(connectorIndex); uint32 connectorFlags = connector->flags; uint32 externalEncoderID = 0; pll->adjustedClock = pll->pixelClock; if (connector->encoderExternal.isDPBridge) externalEncoderID = connector->encoderExternal.objectID; if (info.dceMajor >= 3) { uint8 tableMajor; uint8 tableMinor; int index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor) != B_OK) { ERROR("%s: Couldn't find AtomBIOS PLL adjustment\n", __func__); return B_ERROR; } TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); // Prepare arguments for AtomBIOS call union adjustPixelClock { ADJUST_DISPLAY_PLL_PS_ALLOCATION v1; ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3; }; union adjustPixelClock args; memset(&args, 0, sizeof(args)); switch (tableMajor) { case 1: switch (tableMinor) { case 1: case 2: args.v1.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10); args.v1.ucTransmitterID = encoderID; args.v1.ucEncodeMode = encoderMode; if (pll->ssPercentage > 0) { args.v1.ucConfig |= ADJUST_DISPLAY_CONFIG_SS_ENABLE; } atom_execute_table(gAtomContext, index, (uint32*)&args); // get returned adjusted clock pll->adjustedClock = B_LENDIAN_TO_HOST_INT16(args.v1.usPixelClock); pll->adjustedClock *= 10; break; case 3: args.v3.sInput.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10); args.v3.sInput.ucTransmitterID = encoderID; args.v3.sInput.ucEncodeMode = encoderMode; args.v3.sInput.ucDispPllConfig = 0; if (pll->ssPercentage > 0) { args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_SS_ENABLE; } // Handle DP adjustments if (encoderMode == ATOM_ENCODER_MODE_DP || encoderMode == ATOM_ENCODER_MODE_DP_MST) { TRACE("%s: encoderMode is DP\n", __func__); args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_COHERENT_MODE; /* 162000 or 270000 */ uint32 dpLinkSpeed = dp_get_link_rate(connectorIndex, mode); /* 16200 or 27000 */ args.v3.sInput.usPixelClock = B_HOST_TO_LENDIAN_INT16(dpLinkSpeed / 10); } else if ((connectorFlags & ATOM_DEVICE_DFP_SUPPORT) != 0) { #if 0 if (encoderMode == ATOM_ENCODER_MODE_HDMI) { /* deep color support */ args.v3.sInput.usPixelClock = cpu_to_le16((mode->clock * bpc / 8) / 10); } #endif if (pixelClock > 165000) { args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_DUAL_LINK; } if (1) { // dig coherent mode? args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_COHERENT_MODE; } } args.v3.sInput.ucExtTransmitterID = externalEncoderID; atom_execute_table(gAtomContext, index, (uint32*)&args); // get returned adjusted clock pll->adjustedClock = B_LENDIAN_TO_HOST_INT32( args.v3.sOutput.ulDispPllFreq); pll->adjustedClock *= 10; // convert to kHz for storage if (args.v3.sOutput.ucRefDiv) { pll->flags |= PLL_USE_FRAC_FB_DIV; pll->flags |= PLL_USE_REF_DIV; pll->referenceDiv = args.v3.sOutput.ucRefDiv; } if (args.v3.sOutput.ucPostDiv) { pll->flags |= PLL_USE_FRAC_FB_DIV; pll->flags |= PLL_USE_POST_DIV; pll->postDiv = args.v3.sOutput.ucPostDiv; } break; default: TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " unknown\n", __func__, tableMajor, tableMinor); return B_ERROR; } break; default: TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " unknown\n", __func__, tableMajor, tableMinor); return B_ERROR; } } TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__, pixelClock, pll->adjustedClock); return B_OK; }