static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *send, int send_bytes, u8 *recv, int recv_size, u8 delay, u8 *ack) { struct drm_device *dev = chan->dev; struct radeon_device *rdev = dev->dev_private; union aux_channel_transaction args; int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); unsigned char *base; int recv_bytes; memset(&args, 0, sizeof(args)); base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1); memcpy(base, send, send_bytes); args.v1.lpAuxRequest = 0 + 4; args.v1.lpDataOut = 16 + 4; args.v1.ucDataOutLen = 0; args.v1.ucChannelID = chan->rec.i2c_id; args.v1.ucDelay = delay / 10; if (ASIC_IS_DCE4(rdev)) args.v2.ucHPD_ID = chan->rec.hpd; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); *ack = args.v1.ucReplyStatus; /* timeout */ if (args.v1.ucReplyStatus == 1) { DRM_DEBUG_KMS("dp_aux_ch timeout\n"); return -ETIMEDOUT; } /* flags not zero */ if (args.v1.ucReplyStatus == 2) { DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); return -EBUSY; } /* error */ if (args.v1.ucReplyStatus == 3) { DRM_DEBUG_KMS("dp_aux_ch error\n"); return -EIO; } recv_bytes = args.v1.ucDataOutLen; if (recv_bytes > recv_size) recv_bytes = recv_size; if (recv && recv_size) memcpy(recv, base + 16, recv_bytes); return recv_bytes; }
static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan, u8 slave_addr, u8 flags, u8 *buf, u8 num) { struct drm_device *dev = chan->dev; struct radeon_device *rdev = dev->dev_private; PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); unsigned char *base; u16 out = cpu_to_le16(0); memset(&args, 0, sizeof(args)); base = (unsigned char *)rdev->mode_info.atom_context->scratch; if (flags & HW_I2C_WRITE) { if (num > ATOM_MAX_HW_I2C_WRITE) { DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num); return -EINVAL; } if (buf == NULL) args.ucRegIndex = 0; else args.ucRegIndex = buf[0]; if (num) num--; if (num) memcpy(&out, &buf[1], num); args.lpI2CDataOut = cpu_to_le16(out); } else { if (num > ATOM_MAX_HW_I2C_READ) { DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num); return -EINVAL; } args.ucRegIndex = 0; args.lpI2CDataOut = 0; } args.ucFlag = flags; args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; args.ucTransBytes = num; args.ucSlaveAddr = slave_addr << 1; args.ucLineNumber = chan->rec.i2c_id; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); /* error */ if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) { DRM_DEBUG_KMS("hw_i2c error\n"); return -EIO; } if (!(flags & HW_I2C_WRITE)) radeon_atom_copy_swap(buf, base, num, false); return 0; }
static int dp_aux_speak(uint32 hwPin, uint8* send, int sendBytes, uint8* recv, int recvBytes, uint8 delay, uint8* ack) { if (hwPin == 0) { ERROR("%s: cannot speak on invalid GPIO pin!\n", __func__); return B_IO_ERROR; } int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); // Build AtomBIOS Transaction union auxChannelTransaction { PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; }; union auxChannelTransaction args; memset(&args, 0, sizeof(args)); args.v1.lpAuxRequest = 0; args.v1.lpDataOut = 16; args.v1.ucDataOutLen = 0; args.v1.ucChannelID = hwPin; args.v1.ucDelay = delay / 10; //if (ASIC_IS_DCE4(rdev)) // args.v2.ucHPD_ID = chan->rec.hpd; unsigned char* base = (unsigned char*)gAtomContext->scratch; memcpy(base, send, sendBytes); atom_execute_table(gAtomContext, index, (uint32*)&args); *ack = args.v1.ucReplyStatus; switch (args.v1.ucReplyStatus) { case 1: ERROR("%s: dp_aux_ch timeout!\n", __func__); return B_TIMED_OUT; case 2: ERROR("%s: dp_aux_ch flags not zero!\n", __func__); return B_BUSY; case 3: ERROR("%s: dp_aux_ch error!\n", __func__); return B_IO_ERROR; } int recvLength = args.v1.ucDataOutLen; if (recvLength > recvBytes) recvLength = recvBytes; if (recv && recvBytes) memcpy(recv, base + 16, recvLength); return recvLength; }
void display_crtc_scale(uint8 crtcID, display_mode* mode) { TRACE("%s\n", __func__); ENABLE_SCALER_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); memset(&args, 0, sizeof(args)); args.ucScaler = crtcID; args.ucEnable = ATOM_SCALER_DISABLE; atom_execute_table(gAtomContext, index, (uint32*)&args); }
/* radeon aux chan functions */ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, int num_bytes, u8 *read_byte, u8 read_buf_len, u8 delay) { struct drm_device *dev = chan->dev; struct radeon_device *rdev = dev->dev_private; union aux_channel_transaction args; int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); unsigned char *base; int retry_count = 0; memset(&args, 0, sizeof(args)); base = (unsigned char *)rdev->mode_info.atom_context->scratch; retry: memcpy(base, req_bytes, num_bytes); args.v1.lpAuxRequest = 0; args.v1.lpDataOut = 16; args.v1.ucDataOutLen = 0; args.v1.ucChannelID = chan->rec.i2c_id; args.v1.ucDelay = delay / 10; if (ASIC_IS_DCE4(rdev)) args.v2.ucHPD_ID = chan->rec.hpd; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); if (args.v1.ucReplyStatus && !args.v1.ucDataOutLen) { if (args.v1.ucReplyStatus == 0x20 && retry_count++ < 10) goto retry; DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n", req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], chan->rec.i2c_id, args.v1.ucReplyStatus, retry_count); return false; } if (args.v1.ucDataOutLen && read_byte && read_buf_len) { if (read_buf_len < args.v1.ucDataOutLen) { DRM_ERROR("Buffer to small for return answer %d %d\n", read_buf_len, args.v1.ucDataOutLen); return false; } { int len = min(read_buf_len, args.v1.ucDataOutLen); memcpy(read_byte, base + 16, len); } } return true; }
void display_crtc_lock(uint8 crtcID, int command) { TRACE("%s\n", __func__); ENABLE_CRTC_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters); memset(&args, 0, sizeof(args)); args.ucCRTC = crtcID; args.ucEnable = command; atom_execute_table(gAtomContext, index, (uint32*)&args); }
/* radeon dp functions */ static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, uint8_t ucconfig, uint8_t lane_num) { DP_ENCODER_SERVICE_PARAMETERS args; int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); memset(&args, 0, sizeof(args)); args.ucLinkClock = dp_clock / 10; args.ucConfig = ucconfig; args.ucAction = action; args.ucLaneNum = lane_num; args.ucStatus = 0; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); return args.ucStatus; }
void display_crtc_blank(uint8 crtcID, int command) { TRACE("%s\n", __func__); BLANK_CRTC_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC); memset(&args, 0, sizeof(args)); args.ucCRTC = crtcID; args.ucBlanking = command; args.usBlackColorRCr = 0; args.usBlackColorGY = 0; args.usBlackColorBCb = 0; atom_execute_table(gAtomContext, index, (uint32*)&args); }
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_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; }