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 <alexdeucher@gmail.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
		
							parent
							
								
									c290dadf4c
								
							
						
					
					
						commit
						ebbe1cb936
					
				| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
| @ -641,7 +685,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)) | ||||
|  | ||||
| @ -771,6 +771,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; | ||||
| @ -826,6 +866,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; | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user