mirror of
https://github.com/torvalds/linux.git
synced 2025-04-12 06:49:52 +00:00
HID: amd_sfh: Allow configuring whether HPD is enabled or disabled
Human presence detection (HPD) sensor uses a camera to determine when user is physically in front of the machine. This might not be a desirable behavior because it can (for example) cause the machine to wake on approach. Add a new sysfs file "hpd" that will control whether this sensor is enabled. Use the value of this sysfs file to turn off HPD and prevent it from re-enabling after resume from suspend. Cc: Pratap Nirujogi <pratap.nirujogi@amd.com> Tested-by: Anson Tsao <anson.tsao@amd.com> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
parent
58c9bf3363
commit
e38764f6db
13
Documentation/ABI/testing/sysfs-driver-amd-sfh
Normal file
13
Documentation/ABI/testing/sysfs-driver-amd-sfh
Normal file
@ -0,0 +1,13 @@
|
||||
What: /sys/bus/pci/drivers/pcie_mp2_amd/*/hpd
|
||||
Date: April 2025
|
||||
Contact: mario.limonciello@amd.com
|
||||
Description:
|
||||
Human presence detection (HPD) enable/disable.
|
||||
When HPD is enabled, the device will be able to detect the
|
||||
presence of a human and will send an interrupt that can be
|
||||
used to wake the system from a low power state.
|
||||
When HPD is disabled, the device will not be able to detect
|
||||
the presence of a human.
|
||||
|
||||
Access: Read/Write
|
||||
Valid values: enabled/disabled
|
@ -42,6 +42,7 @@ struct amd_mp2_sensor_info {
|
||||
|
||||
struct sfh_dev_status {
|
||||
bool is_hpd_present;
|
||||
bool is_hpd_enabled;
|
||||
bool is_als_present;
|
||||
bool is_sra_present;
|
||||
};
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include "amd_sfh_pcie.h"
|
||||
#include "sfh1_1/amd_sfh_init.h"
|
||||
@ -330,6 +331,57 @@ static const struct dmi_system_id dmi_nodevs[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%s\n", str_enabled_disabled(mp2->dev_en.is_hpd_enabled));
|
||||
}
|
||||
|
||||
static ssize_t hpd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
|
||||
bool enabled;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &enabled);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mp2->sfh1_1_ops->toggle_hpd(mp2, enabled);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(hpd);
|
||||
|
||||
static umode_t sfh_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
|
||||
|
||||
if (!mp2->sfh1_1_ops || !mp2->dev_en.is_hpd_present)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute *sfh_attrs[] = {
|
||||
&dev_attr_hpd.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group sfh_attr_group = {
|
||||
.attrs = sfh_attrs,
|
||||
.is_visible = sfh_attr_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *amd_sfh_groups[] = {
|
||||
&sfh_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void sfh1_1_init_work(struct work_struct *work)
|
||||
{
|
||||
struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work);
|
||||
@ -341,6 +393,11 @@ static void sfh1_1_init_work(struct work_struct *work)
|
||||
|
||||
amd_sfh_clear_intr(mp2);
|
||||
mp2->init_done = 1;
|
||||
|
||||
rc = sysfs_update_group(&mp2->pdev->dev.kobj, &sfh_attr_group);
|
||||
if (rc)
|
||||
dev_warn(&mp2->pdev->dev, "failed to update sysfs group\n");
|
||||
|
||||
}
|
||||
|
||||
static void sfh_init_work(struct work_struct *work)
|
||||
@ -487,6 +544,7 @@ static struct pci_driver amd_mp2_pci_driver = {
|
||||
.driver.pm = &amd_mp2_pm_ops,
|
||||
.shutdown = amd_sfh_shutdown,
|
||||
.remove = amd_sfh_remove,
|
||||
.dev_groups = amd_sfh_groups,
|
||||
};
|
||||
module_pci_driver(amd_mp2_pci_driver);
|
||||
|
||||
|
@ -212,6 +212,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
|
||||
switch (cl_data->sensor_idx[i]) {
|
||||
case HPD_IDX:
|
||||
privdata->dev_en.is_hpd_present = true;
|
||||
privdata->dev_en.is_hpd_enabled = true;
|
||||
break;
|
||||
case ALS_IDX:
|
||||
privdata->dev_en.is_als_present = true;
|
||||
@ -255,6 +256,10 @@ static void amd_sfh_resume(struct amd_mp2_dev *mp2)
|
||||
}
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
/* leave HPD alone; policy is controlled by sysfs */
|
||||
if (cl_data->sensor_idx[i] == HPD_IDX)
|
||||
continue;
|
||||
|
||||
if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
|
||||
info.sensor_idx = cl_data->sensor_idx[i];
|
||||
mp2->mp2_ops->start(mp2, info);
|
||||
@ -285,8 +290,10 @@ static void amd_sfh_suspend(struct amd_mp2_dev *mp2)
|
||||
}
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
if (cl_data->sensor_idx[i] != HPD_IDX &&
|
||||
cl_data->sensor_sts[i] == SENSOR_ENABLED) {
|
||||
/* leave HPD alone; policy is controlled by sysfs */
|
||||
if (cl_data->sensor_idx[i] == HPD_IDX)
|
||||
continue;
|
||||
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
|
||||
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
|
||||
status = amd_sfh_wait_for_response
|
||||
(mp2, cl_data->sensor_idx[i], DISABLE_SENSOR);
|
||||
@ -304,6 +311,44 @@ static void amd_sfh_suspend(struct amd_mp2_dev *mp2)
|
||||
amd_sfh_clear_intr(mp2);
|
||||
}
|
||||
|
||||
void amd_sfh_toggle_hpd(struct amd_mp2_dev *mp2, bool enabled)
|
||||
{
|
||||
struct amdtp_cl_data *cl_data = mp2->cl_data;
|
||||
struct amd_mp2_sensor_info info;
|
||||
int i, status;
|
||||
|
||||
if (mp2->dev_en.is_hpd_enabled == enabled)
|
||||
return;
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
if (cl_data->sensor_idx[i] != HPD_IDX)
|
||||
continue;
|
||||
info.sensor_idx = cl_data->sensor_idx[i];
|
||||
if (enabled) {
|
||||
mp2->mp2_ops->start(mp2, info);
|
||||
status = amd_sfh_wait_for_response
|
||||
(mp2, cl_data->sensor_idx[i], ENABLE_SENSOR);
|
||||
if (status == 0)
|
||||
status = SENSOR_ENABLED;
|
||||
if (status == SENSOR_ENABLED)
|
||||
cl_data->sensor_sts[i] = SENSOR_ENABLED;
|
||||
} else {
|
||||
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
|
||||
status = amd_sfh_wait_for_response
|
||||
(mp2, cl_data->sensor_idx[i], DISABLE_SENSOR);
|
||||
if (status == 0)
|
||||
status = SENSOR_DISABLED;
|
||||
if (status != SENSOR_ENABLED)
|
||||
cl_data->sensor_sts[i] = SENSOR_DISABLED;
|
||||
}
|
||||
dev_dbg(&mp2->pdev->dev, "toggle sid 0x%x (%s) status 0x%x\n",
|
||||
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
|
||||
cl_data->sensor_sts[i]);
|
||||
break;
|
||||
}
|
||||
mp2->dev_en.is_hpd_enabled = enabled;
|
||||
}
|
||||
|
||||
static void amd_mp2_pci_remove(void *privdata)
|
||||
{
|
||||
struct amd_mp2_dev *mp2 = privdata;
|
||||
|
@ -15,12 +15,15 @@
|
||||
|
||||
struct amd_sfh1_1_ops {
|
||||
int (*init)(struct amd_mp2_dev *mp2);
|
||||
void (*toggle_hpd)(struct amd_mp2_dev *mp2, bool enable);
|
||||
};
|
||||
|
||||
int amd_sfh1_1_init(struct amd_mp2_dev *mp2);
|
||||
void amd_sfh_toggle_hpd(struct amd_mp2_dev *mp2, bool enabled);
|
||||
|
||||
static const struct amd_sfh1_1_ops __maybe_unused sfh1_1_ops = {
|
||||
.init = amd_sfh1_1_init,
|
||||
.toggle_hpd = amd_sfh_toggle_hpd,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user