1659 lines · 46.1 KB · C
RawDownload
/* * 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: Rafał Miłecki <zajec5@gmail.com> *          Alex Deucher <alexdeucher@gmail.com> */#include <drm/drmP.h>#include "amdgpu.h"#include "amdgpu_drv.h"#include "amdgpu_pm.h"#include "amdgpu_dpm.h"#include "atom.h"#include <linux/power_supply.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h> #include "amd_powerplay.h" static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev); static const struct cg_flag_name clocks[] = {	{AMD_CG_SUPPORT_GFX_MGCG, "Graphics Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_GFX_MGLS, "Graphics Medium Grain memory Light Sleep"},	{AMD_CG_SUPPORT_GFX_CGCG, "Graphics Coarse Grain Clock Gating"},	{AMD_CG_SUPPORT_GFX_CGLS, "Graphics Coarse Grain memory Light Sleep"},	{AMD_CG_SUPPORT_GFX_CGTS, "Graphics Coarse Grain Tree Shader Clock Gating"},	{AMD_CG_SUPPORT_GFX_CGTS_LS, "Graphics Coarse Grain Tree Shader Light Sleep"},	{AMD_CG_SUPPORT_GFX_CP_LS, "Graphics Command Processor Light Sleep"},	{AMD_CG_SUPPORT_GFX_RLC_LS, "Graphics Run List Controller Light Sleep"},	{AMD_CG_SUPPORT_GFX_3D_CGCG, "Graphics 3D Coarse Grain Clock Gating"},	{AMD_CG_SUPPORT_GFX_3D_CGLS, "Graphics 3D Coarse Grain memory Light Sleep"},	{AMD_CG_SUPPORT_MC_LS, "Memory Controller Light Sleep"},	{AMD_CG_SUPPORT_MC_MGCG, "Memory Controller Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_SDMA_LS, "System Direct Memory Access Light Sleep"},	{AMD_CG_SUPPORT_SDMA_MGCG, "System Direct Memory Access Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_BIF_MGCG, "Bus Interface Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_BIF_LS, "Bus Interface Light Sleep"},	{AMD_CG_SUPPORT_UVD_MGCG, "Unified Video Decoder Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_VCE_MGCG, "Video Compression Engine Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_HDP_LS, "Host Data Path Light Sleep"},	{AMD_CG_SUPPORT_HDP_MGCG, "Host Data Path Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_DRM_MGCG, "Digital Right Management Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_DRM_LS, "Digital Right Management Light Sleep"},	{AMD_CG_SUPPORT_ROM_MGCG, "Rom Medium Grain Clock Gating"},	{AMD_CG_SUPPORT_DF_MGCG, "Data Fabric Medium Grain Clock Gating"},	{0, NULL},}; void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev){	if (adev->pp_enabled)		/* TODO */		return; 	if (adev->pm.dpm_enabled) {		mutex_lock(&adev->pm.mutex);		if (power_supply_is_system_supplied() > 0)			adev->pm.dpm.ac_power = true;		else			adev->pm.dpm.ac_power = false;		if (adev->pm.funcs->enable_bapm)			amdgpu_dpm_enable_bapm(adev, adev->pm.dpm.ac_power);		mutex_unlock(&adev->pm.mutex);	}} static ssize_t amdgpu_get_dpm_state(struct device *dev,				    struct device_attribute *attr,				    char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	enum amd_pm_state_type pm; 	if (adev->pp_enabled) {		pm = amdgpu_dpm_get_current_power_state(adev);	} else		pm = adev->pm.dpm.user_state; 	return snprintf(buf, PAGE_SIZE, "%s\n",			(pm == POWER_STATE_TYPE_BATTERY) ? "battery" :			(pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");} static ssize_t amdgpu_set_dpm_state(struct device *dev,				    struct device_attribute *attr,				    const char *buf,				    size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	enum amd_pm_state_type  state; 	if (strncmp("battery", buf, strlen("battery")) == 0)		state = POWER_STATE_TYPE_BATTERY;	else if (strncmp("balanced", buf, strlen("balanced")) == 0)		state = POWER_STATE_TYPE_BALANCED;	else if (strncmp("performance", buf, strlen("performance")) == 0)		state = POWER_STATE_TYPE_PERFORMANCE;	else {		count = -EINVAL;		goto fail;	} 	if (adev->pp_enabled) {		amdgpu_dpm_dispatch_task(adev, AMD_PP_EVENT_ENABLE_USER_STATE, &state, NULL);	} else {		mutex_lock(&adev->pm.mutex);		adev->pm.dpm.user_state = state;		mutex_unlock(&adev->pm.mutex); 		/* Can't set dpm state when the card is off */		if (!(adev->flags & AMD_IS_PX) ||		    (ddev->switch_power_state == DRM_SWITCH_POWER_ON))			amdgpu_pm_compute_clocks(adev);	}fail:	return count;} static ssize_t amdgpu_get_dpm_forced_performance_level(struct device *dev,						struct device_attribute *attr,								char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	enum amd_dpm_forced_level level; 	if  ((adev->flags & AMD_IS_PX) &&	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))		return snprintf(buf, PAGE_SIZE, "off\n"); 	level = amdgpu_dpm_get_performance_level(adev);	return snprintf(buf, PAGE_SIZE, "%s\n",			(level == AMD_DPM_FORCED_LEVEL_AUTO) ? "auto" :			(level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" :			(level == AMD_DPM_FORCED_LEVEL_HIGH) ? "high" :			(level == AMD_DPM_FORCED_LEVEL_MANUAL) ? "manual" :			(level == AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD) ? "profile_standard" :			(level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) ? "profile_min_sclk" :			(level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) ? "profile_min_mclk" :			(level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) ? "profile_peak" :			"unknown");} static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev,						       struct device_attribute *attr,						       const char *buf,						       size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	enum amd_dpm_forced_level level;	enum amd_dpm_forced_level current_level;	int ret = 0; 	/* Can't force performance level when the card is off */	if  ((adev->flags & AMD_IS_PX) &&	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))		return -EINVAL; 	current_level = amdgpu_dpm_get_performance_level(adev); 	if (strncmp("low", buf, strlen("low")) == 0) {		level = AMD_DPM_FORCED_LEVEL_LOW;	} else if (strncmp("high", buf, strlen("high")) == 0) {		level = AMD_DPM_FORCED_LEVEL_HIGH;	} else if (strncmp("auto", buf, strlen("auto")) == 0) {		level = AMD_DPM_FORCED_LEVEL_AUTO;	} else if (strncmp("manual", buf, strlen("manual")) == 0) {		level = AMD_DPM_FORCED_LEVEL_MANUAL;	} else if (strncmp("profile_exit", buf, strlen("profile_exit")) == 0) {		level = AMD_DPM_FORCED_LEVEL_PROFILE_EXIT;	} else if (strncmp("profile_standard", buf, strlen("profile_standard")) == 0) {		level = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD;	} else if (strncmp("profile_min_sclk", buf, strlen("profile_min_sclk")) == 0) {		level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK;	} else if (strncmp("profile_min_mclk", buf, strlen("profile_min_mclk")) == 0) {		level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK;	} else if (strncmp("profile_peak", buf, strlen("profile_peak")) == 0) {		level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;	}  else {		count = -EINVAL;		goto fail;	} 	if (current_level == level)		return count; 	if (adev->pp_enabled)		amdgpu_dpm_force_performance_level(adev, level);	else {		mutex_lock(&adev->pm.mutex);		if (adev->pm.dpm.thermal_active) {			count = -EINVAL;			mutex_unlock(&adev->pm.mutex);			goto fail;		}		ret = amdgpu_dpm_force_performance_level(adev, level);		if (ret)			count = -EINVAL;		else			adev->pm.dpm.forced_level = level;		mutex_unlock(&adev->pm.mutex);	} fail:	return count;} static ssize_t amdgpu_get_pp_num_states(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	struct pp_states_info data;	int i, buf_len; 	if (adev->pp_enabled)		amdgpu_dpm_get_pp_num_states(adev, &data); 	buf_len = snprintf(buf, PAGE_SIZE, "states: %d\n", data.nums);	for (i = 0; i < data.nums; i++)		buf_len += snprintf(buf + buf_len, PAGE_SIZE, "%d %s\n", i,				(data.states[i] == POWER_STATE_TYPE_INTERNAL_BOOT) ? "boot" :				(data.states[i] == POWER_STATE_TYPE_BATTERY) ? "battery" :				(data.states[i] == POWER_STATE_TYPE_BALANCED) ? "balanced" :				(data.states[i] == POWER_STATE_TYPE_PERFORMANCE) ? "performance" : "default"); 	return buf_len;} static ssize_t amdgpu_get_pp_cur_state(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	struct pp_states_info data;	enum amd_pm_state_type pm = 0;	int i = 0; 	if (adev->pp_enabled) { 		pm = amdgpu_dpm_get_current_power_state(adev);		amdgpu_dpm_get_pp_num_states(adev, &data); 		for (i = 0; i < data.nums; i++) {			if (pm == data.states[i])				break;		} 		if (i == data.nums)			i = -EINVAL;	} 	return snprintf(buf, PAGE_SIZE, "%d\n", i);} static ssize_t amdgpu_get_pp_force_state(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	struct pp_states_info data;	enum amd_pm_state_type pm = 0;	int i; 	if (adev->pp_force_state_enabled && adev->pp_enabled) {		pm = amdgpu_dpm_get_current_power_state(adev);		amdgpu_dpm_get_pp_num_states(adev, &data); 		for (i = 0; i < data.nums; i++) {			if (pm == data.states[i])				break;		} 		if (i == data.nums)			i = -EINVAL; 		return snprintf(buf, PAGE_SIZE, "%d\n", i); 	} else		return snprintf(buf, PAGE_SIZE, "\n");} static ssize_t amdgpu_set_pp_force_state(struct device *dev,		struct device_attribute *attr,		const char *buf,		size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	enum amd_pm_state_type state = 0;	unsigned long idx;	int ret; 	if (strlen(buf) == 1)		adev->pp_force_state_enabled = false;	else if (adev->pp_enabled) {		struct pp_states_info data; 		ret = kstrtoul(buf, 0, &idx);		if (ret || idx >= ARRAY_SIZE(data.states)) {			count = -EINVAL;			goto fail;		} 		amdgpu_dpm_get_pp_num_states(adev, &data);		state = data.states[idx];		/* only set user selected power states */		if (state != POWER_STATE_TYPE_INTERNAL_BOOT &&		    state != POWER_STATE_TYPE_DEFAULT) {			amdgpu_dpm_dispatch_task(adev,					AMD_PP_EVENT_ENABLE_USER_STATE, &state, NULL);			adev->pp_force_state_enabled = true;		}	}fail:	return count;} static ssize_t amdgpu_get_pp_table(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	char *table = NULL;	int size; 	if (adev->pp_enabled)		size = amdgpu_dpm_get_pp_table(adev, &table);	else		return 0; 	if (size >= PAGE_SIZE)		size = PAGE_SIZE - 1; 	memcpy(buf, table, size); 	return size;} static ssize_t amdgpu_set_pp_table(struct device *dev,		struct device_attribute *attr,		const char *buf,		size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private; 	if (adev->pp_enabled)		amdgpu_dpm_set_pp_table(adev, buf, count); 	return count;} static ssize_t amdgpu_get_pp_dpm_sclk(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	ssize_t size = 0; 	if (adev->pp_enabled)		size = amdgpu_dpm_print_clock_levels(adev, PP_SCLK, buf);	else if (adev->pm.funcs->print_clock_levels)		size = adev->pm.funcs->print_clock_levels(adev, PP_SCLK, buf); 	return size;} static ssize_t amdgpu_set_pp_dpm_sclk(struct device *dev,		struct device_attribute *attr,		const char *buf,		size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	int ret;	long level;	uint32_t i, mask = 0;	char sub_str[2]; 	for (i = 0; i < strlen(buf); i++) {		if (*(buf + i) == '\n')			continue;		sub_str[0] = *(buf + i);		sub_str[1] = '\0';		ret = kstrtol(sub_str, 0, &level); 		if (ret) {			count = -EINVAL;			goto fail;		}		mask |= 1 << level;	} 	if (adev->pp_enabled)		amdgpu_dpm_force_clock_level(adev, PP_SCLK, mask);	else if (adev->pm.funcs->force_clock_level)		adev->pm.funcs->force_clock_level(adev, PP_SCLK, mask);fail:	return count;} static ssize_t amdgpu_get_pp_dpm_mclk(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	ssize_t size = 0; 	if (adev->pp_enabled)		size = amdgpu_dpm_print_clock_levels(adev, PP_MCLK, buf);	else if (adev->pm.funcs->print_clock_levels)		size = adev->pm.funcs->print_clock_levels(adev, PP_MCLK, buf); 	return size;} static ssize_t amdgpu_set_pp_dpm_mclk(struct device *dev,		struct device_attribute *attr,		const char *buf,		size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	int ret;	long level;	uint32_t i, mask = 0;	char sub_str[2]; 	for (i = 0; i < strlen(buf); i++) {		if (*(buf + i) == '\n')			continue;		sub_str[0] = *(buf + i);		sub_str[1] = '\0';		ret = kstrtol(sub_str, 0, &level); 		if (ret) {			count = -EINVAL;			goto fail;		}		mask |= 1 << level;	} 	if (adev->pp_enabled)		amdgpu_dpm_force_clock_level(adev, PP_MCLK, mask);	else if (adev->pm.funcs->force_clock_level)		adev->pm.funcs->force_clock_level(adev, PP_MCLK, mask);fail:	return count;} static ssize_t amdgpu_get_pp_dpm_pcie(struct device *dev,		struct device_attribute *attr,		char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	ssize_t size = 0; 	if (adev->pp_enabled)		size = amdgpu_dpm_print_clock_levels(adev, PP_PCIE, buf);	else if (adev->pm.funcs->print_clock_levels)		size = adev->pm.funcs->print_clock_levels(adev, PP_PCIE, buf); 	return size;} static ssize_t amdgpu_set_pp_dpm_pcie(struct device *dev,		struct device_attribute *attr,		const char *buf,		size_t count){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = ddev->dev_private;	int ret;

Lines 1–500 of 1659.