969 lines · 35.5 KB · C
RawDownload
/* * Copyright 2011 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Alex Deucher */ #include <drm/drmP.h>#include "amdgpu.h"#include "amdgpu_atombios.h"#include "amdgpu_i2c.h"#include "amdgpu_dpm.h"#include "atom.h" void amdgpu_dpm_print_class_info(u32 class, u32 class2){	const char *s; 	switch (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) {	case ATOM_PPLIB_CLASSIFICATION_UI_NONE:	default:		s = "none";		break;	case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY:		s = "battery";		break;	case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED:		s = "balanced";		break;	case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE:		s = "performance";		break;	}	printk("\tui class: %s\n", s);	printk("\tinternal class:");	if (((class & ~ATOM_PPLIB_CLASSIFICATION_UI_MASK) == 0) &&	    (class2 == 0))		pr_cont(" none");	else {		if (class & ATOM_PPLIB_CLASSIFICATION_BOOT)			pr_cont(" boot");		if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL)			pr_cont(" thermal");		if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)			pr_cont(" limited_pwr");		if (class & ATOM_PPLIB_CLASSIFICATION_REST)			pr_cont(" rest");		if (class & ATOM_PPLIB_CLASSIFICATION_FORCED)			pr_cont(" forced");		if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)			pr_cont(" 3d_perf");		if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE)			pr_cont(" ovrdrv");		if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)			pr_cont(" uvd");		if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW)			pr_cont(" 3d_low");		if (class & ATOM_PPLIB_CLASSIFICATION_ACPI)			pr_cont(" acpi");		if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)			pr_cont(" uvd_hd2");		if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)			pr_cont(" uvd_hd");		if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)			pr_cont(" uvd_sd");		if (class2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)			pr_cont(" limited_pwr2");		if (class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)			pr_cont(" ulv");		if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)			pr_cont(" uvd_mvc");	}	pr_cont("\n");} void amdgpu_dpm_print_cap_info(u32 caps){	printk("\tcaps:");	if (caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY)		pr_cont(" single_disp");	if (caps & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK)		pr_cont(" video");	if (caps & ATOM_PPLIB_DISALLOW_ON_DC)		pr_cont(" no_dc");	pr_cont("\n");} void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev,				struct amdgpu_ps *rps){	printk("\tstatus:");	if (rps == adev->pm.dpm.current_ps)		pr_cont(" c");	if (rps == adev->pm.dpm.requested_ps)		pr_cont(" r");	if (rps == adev->pm.dpm.boot_ps)		pr_cont(" b");	pr_cont("\n");}  u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev){	struct drm_device *dev = adev->ddev;	struct drm_crtc *crtc;	struct amdgpu_crtc *amdgpu_crtc;	u32 vblank_in_pixels;	u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */ 	if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) {		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {			amdgpu_crtc = to_amdgpu_crtc(crtc);			if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) {				vblank_in_pixels =					amdgpu_crtc->hw_mode.crtc_htotal *					(amdgpu_crtc->hw_mode.crtc_vblank_end -					amdgpu_crtc->hw_mode.crtc_vdisplay +					(amdgpu_crtc->v_border * 2)); 				vblank_time_us = vblank_in_pixels * 1000 / amdgpu_crtc->hw_mode.clock;				break;			}		}	} 	return vblank_time_us;} u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev){	struct drm_device *dev = adev->ddev;	struct drm_crtc *crtc;	struct amdgpu_crtc *amdgpu_crtc;	u32 vrefresh = 0; 	if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) {		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {			amdgpu_crtc = to_amdgpu_crtc(crtc);			if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) {				vrefresh = drm_mode_vrefresh(&amdgpu_crtc->hw_mode);				break;			}		}	} 	return vrefresh;} void amdgpu_calculate_u_and_p(u32 i, u32 r_c, u32 p_b,			      u32 *p, u32 *u){	u32 b_c = 0;	u32 i_c;	u32 tmp; 	i_c = (i * r_c) / 100;	tmp = i_c >> p_b; 	while (tmp) {		b_c++;		tmp >>= 1;	} 	*u = (b_c + 1) / 2;	*p = i_c / (1 << (2 * (*u)));} int amdgpu_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th){	u32 k, a, ah, al;	u32 t1; 	if ((fl == 0) || (fh == 0) || (fl > fh))		return -EINVAL; 	k = (100 * fh) / fl;	t1 = (t * (k - 100));	a = (1000 * (100 * h + t1)) / (10000 + (t1 / 100));	a = (a + 5) / 10;	ah = ((a * t) + 5000) / 10000;	al = a - ah; 	*th = t - ah;	*tl = t + al; 	return 0;} bool amdgpu_is_uvd_state(u32 class, u32 class2){	if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)		return true;	if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)		return true;	if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)		return true;	if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)		return true;	if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)		return true;	return false;} bool amdgpu_is_internal_thermal_sensor(enum amdgpu_int_thermal_type sensor){	switch (sensor) {	case THERMAL_TYPE_RV6XX:	case THERMAL_TYPE_RV770:	case THERMAL_TYPE_EVERGREEN:	case THERMAL_TYPE_SUMO:	case THERMAL_TYPE_NI:	case THERMAL_TYPE_SI:	case THERMAL_TYPE_CI:	case THERMAL_TYPE_KV:		return true;	case THERMAL_TYPE_ADT7473_WITH_INTERNAL:	case THERMAL_TYPE_EMC2103_WITH_INTERNAL:		return false; /* need special handling */	case THERMAL_TYPE_NONE:	case THERMAL_TYPE_EXTERNAL:	case THERMAL_TYPE_EXTERNAL_GPIO:	default:		return false;	}} union power_info {	struct _ATOM_POWERPLAY_INFO info;	struct _ATOM_POWERPLAY_INFO_V2 info_2;	struct _ATOM_POWERPLAY_INFO_V3 info_3;	struct _ATOM_PPLIB_POWERPLAYTABLE pplib;	struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;	struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;	struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4;	struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5;}; union fan_info {	struct _ATOM_PPLIB_FANTABLE fan;	struct _ATOM_PPLIB_FANTABLE2 fan2;	struct _ATOM_PPLIB_FANTABLE3 fan3;}; static int amdgpu_parse_clk_voltage_dep_table(struct amdgpu_clock_voltage_dependency_table *amdgpu_table,					      ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table){	u32 size = atom_table->ucNumEntries *		sizeof(struct amdgpu_clock_voltage_dependency_entry);	int i;	ATOM_PPLIB_Clock_Voltage_Dependency_Record *entry; 	amdgpu_table->entries = kzalloc(size, GFP_KERNEL);	if (!amdgpu_table->entries)		return -ENOMEM; 	entry = &atom_table->entries[0];	for (i = 0; i < atom_table->ucNumEntries; i++) {		amdgpu_table->entries[i].clk = le16_to_cpu(entry->usClockLow) |			(entry->ucClockHigh << 16);		amdgpu_table->entries[i].v = le16_to_cpu(entry->usVoltage);		entry = (ATOM_PPLIB_Clock_Voltage_Dependency_Record *)			((u8 *)entry + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Record));	}	amdgpu_table->count = atom_table->ucNumEntries; 	return 0;} int amdgpu_get_platform_caps(struct amdgpu_device *adev){	struct amdgpu_mode_info *mode_info = &adev->mode_info;	union power_info *power_info;	int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);	u16 data_offset;	u8 frev, crev; 	if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,				   &frev, &crev, &data_offset))		return -EINVAL;	power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); 	adev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);	adev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);	adev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); 	return 0;} /* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8 24#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V9 26 int amdgpu_parse_extended_power_table(struct amdgpu_device *adev){	struct amdgpu_mode_info *mode_info = &adev->mode_info;	union power_info *power_info;	union fan_info *fan_info;	ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table;	int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);	u16 data_offset;	u8 frev, crev;	int ret, i; 	if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,				   &frev, &crev, &data_offset))		return -EINVAL;	power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); 	/* fan table */	if (le16_to_cpu(power_info->pplib.usTableSize) >=	    sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) {		if (power_info->pplib3.usFanTableOffset) {			fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset +						      le16_to_cpu(power_info->pplib3.usFanTableOffset));			adev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst;			adev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin);			adev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed);			adev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh);			adev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin);			adev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed);			adev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh);			if (fan_info->fan.ucFanTableFormat >= 2)				adev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax);			else				adev->pm.dpm.fan.t_max = 10900;			adev->pm.dpm.fan.cycle_delay = 100000;			if (fan_info->fan.ucFanTableFormat >= 3) {				adev->pm.dpm.fan.control_mode = fan_info->fan3.ucFanControlMode;				adev->pm.dpm.fan.default_max_fan_pwm =					le16_to_cpu(fan_info->fan3.usFanPWMMax);				adev->pm.dpm.fan.default_fan_output_sensitivity = 4836;				adev->pm.dpm.fan.fan_output_sensitivity =					le16_to_cpu(fan_info->fan3.usFanOutputSensitivity);			}			adev->pm.dpm.fan.ucode_fan_control = true;		}	} 	/* clock dependancy tables, shedding tables */	if (le16_to_cpu(power_info->pplib.usTableSize) >=	    sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) {		if (power_info->pplib4.usVddcDependencyOnSCLKOffset) {			dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset));			ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk,								 dep_table);			if (ret) {				amdgpu_free_extended_power_table(adev);				return ret;			}		}		if (power_info->pplib4.usVddciDependencyOnMCLKOffset) {			dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset));			ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddci_dependency_on_mclk,								 dep_table);			if (ret) {				amdgpu_free_extended_power_table(adev);				return ret;			}		}		if (power_info->pplib4.usVddcDependencyOnMCLKOffset) {			dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset));			ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,								 dep_table);			if (ret) {				amdgpu_free_extended_power_table(adev);				return ret;			}		}		if (power_info->pplib4.usMvddDependencyOnMCLKOffset) {			dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib4.usMvddDependencyOnMCLKOffset));			ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.mvdd_dependency_on_mclk,								 dep_table);			if (ret) {				amdgpu_free_extended_power_table(adev);				return ret;			}		}		if (power_info->pplib4.usMaxClockVoltageOnDCOffset) {			ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v =				(ATOM_PPLIB_Clock_Voltage_Limit_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset));			if (clk_v->ucNumEntries) {				adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk =					le16_to_cpu(clk_v->entries[0].usSclkLow) |					(clk_v->entries[0].ucSclkHigh << 16);				adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk =					le16_to_cpu(clk_v->entries[0].usMclkLow) |					(clk_v->entries[0].ucMclkHigh << 16);				adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc =					le16_to_cpu(clk_v->entries[0].usVddc);				adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci =					le16_to_cpu(clk_v->entries[0].usVddci);			}		}		if (power_info->pplib4.usVddcPhaseShedLimitsTableOffset) {			ATOM_PPLIB_PhaseSheddingLimits_Table *psl =				(ATOM_PPLIB_PhaseSheddingLimits_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib4.usVddcPhaseShedLimitsTableOffset));			ATOM_PPLIB_PhaseSheddingLimits_Record *entry; 			adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries =				kzalloc(psl->ucNumEntries *					sizeof(struct amdgpu_phase_shedding_limits_entry),					GFP_KERNEL);			if (!adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) {				amdgpu_free_extended_power_table(adev);				return -ENOMEM;			} 			entry = &psl->entries[0];			for (i = 0; i < psl->ucNumEntries; i++) {				adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].sclk =					le16_to_cpu(entry->usSclkLow) | (entry->ucSclkHigh << 16);				adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].mclk =					le16_to_cpu(entry->usMclkLow) | (entry->ucMclkHigh << 16);				adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].voltage =					le16_to_cpu(entry->usVoltage);				entry = (ATOM_PPLIB_PhaseSheddingLimits_Record *)					((u8 *)entry + sizeof(ATOM_PPLIB_PhaseSheddingLimits_Record));			}			adev->pm.dpm.dyn_state.phase_shedding_limits_table.count =				psl->ucNumEntries;		}	} 	/* cac data */	if (le16_to_cpu(power_info->pplib.usTableSize) >=	    sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) {		adev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit);		adev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit);		adev->pm.dpm.near_tdp_limit_adjusted = adev->pm.dpm.near_tdp_limit;		adev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit);		if (adev->pm.dpm.tdp_od_limit)			adev->pm.dpm.power_control = true;		else			adev->pm.dpm.power_control = false;		adev->pm.dpm.tdp_adjustment = 0;		adev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold);		adev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage);		adev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope);		if (power_info->pplib5.usCACLeakageTableOffset) {			ATOM_PPLIB_CAC_Leakage_Table *cac_table =				(ATOM_PPLIB_CAC_Leakage_Table *)				(mode_info->atom_context->bios + data_offset +				 le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset));			ATOM_PPLIB_CAC_Leakage_Record *entry;			u32 size = cac_table->ucNumEntries * sizeof(struct amdgpu_cac_leakage_table);			adev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL);			if (!adev->pm.dpm.dyn_state.cac_leakage_table.entries) {				amdgpu_free_extended_power_table(adev);				return -ENOMEM;			}			entry = &cac_table->entries[0];			for (i = 0; i < cac_table->ucNumEntries; i++) {				if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) {					adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc1 =						le16_to_cpu(entry->usVddc1);					adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc2 =						le16_to_cpu(entry->usVddc2);					adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc3 =						le16_to_cpu(entry->usVddc3);				} else {					adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc =						le16_to_cpu(entry->usVddc);					adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage =						le32_to_cpu(entry->ulLeakageValue);				}

Lines 1–500 of 969.