Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ebbe1cb9 authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie
Browse files

drm/radeon/kms/atom: add support for spread spectrum (v2)



Spread spectrum is a periodic disturbance added
to the feedback divider to change the pixel clock
periodically to reduce interference.

Only enabled on LVDS.

v2: add support for r4xx and fix DCE 3

Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent c290dadf
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -2314,7 +2314,7 @@ typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT {
	UCHAR ucSS_Step;
	UCHAR ucSS_Step;
	UCHAR ucSS_Delay;
	UCHAR ucSS_Delay;
	UCHAR ucSS_Id;
	UCHAR ucSS_Id;
	UCHAR ucRecommandedRef_Div;
	UCHAR ucRecommendedRef_Div;
	UCHAR ucSS_Range;	/* it was reserved for V11 */
	UCHAR ucSS_Range;	/* it was reserved for V11 */
} ATOM_SPREAD_SPECTRUM_ASSIGNMENT;
} ATOM_SPREAD_SPECTRUM_ASSIGNMENT;


+57 −11
Original line number Original line Diff line number Diff line
@@ -351,6 +351,61 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc,
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
}
}


static void atombios_set_ss(struct drm_crtc *crtc, int enable)
{
	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct radeon_device *rdev = dev->dev_private;
	struct drm_encoder *encoder = NULL;
	struct radeon_encoder *radeon_encoder = NULL;
	struct radeon_encoder_atom_dig *dig = NULL;
	int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
	ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION args;
	ENABLE_LVDS_SS_PARAMETERS legacy_args;
	uint16_t percentage = 0;
	uint8_t type = 0, step = 0, delay = 0, range = 0;

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		if (encoder->crtc == crtc) {
			radeon_encoder = to_radeon_encoder(encoder);
			dig = radeon_encoder->enc_priv;
			/* only enable spread spectrum on LVDS */
			if (dig && dig->ss) {
				percentage = dig->ss->percentage;
				type = dig->ss->type;
				step = dig->ss->step;
				delay = dig->ss->delay;
				range = dig->ss->range;
			} else if (enable)
				return;
			break;
		}
	}

	if (!radeon_encoder)
		return;

	if (ASIC_IS_AVIVO(rdev)) {
		memset(&args, 0, sizeof(args));
		args.usSpreadSpectrumPercentage = percentage;
		args.ucSpreadSpectrumType = type;
		args.ucSpreadSpectrumStep = step;
		args.ucSpreadSpectrumDelay = delay;
		args.ucSpreadSpectrumRange = range;
		args.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
		args.ucEnable = enable;
		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	} else {
		memset(&legacy_args, 0, sizeof(legacy_args));
		legacy_args.usSpreadSpectrumPercentage = percentage;
		legacy_args.ucSpreadSpectrumType = type;
		legacy_args.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2;
		legacy_args.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4;
		legacy_args.ucEnable = enable;
		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&legacy_args);
	}
}

void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
{
{
	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -373,8 +428,6 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
	memset(&args, 0, sizeof(args));
	memset(&args, 0, sizeof(args));


	if (ASIC_IS_AVIVO(rdev)) {
	if (ASIC_IS_AVIVO(rdev)) {
		uint32_t ss_cntl;

		if ((rdev->family == CHIP_RS600) ||
		if ((rdev->family == CHIP_RS600) ||
		    (rdev->family == CHIP_RS690) ||
		    (rdev->family == CHIP_RS690) ||
		    (rdev->family == CHIP_RS740))
		    (rdev->family == CHIP_RS740))
@@ -385,15 +438,6 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
			pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
			pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
		else
		else
			pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
			pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;

		/* disable spread spectrum clocking for now -- thanks Hedy Lamarr */
		if (radeon_crtc->crtc_id == 0) {
			ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL);
			WREG32(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl & ~1);
		} else {
			ss_cntl = RREG32(AVIVO_P2PLL_INT_SS_CNTL);
			WREG32(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl & ~1);
		}
	} else {
	} else {
		pll_flags |= RADEON_PLL_LEGACY;
		pll_flags |= RADEON_PLL_LEGACY;


@@ -641,7 +685,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,


	/* TODO color tiling */
	/* TODO color tiling */


	atombios_set_ss(crtc, 0);
	atombios_crtc_set_pll(crtc, adjusted_mode);
	atombios_crtc_set_pll(crtc, adjusted_mode);
	atombios_set_ss(crtc, 1);
	atombios_crtc_set_timing(crtc, adjusted_mode);
	atombios_crtc_set_timing(crtc, adjusted_mode);


	if (ASIC_IS_AVIVO(rdev))
	if (ASIC_IS_AVIVO(rdev))
+42 −0
Original line number Original line Diff line number Diff line
@@ -771,6 +771,46 @@ bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
	return false;
	return false;
}
}


static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
							  radeon_encoder
							  *encoder,
							  int id)
{
	struct drm_device *dev = encoder->base.dev;
	struct radeon_device *rdev = dev->dev_private;
	struct radeon_mode_info *mode_info = &rdev->mode_info;
	int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info);
	uint16_t data_offset;
	struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info;
	uint8_t frev, crev;
	struct radeon_atom_ss *ss = NULL;

	if (id > ATOM_MAX_SS_ENTRY)
		return NULL;

	atom_parse_data_header(mode_info->atom_context, index, NULL, &frev,
			       &crev, &data_offset);

	ss_info =
	    (struct _ATOM_SPREAD_SPECTRUM_INFO *)(mode_info->atom_context->bios + data_offset);

	if (ss_info) {
		ss =
		    kzalloc(sizeof(struct radeon_atom_ss), GFP_KERNEL);

		if (!ss)
			return NULL;

		ss->percentage = le16_to_cpu(ss_info->asSS_Info[id].usSpreadSpectrumPercentage);
		ss->type = ss_info->asSS_Info[id].ucSpreadSpectrumType;
		ss->step = ss_info->asSS_Info[id].ucSS_Step;
		ss->delay = ss_info->asSS_Info[id].ucSS_Delay;
		ss->range = ss_info->asSS_Info[id].ucSS_Range;
		ss->refdiv = ss_info->asSS_Info[id].ucRecommendedRef_Div;
	}
	return ss;
}

union lvds_info {
union lvds_info {
	struct _ATOM_LVDS_INFO info;
	struct _ATOM_LVDS_INFO info;
	struct _ATOM_LVDS_INFO_V12 info_12;
	struct _ATOM_LVDS_INFO_V12 info_12;
@@ -826,6 +866,8 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
		/* set crtc values */
		/* set crtc values */
		drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V);
		drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V);


		lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id);

		encoder->native_mode = lvds->native_mode;
		encoder->native_mode = lvds->native_mode;
	}
	}
	return lvds;
	return lvds;
+11 −0
Original line number Original line Diff line number Diff line
@@ -260,6 +260,16 @@ struct radeon_encoder_int_tmds {
	struct radeon_tmds_pll tmds_pll[4];
	struct radeon_tmds_pll tmds_pll[4];
};
};


/* spread spectrum */
struct radeon_atom_ss {
	uint16_t percentage;
	uint8_t type;
	uint8_t step;
	uint8_t delay;
	uint8_t range;
	uint8_t refdiv;
};

struct radeon_encoder_atom_dig {
struct radeon_encoder_atom_dig {
	/* atom dig */
	/* atom dig */
	bool coherent_mode;
	bool coherent_mode;
@@ -267,6 +277,7 @@ struct radeon_encoder_atom_dig {
	/* atom lvds */
	/* atom lvds */
	uint32_t lvds_misc;
	uint32_t lvds_misc;
	uint16_t panel_pwr_delay;
	uint16_t panel_pwr_delay;
	struct radeon_atom_ss *ss;
	/* panel mode */
	/* panel mode */
	struct drm_display_mode native_mode;
	struct drm_display_mode native_mode;
};
};