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

I don't have an atom-based laptop at the moment, so it's only been
tested on desktop cards.  I'd appreciate feedback from laptop users.

Alex
From 562b4d8c261db106acdd272ab3bdb12c0152649c Mon Sep 17 00:00:00 2001
From: Alex Deucher <[email protected]>
Date: Fri, 16 Oct 2009 11:15:25 -0400
Subject: [PATCH] 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: Alex Deucher <[email protected]>
---
 drivers/gpu/drm/radeon/atombios.h        |    2 +-
 drivers/gpu/drm/radeon/atombios_crtc.c   |   68 +++++++++++++++++++++++++-----
 drivers/gpu/drm/radeon/radeon_atombios.c |   42 ++++++++++++++++++
 drivers/gpu/drm/radeon/radeon_mode.h     |   11 +++++
 4 files changed, 111 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index 5d40208..c11dddd 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -2314,7 +2314,7 @@ typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT {
 	UCHAR ucSS_Step;
 	UCHAR ucSS_Delay;
 	UCHAR ucSS_Id;
-	UCHAR ucRecommandedRef_Div;
+	UCHAR ucRecommendedRef_Div;
 	UCHAR ucSS_Range;	/* it was reserved for V11 */
 } ATOM_SPREAD_SPECTRUM_ASSIGNMENT;
 
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index f7dcb69..0a9df66 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -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);
 }
 
+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)
 {
 	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));
 
 	if (ASIC_IS_AVIVO(rdev)) {
-		uint32_t ss_cntl;
-
 		if ((rdev->family == CHIP_RS600) ||
 		    (rdev->family == CHIP_RS690) ||
 		    (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;
 		else
 			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 {
 		pll_flags |= RADEON_PLL_LEGACY;
 
@@ -631,7 +675,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
 
 	/* TODO color tiling */
 
+	atombios_set_ss(crtc, 0);
 	atombios_crtc_set_pll(crtc, adjusted_mode);
+	atombios_set_ss(crtc, 1);
 	atombios_crtc_set_timing(crtc, adjusted_mode);
 
 	if (ASIC_IS_AVIVO(rdev))
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 79d4b77..0cd992b 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -767,6 +767,46 @@ bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
 	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 {
 	struct _ATOM_LVDS_INFO info;
 	struct _ATOM_LVDS_INFO_V12 info_12;
@@ -822,6 +862,8 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
 		/* set crtc values */
 		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;
 	}
 	return lvds;
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index b6868fb..2ea5707 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -260,6 +260,16 @@ struct radeon_encoder_int_tmds {
 	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 {
 	/* atom dig */
 	bool coherent_mode;
@@ -267,6 +277,7 @@ struct radeon_encoder_atom_dig {
 	/* atom lvds */
 	uint32_t lvds_misc;
 	uint16_t panel_pwr_delay;
+	struct radeon_atom_ss *ss;
 	/* panel mode */
 	struct drm_display_mode native_mode;
 };
-- 
1.5.6.3

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay 
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to