mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	AMD SFH driver has APIs defined to export the ambient light information; use this within the PMF driver to send inputs to the PMF TA, so that PMF driver can enact to the actions coming from the TA. Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://lore.kernel.org/r/20240123141458.3715211-2-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
		
			
				
	
	
		
			194 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
/*
 | 
						|
 * AMD Platform Management Framework Driver - Smart PC Capabilities
 | 
						|
 *
 | 
						|
 * Copyright (c) 2023, Advanced Micro Devices, Inc.
 | 
						|
 * All Rights Reserved.
 | 
						|
 *
 | 
						|
 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
 | 
						|
 *          Patil Rajesh Reddy <Patil.Reddy@amd.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <acpi/button.h>
 | 
						|
#include <linux/amd-pmf-io.h>
 | 
						|
#include <linux/power_supply.h>
 | 
						|
#include <linux/units.h>
 | 
						|
#include "pmf.h"
 | 
						|
 | 
						|
#ifdef CONFIG_AMD_PMF_DEBUG
 | 
						|
static const char *ta_slider_as_str(unsigned int state)
 | 
						|
{
 | 
						|
	switch (state) {
 | 
						|
	case TA_BEST_PERFORMANCE:
 | 
						|
		return "PERFORMANCE";
 | 
						|
	case TA_BETTER_PERFORMANCE:
 | 
						|
		return "BALANCED";
 | 
						|
	case TA_BEST_BATTERY:
 | 
						|
		return "POWER_SAVER";
 | 
						|
	default:
 | 
						|
		return "Unknown TA Slider State";
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 | 
						|
{
 | 
						|
	dev_dbg(dev->dev, "==== TA inputs START ====\n");
 | 
						|
	dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
 | 
						|
	dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
 | 
						|
	dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
 | 
						|
	dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
 | 
						|
	dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
 | 
						|
	dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
 | 
						|
	dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
 | 
						|
	dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
 | 
						|
	dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
 | 
						|
	dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
 | 
						|
	dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
 | 
						|
	dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
 | 
						|
	dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
 | 
						|
	dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
 | 
						|
	dev_dbg(dev->dev, "==== TA inputs END ====\n");
 | 
						|
}
 | 
						|
#else
 | 
						|
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
 | 
						|
#endif
 | 
						|
 | 
						|
static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 | 
						|
{
 | 
						|
	u16 max, avg = 0;
 | 
						|
	int i;
 | 
						|
 | 
						|
	memset(dev->buf, 0, sizeof(dev->m_table));
 | 
						|
	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
 | 
						|
	memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
 | 
						|
 | 
						|
	in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
 | 
						|
	in->ev_info.skin_temperature = dev->m_table.skin_temp;
 | 
						|
 | 
						|
	/* Get the avg and max C0 residency of all the cores */
 | 
						|
	max = dev->m_table.avg_core_c0residency[0];
 | 
						|
	for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) {
 | 
						|
		avg += dev->m_table.avg_core_c0residency[i];
 | 
						|
		if (dev->m_table.avg_core_c0residency[i] > max)
 | 
						|
			max = dev->m_table.avg_core_c0residency[i];
 | 
						|
	}
 | 
						|
 | 
						|
	avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency));
 | 
						|
	in->ev_info.avg_c0residency = avg;
 | 
						|
	in->ev_info.max_c0residency = max;
 | 
						|
	in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
 | 
						|
}
 | 
						|
 | 
						|
static const char * const pmf_battery_supply_name[] = {
 | 
						|
	"BATT",
 | 
						|
	"BAT0",
 | 
						|
};
 | 
						|
 | 
						|
static int amd_pmf_get_battery_prop(enum power_supply_property prop)
 | 
						|
{
 | 
						|
	union power_supply_propval value;
 | 
						|
	struct power_supply *psy;
 | 
						|
	int i, ret;
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
 | 
						|
		psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
 | 
						|
		if (!psy)
 | 
						|
			continue;
 | 
						|
 | 
						|
		ret = power_supply_get_property(psy, prop, &value);
 | 
						|
		if (ret) {
 | 
						|
			power_supply_put(psy);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return value.intval;
 | 
						|
}
 | 
						|
 | 
						|
static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 | 
						|
{
 | 
						|
	int val;
 | 
						|
 | 
						|
	val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
 | 
						|
	if (val < 0)
 | 
						|
		return val;
 | 
						|
	if (val != 1)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
 | 
						|
	/* all values in mWh metrics */
 | 
						|
	in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
 | 
						|
		MILLIWATT_PER_WATT;
 | 
						|
	in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
 | 
						|
		MILLIWATT_PER_WATT;
 | 
						|
	in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
 | 
						|
		MILLIWATT_PER_WATT;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 | 
						|
{
 | 
						|
	int val;
 | 
						|
 | 
						|
	switch (dev->current_profile) {
 | 
						|
	case PLATFORM_PROFILE_PERFORMANCE:
 | 
						|
		val = TA_BEST_PERFORMANCE;
 | 
						|
		break;
 | 
						|
	case PLATFORM_PROFILE_BALANCED:
 | 
						|
		val = TA_BETTER_PERFORMANCE;
 | 
						|
		break;
 | 
						|
	case PLATFORM_PROFILE_LOW_POWER:
 | 
						|
		val = TA_BEST_BATTERY;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dev_err(dev->dev, "Unknown Platform Profile.\n");
 | 
						|
		return -EOPNOTSUPP;
 | 
						|
	}
 | 
						|
	in->ev_info.power_slider = val;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 | 
						|
{
 | 
						|
	struct amd_sfh_info sfh_info;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	/* Get ALS data */
 | 
						|
	ret = amd_get_sfh_info(&sfh_info, MT_ALS);
 | 
						|
	if (!ret)
 | 
						|
		in->ev_info.ambient_light = sfh_info.ambient_light;
 | 
						|
	else
 | 
						|
		return ret;
 | 
						|
 | 
						|
	/* get HPD data */
 | 
						|
	ret = amd_get_sfh_info(&sfh_info, MT_HPD);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	switch (sfh_info.user_present) {
 | 
						|
	case SFH_NOT_DETECTED:
 | 
						|
		in->ev_info.user_present = 0xff; /* assume no sensors connected */
 | 
						|
		break;
 | 
						|
	case SFH_USER_PRESENT:
 | 
						|
		in->ev_info.user_present = 1;
 | 
						|
		break;
 | 
						|
	case SFH_USER_AWAY:
 | 
						|
		in->ev_info.user_present = 0;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
 | 
						|
{
 | 
						|
	/* TA side lid open is 1 and close is 0, hence the ! here */
 | 
						|
	in->ev_info.lid_state = !acpi_lid_open();
 | 
						|
	in->ev_info.power_source = amd_pmf_get_power_source();
 | 
						|
	amd_pmf_get_smu_info(dev, in);
 | 
						|
	amd_pmf_get_battery_info(dev, in);
 | 
						|
	amd_pmf_get_slider_info(dev, in);
 | 
						|
	amd_pmf_get_sensor_info(dev, in);
 | 
						|
}
 |