Skip to content

Commit e38764f

Browse files
superm1Jiri Kosina
authored andcommitted
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 <[email protected]> Tested-by: Anson Tsao <[email protected]> Signed-off-by: Mario Limonciello <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 58c9bf3 commit e38764f

File tree

5 files changed

+122
-2
lines changed

5 files changed

+122
-2
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
What: /sys/bus/pci/drivers/pcie_mp2_amd/*/hpd
2+
Date: April 2025
3+
4+
Description:
5+
Human presence detection (HPD) enable/disable.
6+
When HPD is enabled, the device will be able to detect the
7+
presence of a human and will send an interrupt that can be
8+
used to wake the system from a low power state.
9+
When HPD is disabled, the device will not be able to detect
10+
the presence of a human.
11+
12+
Access: Read/Write
13+
Valid values: enabled/disabled

drivers/hid/amd-sfh-hid/amd_sfh_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct amd_mp2_sensor_info {
4242

4343
struct sfh_dev_status {
4444
bool is_hpd_present;
45+
bool is_hpd_enabled;
4546
bool is_als_present;
4647
bool is_sra_present;
4748
};

drivers/hid/amd-sfh-hid/amd_sfh_pcie.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/iopoll.h>
1919
#include <linux/module.h>
2020
#include <linux/slab.h>
21+
#include <linux/string_choices.h>
2122

2223
#include "amd_sfh_pcie.h"
2324
#include "sfh1_1/amd_sfh_init.h"
@@ -330,6 +331,57 @@ static const struct dmi_system_id dmi_nodevs[] = {
330331
{ }
331332
};
332333

334+
static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
335+
{
336+
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
337+
338+
return sysfs_emit(buf, "%s\n", str_enabled_disabled(mp2->dev_en.is_hpd_enabled));
339+
}
340+
341+
static ssize_t hpd_store(struct device *dev,
342+
struct device_attribute *attr,
343+
const char *buf, size_t count)
344+
{
345+
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
346+
bool enabled;
347+
int ret;
348+
349+
ret = kstrtobool(buf, &enabled);
350+
if (ret)
351+
return ret;
352+
353+
mp2->sfh1_1_ops->toggle_hpd(mp2, enabled);
354+
355+
return count;
356+
}
357+
static DEVICE_ATTR_RW(hpd);
358+
359+
static umode_t sfh_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
360+
{
361+
struct device *dev = kobj_to_dev(kobj);
362+
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
363+
364+
if (!mp2->sfh1_1_ops || !mp2->dev_en.is_hpd_present)
365+
return 0;
366+
367+
return attr->mode;
368+
}
369+
370+
static struct attribute *sfh_attrs[] = {
371+
&dev_attr_hpd.attr,
372+
NULL,
373+
};
374+
375+
static struct attribute_group sfh_attr_group = {
376+
.attrs = sfh_attrs,
377+
.is_visible = sfh_attr_is_visible,
378+
};
379+
380+
static const struct attribute_group *amd_sfh_groups[] = {
381+
&sfh_attr_group,
382+
NULL,
383+
};
384+
333385
static void sfh1_1_init_work(struct work_struct *work)
334386
{
335387
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)
341393

342394
amd_sfh_clear_intr(mp2);
343395
mp2->init_done = 1;
396+
397+
rc = sysfs_update_group(&mp2->pdev->dev.kobj, &sfh_attr_group);
398+
if (rc)
399+
dev_warn(&mp2->pdev->dev, "failed to update sysfs group\n");
400+
344401
}
345402

346403
static void sfh_init_work(struct work_struct *work)
@@ -487,6 +544,7 @@ static struct pci_driver amd_mp2_pci_driver = {
487544
.driver.pm = &amd_mp2_pm_ops,
488545
.shutdown = amd_sfh_shutdown,
489546
.remove = amd_sfh_remove,
547+
.dev_groups = amd_sfh_groups,
490548
};
491549
module_pci_driver(amd_mp2_pci_driver);
492550

drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
212212
switch (cl_data->sensor_idx[i]) {
213213
case HPD_IDX:
214214
privdata->dev_en.is_hpd_present = true;
215+
privdata->dev_en.is_hpd_enabled = true;
215216
break;
216217
case ALS_IDX:
217218
privdata->dev_en.is_als_present = true;
@@ -255,6 +256,10 @@ static void amd_sfh_resume(struct amd_mp2_dev *mp2)
255256
}
256257

257258
for (i = 0; i < cl_data->num_hid_devices; i++) {
259+
/* leave HPD alone; policy is controlled by sysfs */
260+
if (cl_data->sensor_idx[i] == HPD_IDX)
261+
continue;
262+
258263
if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
259264
info.sensor_idx = cl_data->sensor_idx[i];
260265
mp2->mp2_ops->start(mp2, info);
@@ -285,8 +290,10 @@ static void amd_sfh_suspend(struct amd_mp2_dev *mp2)
285290
}
286291

287292
for (i = 0; i < cl_data->num_hid_devices; i++) {
288-
if (cl_data->sensor_idx[i] != HPD_IDX &&
289-
cl_data->sensor_sts[i] == SENSOR_ENABLED) {
293+
/* leave HPD alone; policy is controlled by sysfs */
294+
if (cl_data->sensor_idx[i] == HPD_IDX)
295+
continue;
296+
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
290297
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
291298
status = amd_sfh_wait_for_response
292299
(mp2, cl_data->sensor_idx[i], DISABLE_SENSOR);
@@ -304,6 +311,44 @@ static void amd_sfh_suspend(struct amd_mp2_dev *mp2)
304311
amd_sfh_clear_intr(mp2);
305312
}
306313

314+
void amd_sfh_toggle_hpd(struct amd_mp2_dev *mp2, bool enabled)
315+
{
316+
struct amdtp_cl_data *cl_data = mp2->cl_data;
317+
struct amd_mp2_sensor_info info;
318+
int i, status;
319+
320+
if (mp2->dev_en.is_hpd_enabled == enabled)
321+
return;
322+
323+
for (i = 0; i < cl_data->num_hid_devices; i++) {
324+
if (cl_data->sensor_idx[i] != HPD_IDX)
325+
continue;
326+
info.sensor_idx = cl_data->sensor_idx[i];
327+
if (enabled) {
328+
mp2->mp2_ops->start(mp2, info);
329+
status = amd_sfh_wait_for_response
330+
(mp2, cl_data->sensor_idx[i], ENABLE_SENSOR);
331+
if (status == 0)
332+
status = SENSOR_ENABLED;
333+
if (status == SENSOR_ENABLED)
334+
cl_data->sensor_sts[i] = SENSOR_ENABLED;
335+
} else {
336+
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
337+
status = amd_sfh_wait_for_response
338+
(mp2, cl_data->sensor_idx[i], DISABLE_SENSOR);
339+
if (status == 0)
340+
status = SENSOR_DISABLED;
341+
if (status != SENSOR_ENABLED)
342+
cl_data->sensor_sts[i] = SENSOR_DISABLED;
343+
}
344+
dev_dbg(&mp2->pdev->dev, "toggle sid 0x%x (%s) status 0x%x\n",
345+
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
346+
cl_data->sensor_sts[i]);
347+
break;
348+
}
349+
mp2->dev_en.is_hpd_enabled = enabled;
350+
}
351+
307352
static void amd_mp2_pci_remove(void *privdata)
308353
{
309354
struct amd_mp2_dev *mp2 = privdata;

drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515

1616
struct amd_sfh1_1_ops {
1717
int (*init)(struct amd_mp2_dev *mp2);
18+
void (*toggle_hpd)(struct amd_mp2_dev *mp2, bool enable);
1819
};
1920

2021
int amd_sfh1_1_init(struct amd_mp2_dev *mp2);
22+
void amd_sfh_toggle_hpd(struct amd_mp2_dev *mp2, bool enabled);
2123

2224
static const struct amd_sfh1_1_ops __maybe_unused sfh1_1_ops = {
2325
.init = amd_sfh1_1_init,
26+
.toggle_hpd = amd_sfh_toggle_hpd,
2427
};
2528

2629
#endif

0 commit comments

Comments
 (0)