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;
}
예제 #2
0
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;
}
예제 #3
0
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);
}
예제 #4
0
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;
}
예제 #5
0
파일: pll.cpp 프로젝트: FreemanLou/haiku
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;
}
예제 #6
0
파일: pll.cpp 프로젝트: FreemanLou/haiku
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;
}
예제 #7
0
파일: pll.cpp 프로젝트: FreemanLou/haiku
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;
}