* [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
@ 2026-04-27 6:22 Helge Bahmann
2026-05-12 16:06 ` Jiri Kosina
2026-06-10 16:33 ` [PATCH] amd-sfh-hid: tablet mode switch and asus quirk Jiri Kosina
0 siblings, 2 replies; 13+ messages in thread
From: Helge Bahmann @ 2026-04-27 6:22 UTC (permalink / raw)
To: Nehal Bakulchandra Shah, Sandeep Singh, Basavaraj Natikar, jikos,
bentiss
Cc: linux-hid, linux-input
Add an input driver that interprets the "operation mode" sensor offered
by the amd sfh on some laptop models.
Add a quirk to make the driver work again with the Asus VivoBook
VivoBook (turn off the "disable interrupts" flag).
Expose the intr_disable flag as a module parameter in case it turns out
to be needed on further laptop models.
Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
---
drivers/hid/amd-sfh-hid/amd_sfh_client.c | 25 ++++++++------
drivers/hid/amd-sfh-hid/amd_sfh_hid.c | 43 ++++++++++++++++++++++++
drivers/hid/amd-sfh-hid/amd_sfh_hid.h | 6 ++++
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 9 +++++
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 3 ++
5 files changed, 75 insertions(+), 11 deletions(-)
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
index 7017bfa59093..a24757c5a203 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
@@ -128,10 +128,16 @@ void amd_sfh_work_buffer(struct work_struct *work)
guard(mutex)(&mp2->lock);
for (i = 0; i < cli_data->num_hid_devices; i++) {
if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {
- report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],
- cli_data->report_id[i], in_data);
- hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
- in_data->input_report[i], report_size, 0);
+ if (cli_data->hid_sensor_hubs[i]) {
+ report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],
+ cli_data->report_id[i],
+ in_data);
+ hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
+ in_data->input_report[i], report_size, 0);
+ } else if (cli_data->sensor_idx[i] == op_idx &&
+ cli_data->modeswitch_input) {
+ amdtp_modeswitch_report(i, cli_data->modeswitch_input, in_data);
+ }
}
}
schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
@@ -327,15 +333,12 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
for (i = 0; i < cl_data->num_hid_devices; i++) {
cl_data->cur_hid_dev = i;
- if (cl_data->sensor_idx[i] == op_idx) {
- dev_dbg(dev, "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]);
- continue;
- }
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
- rc = amdtp_hid_probe(i, cl_data);
+ if (cl_data->sensor_idx[i] != op_idx)
+ rc = amdtp_hid_probe(i, cl_data);
+ else
+ rc = amdtp_modeswitch_probe(i, cl_data);
if (rc)
goto cleanup;
} else {
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
index 81f3024b7b1b..44008c02b63c 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
@@ -181,4 +181,47 @@ void amdtp_hid_remove(struct amdtp_cl_data *cli_data)
cli_data->hid_sensor_hubs[i] = NULL;
}
}
+
+ /* note: cli_data->modeswitch_input implicitly cleaned by devres */
+}
+
+int amdtp_modeswitch_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
+{
+ struct amd_mp2_dev *mp2 = container_of(cli_data->in_data, struct amd_mp2_dev, in_data);
+ struct device *dev = &mp2->pdev->dev;
+ struct input_dev *input;
+ int rc;
+
+ input = devm_input_allocate_device(dev);
+ if (IS_ERR(input))
+ return PTR_ERR(input);
+
+ input->name = "AMD SFH tablet mode switch sensor";
+ input->id.bustype = BUS_PCI;
+
+ input_set_capability(input, EV_SW, SW_TABLET_MODE);
+
+ rc = input_register_device(input);
+ if (rc)
+ goto cleanup;
+
+ cli_data->modeswitch_input = input;
+
+ return 0;
+
+cleanup:
+ return rc;
+}
+
+void amdtp_modeswitch_report(u32 index, struct input_dev *input, struct amd_input_data *in_data)
+{
+ u32 *sensor_virt_addr = in_data->sensor_virt_addr[index];
+ u32 value = sensor_virt_addr[0];
+
+ if (value == AMD_SFH_OP_IDX_MODE_TABLET)
+ input_report_switch(input, SW_TABLET_MODE, 1);
+ else
+ input_report_switch(input, SW_TABLET_MODE, 0);
+
+ input_sync(input);
}
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
index 7452b0302953..20aff7b75fbd 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
@@ -11,6 +11,8 @@
#ifndef AMDSFH_HID_H
#define AMDSFH_HID_H
+#include <linux/input.h>
+
#define MAX_HID_DEVICES 7
#define AMD_SFH_HID_VENDOR 0x1022
#define AMD_SFH_HID_PRODUCT 0x0001
@@ -50,6 +52,7 @@ struct amdtp_cl_data {
u8 sensor_idx[MAX_HID_DEVICES];
u8 *feature_report[MAX_HID_DEVICES];
u8 request_done[MAX_HID_DEVICES];
+ struct input_dev *modeswitch_input;
struct amd_input_data *in_data;
struct delayed_work work;
struct delayed_work work_buffer;
@@ -78,4 +81,7 @@ void amdtp_hid_remove(struct amdtp_cl_data *cli_data);
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type);
void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type);
void amdtp_hid_wakeup(struct hid_device *hid);
+int amdtp_modeswitch_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data);
+void amdtp_modeswitch_report(u32 index, struct input_dev *input, struct amd_input_data *in_data);
+
#endif
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
index 1d9f955573aa..cd9cff75f114 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
@@ -39,6 +39,8 @@ module_param_named(sensor_mask, sensor_mask_override, int, 0444);
MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
static bool intr_disable = true;
+module_param_named(intr_disable, intr_disable, bool, 0444);
+MODULE_PARM_DESC(intr_disable, "override the interrupt disable sensor bit");
static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
@@ -317,6 +319,13 @@ static const struct dmi_system_id dmi_sfh_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"),
},
},
+ {
+ .callback = mp2_disable_intr,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VivoBook_ASUSLaptop TP420UA_TM420UA"),
+ }
+ },
{}
};
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
index 2eb61f4e8434..5e968894ebe4 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
@@ -83,6 +83,9 @@ enum sensor_idx {
als_idx = 19
};
+#define AMD_SFH_OP_IDX_MODE_LAPTOP 1
+#define AMD_SFH_OP_IDX_MODE_TABLET 3
+
enum mem_use_type {
USE_DRAM,
USE_C2P_REG,
--
2.47.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
2026-04-27 6:22 [PATCH] amd-sfh-hid: tablet mode switch and asus quirk Helge Bahmann
@ 2026-05-12 16:06 ` Jiri Kosina
2026-05-12 17:09 ` Basavaraj Natikar
2026-05-14 7:59 ` Helge Bahmann
2026-06-10 16:33 ` [PATCH] amd-sfh-hid: tablet mode switch and asus quirk Jiri Kosina
1 sibling, 2 replies; 13+ messages in thread
From: Jiri Kosina @ 2026-05-12 16:06 UTC (permalink / raw)
To: Helge Bahmann
Cc: Nehal Bakulchandra Shah, Sandeep Singh, Basavaraj Natikar,
bentiss, linux-hid, linux-input
On Mon, 27 Apr 2026, Helge Bahmann wrote:
> Add an input driver that interprets the "operation mode" sensor offered
> by the amd sfh on some laptop models.
>
> Add a quirk to make the driver work again with the Asus VivoBook
> VivoBook (turn off the "disable interrupts" flag).
>
> Expose the intr_disable flag as a module parameter in case it turns out
> to be needed on further laptop models.
>
> Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
Basavaraj, can you please review this one?
Thanks,
--
Jiri Kosina
SUSE Labs
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
2026-05-12 16:06 ` Jiri Kosina
@ 2026-05-12 17:09 ` Basavaraj Natikar
2026-05-14 7:59 ` Helge Bahmann
1 sibling, 0 replies; 13+ messages in thread
From: Basavaraj Natikar @ 2026-05-12 17:09 UTC (permalink / raw)
To: Jiri Kosina, Helge Bahmann
Cc: Nehal Bakulchandra Shah, Sandeep Singh, Basavaraj Natikar,
bentiss, linux-hid, linux-input
On 5/12/2026 9:36 PM, Jiri Kosina wrote:
> On Mon, 27 Apr 2026, Helge Bahmann wrote:
>
>> Add an input driver that interprets the "operation mode" sensor offered
>> by the amd sfh on some laptop models.
>>
>> Add a quirk to make the driver work again with the Asus VivoBook
>> VivoBook (turn off the "disable interrupts" flag).
>>
>> Expose the intr_disable flag as a module parameter in case it turns out
>> to be needed on further laptop models.
>>
>> Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
> Basavaraj, can you please review this one?
Hi Jiri,
Sure, will review. The patch has some changes that deviate from how
other sensors are exposed in this driver, so it will take some time to
review properly. I'll respond with detailed feedback once done.
Thanks,
--
Basavaraj
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
2026-05-12 16:06 ` Jiri Kosina
2026-05-12 17:09 ` Basavaraj Natikar
@ 2026-05-14 7:59 ` Helge Bahmann
2026-06-10 17:12 ` Basavaraj Natikar
1 sibling, 1 reply; 13+ messages in thread
From: Helge Bahmann @ 2026-05-14 7:59 UTC (permalink / raw)
To: Jiri Kosina, linux-input
Cc: Nehal Bakulchandra Shah, Sandeep Singh, Basavaraj Natikar,
bentiss
On Tue, 12 May 2026, Jiri Kosina wrote:
> On Mon, 27 Apr 2026, Helge Bahmann wrote:
>
> > Add an input driver that interprets the "operation mode" sensor offered
> > by the amd sfh on some laptop models.
> >
> > Add a quirk to make the driver work again with the Asus VivoBook
> > VivoBook (turn off the "disable interrupts" flag).
> >
> > Expose the intr_disable flag as a module parameter in case it turns out
> > to be needed on further laptop models.
> >
> > Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
>
> Basavaraj, can you please review this one?
Some additional context, maybe helpful for review:
1. The numbers and behavior were extracted from the ACPI tables
(WMI driver of sorts) of the notebook; I don't have access to any
official AMD / ASUS docs or similar.
2. I have an alternate version of this change that is more indirect:
- create a HID driver providing an "abstract table mode" message
- have an input driver attaching to this newly defined HID driver
While that is keeping "more in line" with the current driver
architecture, I am not sure this indirection really helps. Particularly,
there is no "canonical" HID tablet mode switch message defined,
so it all remains completely bespoke. I am happy to change it if
you prefer, but would need your input.
3. Since this is based on Asus VivoBook and its ACPI tables,
there is a possibility that this "op sensor / tablet mode" behavior
is not as universal as I surmise. A point could be made to make this
entire behavior model-dependent (with a mod param to override
/ activate for other models). Happy to take input / advice.
Thanks for your work!
Helge
>
> Thanks,
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
2026-04-27 6:22 [PATCH] amd-sfh-hid: tablet mode switch and asus quirk Helge Bahmann
2026-05-12 16:06 ` Jiri Kosina
@ 2026-06-10 16:33 ` Jiri Kosina
1 sibling, 0 replies; 13+ messages in thread
From: Jiri Kosina @ 2026-06-10 16:33 UTC (permalink / raw)
To: Helge Bahmann
Cc: Nehal Bakulchandra Shah, Sandeep Singh, Basavaraj Natikar,
Benjamin Tissoires, linux-hid, linux-input
On Mon, 27 Apr 2026, Helge Bahmann wrote:
> Add an input driver that interprets the "operation mode" sensor offered
> by the amd sfh on some laptop models.
>
> Add a quirk to make the driver work again with the Asus VivoBook
> VivoBook (turn off the "disable interrupts" flag).
>
> Expose the intr_disable flag as a module parameter in case it turns out
> to be needed on further laptop models.
>
> Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
Basavaraj, could you please review this one? Thanks.
> ---
> drivers/hid/amd-sfh-hid/amd_sfh_client.c | 25 ++++++++------
> drivers/hid/amd-sfh-hid/amd_sfh_hid.c | 43 ++++++++++++++++++++++++
> drivers/hid/amd-sfh-hid/amd_sfh_hid.h | 6 ++++
> drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 9 +++++
> drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 3 ++
> 5 files changed, 75 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
> index 7017bfa59093..a24757c5a203 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
> @@ -128,10 +128,16 @@ void amd_sfh_work_buffer(struct work_struct *work)
> guard(mutex)(&mp2->lock);
> for (i = 0; i < cli_data->num_hid_devices; i++) {
> if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {
> - report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],
> - cli_data->report_id[i], in_data);
> - hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
> - in_data->input_report[i], report_size, 0);
> + if (cli_data->hid_sensor_hubs[i]) {
> + report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],
> + cli_data->report_id[i],
> + in_data);
> + hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
> + in_data->input_report[i], report_size, 0);
> + } else if (cli_data->sensor_idx[i] == op_idx &&
> + cli_data->modeswitch_input) {
> + amdtp_modeswitch_report(i, cli_data->modeswitch_input, in_data);
> + }
> }
> }
> schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
> @@ -327,15 +333,12 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
>
> for (i = 0; i < cl_data->num_hid_devices; i++) {
> cl_data->cur_hid_dev = i;
> - if (cl_data->sensor_idx[i] == op_idx) {
> - dev_dbg(dev, "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]);
> - continue;
> - }
>
> if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
> - rc = amdtp_hid_probe(i, cl_data);
> + if (cl_data->sensor_idx[i] != op_idx)
> + rc = amdtp_hid_probe(i, cl_data);
> + else
> + rc = amdtp_modeswitch_probe(i, cl_data);
> if (rc)
> goto cleanup;
> } else {
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
> index 81f3024b7b1b..44008c02b63c 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
> @@ -181,4 +181,47 @@ void amdtp_hid_remove(struct amdtp_cl_data *cli_data)
> cli_data->hid_sensor_hubs[i] = NULL;
> }
> }
> +
> + /* note: cli_data->modeswitch_input implicitly cleaned by devres */
> +}
> +
> +int amdtp_modeswitch_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
> +{
> + struct amd_mp2_dev *mp2 = container_of(cli_data->in_data, struct amd_mp2_dev, in_data);
> + struct device *dev = &mp2->pdev->dev;
> + struct input_dev *input;
> + int rc;
> +
> + input = devm_input_allocate_device(dev);
> + if (IS_ERR(input))
> + return PTR_ERR(input);
> +
> + input->name = "AMD SFH tablet mode switch sensor";
> + input->id.bustype = BUS_PCI;
> +
> + input_set_capability(input, EV_SW, SW_TABLET_MODE);
> +
> + rc = input_register_device(input);
> + if (rc)
> + goto cleanup;
> +
> + cli_data->modeswitch_input = input;
> +
> + return 0;
> +
> +cleanup:
> + return rc;
> +}
> +
> +void amdtp_modeswitch_report(u32 index, struct input_dev *input, struct amd_input_data *in_data)
> +{
> + u32 *sensor_virt_addr = in_data->sensor_virt_addr[index];
> + u32 value = sensor_virt_addr[0];
> +
> + if (value == AMD_SFH_OP_IDX_MODE_TABLET)
> + input_report_switch(input, SW_TABLET_MODE, 1);
> + else
> + input_report_switch(input, SW_TABLET_MODE, 0);
> +
> + input_sync(input);
> }
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
> index 7452b0302953..20aff7b75fbd 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
> @@ -11,6 +11,8 @@
> #ifndef AMDSFH_HID_H
> #define AMDSFH_HID_H
>
> +#include <linux/input.h>
> +
> #define MAX_HID_DEVICES 7
> #define AMD_SFH_HID_VENDOR 0x1022
> #define AMD_SFH_HID_PRODUCT 0x0001
> @@ -50,6 +52,7 @@ struct amdtp_cl_data {
> u8 sensor_idx[MAX_HID_DEVICES];
> u8 *feature_report[MAX_HID_DEVICES];
> u8 request_done[MAX_HID_DEVICES];
> + struct input_dev *modeswitch_input;
> struct amd_input_data *in_data;
> struct delayed_work work;
> struct delayed_work work_buffer;
> @@ -78,4 +81,7 @@ void amdtp_hid_remove(struct amdtp_cl_data *cli_data);
> int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type);
> void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type);
> void amdtp_hid_wakeup(struct hid_device *hid);
> +int amdtp_modeswitch_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data);
> +void amdtp_modeswitch_report(u32 index, struct input_dev *input, struct amd_input_data *in_data);
> +
> #endif
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
> index 1d9f955573aa..cd9cff75f114 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
> @@ -39,6 +39,8 @@ module_param_named(sensor_mask, sensor_mask_override, int, 0444);
> MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
>
> static bool intr_disable = true;
> +module_param_named(intr_disable, intr_disable, bool, 0444);
> +MODULE_PARM_DESC(intr_disable, "override the interrupt disable sensor bit");
>
> static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
> {
> @@ -317,6 +319,13 @@ static const struct dmi_system_id dmi_sfh_table[] = {
> DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"),
> },
> },
> + {
> + .callback = mp2_disable_intr,
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
> + DMI_MATCH(DMI_PRODUCT_NAME, "VivoBook_ASUSLaptop TP420UA_TM420UA"),
> + }
> + },
> {}
> };
>
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
> index 2eb61f4e8434..5e968894ebe4 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
> @@ -83,6 +83,9 @@ enum sensor_idx {
> als_idx = 19
> };
>
> +#define AMD_SFH_OP_IDX_MODE_LAPTOP 1
> +#define AMD_SFH_OP_IDX_MODE_TABLET 3
> +
> enum mem_use_type {
> USE_DRAM,
> USE_C2P_REG,
> --
> 2.47.3
>
>
>
>
--
Jiri Kosina
SUSE Labs
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
2026-05-14 7:59 ` Helge Bahmann
@ 2026-06-10 17:12 ` Basavaraj Natikar
2026-06-12 4:22 ` Helge Bahmann
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: Basavaraj Natikar @ 2026-06-10 17:12 UTC (permalink / raw)
To: Helge Bahmann, Jiri Kosina, linux-input; +Cc: Basavaraj Natikar, bentiss
On 5/14/2026 1:29 PM, Helge Bahmann wrote:
> [You don't often get email from hcb@chaoticmind.net. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> On Tue, 12 May 2026, Jiri Kosina wrote:
>> On Mon, 27 Apr 2026, Helge Bahmann wrote:
>>
>>> Add an input driver that interprets the "operation mode" sensor offered
>>> by the amd sfh on some laptop models.
>>>
>>> Add a quirk to make the driver work again with the Asus VivoBook
>>> VivoBook (turn off the "disable interrupts" flag).
>>>
>>> Expose the intr_disable flag as a module parameter in case it turns out
>>> to be needed on further laptop models.
>>>
>>> Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
>> Basavaraj, can you please review this one?
> Some additional context, maybe helpful for review:
>
> 1. The numbers and behavior were extracted from the ACPI tables
> (WMI driver of sorts) of the notebook; I don't have access to any
> official AMD / ASUS docs or similar.
>
> 2. I have an alternate version of this change that is more indirect:
> - create a HID driver providing an "abstract table mode" message
> - have an input driver attaching to this newly defined HID driver
>
> While that is keeping "more in line" with the current driver
> architecture, I am not sure this indirection really helps. Particularly,
> there is no "canonical" HID tablet mode switch message defined,
> so it all remains completely bespoke. I am happy to change it if
> you prefer, but would need your input.
>
> 3. Since this is based on Asus VivoBook and its ACPI tables,
> there is a possibility that this "op sensor / tablet mode" behavior
> is not as universal as I surmise. A point could be made to make this
> entire behavior model-dependent (with a mod param to override
> / activate for other models). Happy to take input / advice.
Thanks Helge.
I'd like to go with the dedicated input-driver approach (your option with
a standalone input driver) rather than the HID-message indirection -- it
keeps clean subsystem boundaries.
For splitting the work, either of these works for me -- whichever you
prefer:
Option 1: I create the new input driver
drivers/input/misc/amd_sfh_tabletmode.c and once done we will review your ASUS VivoBook
quirk + intr_disable module-param patches on top of it.
Option 2: If you'd rather keep ownership of it, you write
drivers/input/misc/amd_sfh_tabletmode.c consuming the mode exposed by
amd-sfh, and I'll review and help.
Let me know which option you'd like and I'll proceed.
Thanks,
--
Basavaraj
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] amd-sfh-hid: tablet mode switch and asus quirk
2026-06-10 17:12 ` Basavaraj Natikar
@ 2026-06-12 4:22 ` Helge Bahmann
2026-07-21 14:28 ` [PATCH 0/2] asus vivobook tablet mode Helge Bahmann
` (2 subsequent siblings)
3 siblings, 0 replies; 13+ messages in thread
From: Helge Bahmann @ 2026-06-12 4:22 UTC (permalink / raw)
To: Jiri Kosina, linux-input, Basavaraj Natikar; +Cc: Basavaraj Natikar, bentiss
Am Mittwoch, 10. Juni 2026, 19:12:37 Mitteleuropäische Sommerzeit schrieb Basavaraj Natikar:
>
> On 5/14/2026 1:29 PM, Helge Bahmann wrote:
>
> > [You don't often get email from hcb@chaoticmind.net. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> >
> > On Tue, 12 May 2026, Jiri Kosina wrote:
> >> On Mon, 27 Apr 2026, Helge Bahmann wrote:
> >>
> >>> Add an input driver that interprets the "operation mode" sensor offered
> >>> by the amd sfh on some laptop models.
> >>>
> >>> Add a quirk to make the driver work again with the Asus VivoBook
> >>> VivoBook (turn off the "disable interrupts" flag).
> >>>
> >>> Expose the intr_disable flag as a module parameter in case it turns out
> >>> to be needed on further laptop models.
> >>>
> >>> Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
> >> Basavaraj, can you please review this one?
> > Some additional context, maybe helpful for review:
> >
> > 1. The numbers and behavior were extracted from the ACPI tables
> > (WMI driver of sorts) of the notebook; I don't have access to any
> > official AMD / ASUS docs or similar.
> >
> > 2. I have an alternate version of this change that is more indirect:
> > - create a HID driver providing an "abstract table mode" message
> > - have an input driver attaching to this newly defined HID driver
> >
> > While that is keeping "more in line" with the current driver
> > architecture, I am not sure this indirection really helps. Particularly,
> > there is no "canonical" HID tablet mode switch message defined,
> > so it all remains completely bespoke. I am happy to change it if
> > you prefer, but would need your input.
> >
> > 3. Since this is based on Asus VivoBook and its ACPI tables,
> > there is a possibility that this "op sensor / tablet mode" behavior
> > is not as universal as I surmise. A point could be made to make this
> > entire behavior model-dependent (with a mod param to override
> > / activate for other models). Happy to take input / advice.
>
> Thanks Helge.
>
> I'd like to go with the dedicated input-driver approach (your option with
> a standalone input driver) rather than the HID-message indirection -- it
> keeps clean subsystem boundaries.
Hello Basavaraj!
Thanks for your architecture input, that sounds good. Let me briefly
clean up the patches and I will send you what I have. I will also
include the input-tabletmode driver that I have, but you will likely
want to change it (use correct protocol message as I am unsure
what would be "correct" here). You can then proceed as you like
(take the driver, rewrite it, ...) -- I will leave ownership decision
entirely up to you, I care more that you are happy with the
overall structure than that.
Thank you
Helge
>
> For splitting the work, either of these works for me -- whichever you
> prefer:
> Option 1: I create the new input driver
> drivers/input/misc/amd_sfh_tabletmode.c and once done we will review your ASUS VivoBook
> quirk + intr_disable module-param patches on top of it.
>
> Option 2: If you'd rather keep ownership of it, you write
> drivers/input/misc/amd_sfh_tabletmode.c consuming the mode exposed by
> amd-sfh, and I'll review and help.
>
>
> Let me know which option you'd like and I'll proceed.
>
> Thanks,
> --
> Basavaraj
>
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 0/2] asus vivobook tablet mode
2026-06-10 17:12 ` Basavaraj Natikar
2026-06-12 4:22 ` Helge Bahmann
@ 2026-07-21 14:28 ` Helge Bahmann
2026-07-21 17:52 ` Basavaraj Natikar
2026-07-21 14:29 ` [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk Helge Bahmann
2026-07-21 14:29 ` [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report Helge Bahmann
3 siblings, 1 reply; 13+ messages in thread
From: Helge Bahmann @ 2026-07-21 14:28 UTC (permalink / raw)
To: Jiri Kosina, linux-input, Basavaraj Natikar; +Cc: Basavaraj Natikar
Hi Basavaraj,
Here the two patches implementing the split hid/input
driver for the tablet mode support. I mulled over what
exactly to use as report descriptor in the hid driver, and
there is no clean and easy solution -- I hope this is what
you had in mind? I could not figure a way how to do
a stand-alone input driver without doing any hid indirection,
as I simply could not find a way to bind to the physical
dev twice.
Please let me know in comments to the patches how you
would like to proceed.
Thanks!
Helge
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk
2026-06-10 17:12 ` Basavaraj Natikar
2026-06-12 4:22 ` Helge Bahmann
2026-07-21 14:28 ` [PATCH 0/2] asus vivobook tablet mode Helge Bahmann
@ 2026-07-21 14:29 ` Helge Bahmann
2026-07-21 14:47 ` sashiko-bot
2026-07-21 14:29 ` [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report Helge Bahmann
3 siblings, 1 reply; 13+ messages in thread
From: Helge Bahmann @ 2026-07-21 14:29 UTC (permalink / raw)
To: Jiri Kosina, linux-input, Basavaraj Natikar; +Cc: Basavaraj Natikar
Add an input driver that interprets the "operation mode" sensor offered
by the amd sfh as a hid driver to generate a custom hid report (tablet
mode switch).
Add a quirk to restore compatibility of the driver with the Asus
VivoBook (turn off the "disable interrupts flag). Expose the
intr_disable flag as a module parameter in case it turns out to be
needed on further laptop models.
Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
---
drivers/hid/amd-sfh-hid/amd_sfh_client.c | 19 ------------
drivers/hid/amd-sfh-hid/amd_sfh_hid.h | 1 +
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 9 ++++++
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 3 ++
.../hid_descriptor/amd_sfh_hid_desc.c | 31 +++++++++++++++++++
.../hid_descriptor/amd_sfh_hid_desc.h | 9 ++++++
.../hid_descriptor/amd_sfh_hid_report_desc.h | 18 +++++++++++
7 files changed, 71 insertions(+), 19 deletions(-)
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
index 7017bfa59093..57ff708ca39b 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
@@ -254,19 +254,6 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
goto cleanup;
}
- if (cl_data->sensor_idx[i] == op_idx) {
- info.period = AMD_SFH_IDLE_LOOP;
- info.sensor_idx = cl_data->sensor_idx[i];
- info.dma_address = cl_data->sensor_dma_addr[i];
- mp2_ops->start(privdata, info);
- cl_data->sensor_sts[i] = amd_sfh_wait_for_response(privdata,
- cl_data->sensor_idx[i],
- SENSOR_ENABLED);
- if (cl_data->sensor_sts[i] == SENSOR_ENABLED)
- cl_data->is_any_sensor_enabled = true;
- continue;
- }
-
cl_data->sensor_sts[i] = SENSOR_DISABLED;
cl_data->sensor_requested_cnt[i] = 0;
cl_data->cur_hid_dev = i;
@@ -327,12 +314,6 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
for (i = 0; i < cl_data->num_hid_devices; i++) {
cl_data->cur_hid_dev = i;
- if (cl_data->sensor_idx[i] == op_idx) {
- dev_dbg(dev, "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]);
- continue;
- }
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
rc = amdtp_hid_probe(i, cl_data);
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
index 7452b0302953..c1b62e5df2ff 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
@@ -78,4 +78,5 @@ void amdtp_hid_remove(struct amdtp_cl_data *cli_data);
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type);
void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type);
void amdtp_hid_wakeup(struct hid_device *hid);
+
#endif
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
index 1d9f955573aa..cd9cff75f114 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
@@ -39,6 +39,8 @@ module_param_named(sensor_mask, sensor_mask_override, int, 0444);
MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
static bool intr_disable = true;
+module_param_named(intr_disable, intr_disable, bool, 0444);
+MODULE_PARM_DESC(intr_disable, "override the interrupt disable sensor bit");
static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
@@ -317,6 +319,13 @@ static const struct dmi_system_id dmi_sfh_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"),
},
},
+ {
+ .callback = mp2_disable_intr,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VivoBook_ASUSLaptop TP420UA_TM420UA"),
+ }
+ },
{}
};
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
index 2eb61f4e8434..5e968894ebe4 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
@@ -83,6 +83,9 @@ enum sensor_idx {
als_idx = 19
};
+#define AMD_SFH_OP_IDX_MODE_LAPTOP 1
+#define AMD_SFH_OP_IDX_MODE_TABLET 3
+
enum mem_use_type {
USE_DRAM,
USE_C2P_REG,
diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
index ef1f9be8b893..7e807b92d296 100644
--- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
+++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
@@ -42,6 +42,11 @@ static int get_report_descriptor(int sensor_idx, u8 *rep_desc)
memcpy(rep_desc, gyro3_report_descriptor,
sizeof(gyro3_report_descriptor));
break;
+ case op_idx: /* op */
+ memset(rep_desc, 0, sizeof(tablet_mode_report_descriptor));
+ memcpy(rep_desc, tablet_mode_report_descriptor,
+ sizeof(tablet_mode_report_descriptor));
+ break;
case mag_idx: /* Magnetometer */
memset(rep_desc, 0, sizeof(comp3_report_descriptor));
memcpy(rep_desc, comp3_report_descriptor,
@@ -87,6 +92,16 @@ static u32 get_descr_sz(int sensor_idx, int descriptor_name)
return sizeof(struct gyro_feature_report);
}
break;
+ case op_idx:
+ switch (descriptor_name) {
+ case descr_size:
+ return sizeof(tablet_mode_report_descriptor);
+ case input_size:
+ return sizeof(struct tablet_mode_input_report);
+ case feature_size:
+ return sizeof(struct tablet_mode_feature_report);
+ }
+ break;
case mag_idx:
switch (descriptor_name) {
case descr_size:
@@ -139,6 +154,7 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report)
{
struct accel3_feature_report acc_feature;
struct gyro_feature_report gyro_feature;
+ struct tablet_mode_feature_report tablet_mode_feature;
struct magno_feature_report magno_feature;
struct hpd_feature_report hpd_feature;
struct als_feature_report als_feature;
@@ -164,6 +180,11 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report)
memcpy(feature_report, &gyro_feature, sizeof(gyro_feature));
report_size = sizeof(gyro_feature);
break;
+ case op_idx: /* op */
+ get_common_features(&tablet_mode_feature.common_property, report_id);
+ memcpy(feature_report, &tablet_mode_feature, sizeof(tablet_mode_feature));
+ report_size = sizeof(tablet_mode_feature);
+ break;
case mag_idx: /* Magnetometer */
get_common_features(&magno_feature.common_property, report_id);
magno_feature.magno_headingchange_sensitivity = HID_DEFAULT_SENSITIVITY;
@@ -212,6 +233,7 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id,
u8 supported_input = privdata->mp2_acs & GENMASK(3, 0);
struct magno_input_report magno_input;
struct accel3_input_report acc_input;
+ struct tablet_mode_input_report tablet_mode_input;
struct gyro_input_report gyro_input;
struct hpd_input_report hpd_input;
struct als_input_report als_input;
@@ -238,6 +260,15 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id,
memcpy(input_report, &gyro_input, sizeof(gyro_input));
report_size = sizeof(gyro_input);
break;
+ case op_idx: /* op */
+ tablet_mode_input.report_id = 0x11;
+ if (sensor_virt_addr[0] == 3)
+ tablet_mode_input.tablet_state = 1;
+ else
+ tablet_mode_input.tablet_state = 0;
+ memcpy(input_report, &tablet_mode_input, sizeof(tablet_mode_input));
+ report_size = sizeof(tablet_mode_input);
+ break;
case mag_idx: /* Magnetometer */
get_common_inputs(&magno_input.common_property, report_id);
magno_input.in_magno_x = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
index 882434b1501f..ff82e47eb3ca 100644
--- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
+++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
@@ -114,4 +114,13 @@ struct hpd_input_report {
u8 human_presence;
} __packed;
+struct tablet_mode_feature_report {
+ struct common_feature_property common_property;
+} __packed;
+
+struct tablet_mode_input_report {
+ u8 report_id; /* Muss 0x11 (17) sein */
+ u8 tablet_state; /* Bit 0: Status, Bits 1-7: Padding */
+} __packed;
+
#endif
diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
index 67ec2d6a417d..f95368534fc6 100644
--- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
+++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
@@ -776,4 +776,22 @@ static const u8 hpd_report_descriptor[] = {
0X81, 0x02, /* HID Input (Data_Var_Abs) */
0xC0 /* HID end collection */
};
+
+/* tablet mode switch */
+static const u8 tablet_mode_report_descriptor[] = {
+0x05, 0x0C, /* HID usage consumer electronics */
+0x09, 0x01, /* HID usage */
+0xA1, 0x00, /* HID collection (Physical) */
+0x85, 0x11, /* HID report id */
+0x0A, 0xFF, 0x02, /* HID usage (unallocated -- (ab)used for tablet mode) */
+0x15, 0x00, /* HID logical Min_8(0) */
+0x25, 0x01, /* HID logical Max_8(1) */
+0x75, 0x01, /* HID report size(1) */
+0x95, 0x01, /* HID report count (1) */
+0X81, 0x02, /* HID input (Data_Var_Abs) */
+0x75, 0x01, /* HID report size (1) */
+0x95, 0x07, /* HID report count (7) */
+0x81, 0x03, /* HID input (Data_Var_Abs) */
+0xC0 /* HID end collection */
+};
#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report
2026-06-10 17:12 ` Basavaraj Natikar
` (2 preceding siblings ...)
2026-07-21 14:29 ` [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk Helge Bahmann
@ 2026-07-21 14:29 ` Helge Bahmann
2026-07-21 14:41 ` sashiko-bot
3 siblings, 1 reply; 13+ messages in thread
From: Helge Bahmann @ 2026-07-21 14:29 UTC (permalink / raw)
To: Jiri Kosina, linux-input, Basavaraj Natikar; +Cc: Basavaraj Natikar
Add a driver that hooks into the tablet mode hid reports
generated by the amd-sfh-hid driver. This enables tablet
mode switching on e.g. Asus Vivobook devices.
Signed-off-by: Helge Bahmann <hcb@chaoticmind.net>
---
drivers/input/misc/Kconfig | 11 ++++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/amd_sfh_tabletmode.c | 88 +++++++++++++++++++++++++
3 files changed, 100 insertions(+)
create mode 100644 drivers/input/misc/amd_sfh_tabletmode.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 94a753fcb64f..5a1dacd6f7b9 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -1003,4 +1003,15 @@ config INPUT_STPMIC1_ONKEY
To compile this driver as a module, choose M here: the
module will be called stpmic1_onkey.
+config INPUT_AMD_SFH_TABLETMODE
+ tristate "AMD sensor fusion hub tablet mode driver"
+ help
+ Say Y to enable support of the tablet mode switch driver for
+ convertible laptops based on the AMD sensor fusion hub. This
+ driver is known to work on Asus VivoBook but will likely
+ work on other laptops featuring the same chipset.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amd_sfh_tabletmode.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415fc4e2918b..3f5abbe39f3a 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -96,3 +96,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
+obj-$(CONFIG_INPUT_AMD_SFH_TABLETMODE) += amd_sfh_tabletmode.o
diff --git a/drivers/input/misc/amd_sfh_tabletmode.c b/drivers/input/misc/amd_sfh_tabletmode.c
new file mode 100644
index 000000000000..ee1207372207
--- /dev/null
+++ b/drivers/input/misc/amd_sfh_tabletmode.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD MP2 PCIe tablet mode hid input driver
+ * Copyright 2026 Helge Bahmann Advanced Micro Devices, Inc.
+ * Authors: Helge Bahmann <hcb@chaoticmind.net>
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#define AMD_SFH_HID_VENDOR 0x1022
+#define AMD_SFH_HID_PRODUCT 0x0001
+
+static int amd_sfh_tabletmode_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int error = 0;
+
+ error = hid_parse(hdev);
+ if (error)
+ return error;
+
+ error = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void amd_sfh_tabletmode_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+}
+
+static int amd_sfh_tabletmode_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER && (usage->hid & HID_USAGE) == 0x02ff) {
+ input_set_capability(hi->input, EV_SW, SW_TABLET_MODE);
+ usage->type = EV_SW;
+ usage->code = SW_TABLET_MODE;
+ return 1;
+ }
+ return 0;
+}
+
+static int amd_sfh_tabletmode_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ input_report_switch(field->hidinput->input, SW_TABLET_MODE, value);
+
+ return 0;
+}
+
+static const struct hid_device_id amd_sfh_tabletmode_devices[] = {
+ { HID_DEVICE(BUS_AMD_SFH, HID_GROUP_GENERIC, AMD_SFH_HID_VENDOR, AMD_SFH_HID_PRODUCT) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, amd_sfh_tabletmode_devices);
+
+static struct hid_driver amd_sfh_tabletmode_driver = {
+ .name = "amd_sfh_tabletmode",
+ .id_table = amd_sfh_tabletmode_devices,
+ .probe = amd_sfh_tabletmode_probe,
+ .remove = amd_sfh_tabletmode_remove,
+ .input_mapping = amd_sfh_tabletmode_input_mapping,
+ .event = amd_sfh_tabletmode_event,
+};
+
+static int __init amd_sfh_tabletmode_init(void)
+{
+ int error;
+
+ error = hid_register_driver(&amd_sfh_tabletmode_driver);
+
+ return error;
+}
+module_init(amd_sfh_tabletmode_init);
+
+static void __exit amd_sfh_tabletmode_exit(void)
+{
+ hid_unregister_driver(&amd_sfh_tabletmode_driver);
+}
+module_exit(amd_sfh_tabletmode_exit);
+
+MODULE_DESCRIPTION("Input driver for tablet mode sensor on amd sfh.");
+MODULE_LICENSE("GPL");
--
2.47.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report
2026-07-21 14:29 ` [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report Helge Bahmann
@ 2026-07-21 14:41 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-21 14:41 UTC (permalink / raw)
To: Helge Bahmann; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Unconditional `field->hidinput` dereference and tablet mode state corruption due to unconstrained `.event` callback.
- [High] The Kconfig symbol `INPUT_AMD_SFH_TABLETMODE` is missing a dependency on `HID`.
--
--- Patch [2]: [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report ---
commit 981d61514e0f554ad31b2f14f7fb391c059df340
Author: Helge Bahmann <hcb@chaoticmind.net>
amd-sfh-tabletmode: interpret sfh tablet mode hid report
Add a driver that hooks into the tablet mode hid reports
generated by the amd-sfh-hid driver. This enables tablet
mode switching on e.g. Asus Vivobook devices.
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 94a753fcb64fa..5a1dacd6f7b9c 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -1003,4 +1003,15 @@ config INPUT_STPMIC1_ONKEY
> To compile this driver as a module, choose M here: the
> module will be called stpmic1_onkey.
>
> +config INPUT_AMD_SFH_TABLETMODE
> + tristate "AMD sensor fusion hub tablet mode driver"
[Severity: High]
Is it possible this Kconfig entry is missing a depends on HID?
If the HID subsystem is disabled or configured as a module while this driver
is built-in, could this result in linking errors for HID core functions like
hid_register_driver() and hid_parse()?
> + help
> + Say Y to enable support of the tablet mode switch driver for
[ ... ]
> diff --git a/drivers/input/misc/amd_sfh_tabletmode.c b/drivers/input/misc/amd_sfh_tabletmode.c
> new file mode 100644
> index 0000000000000..ee1207372207c
> --- /dev/null
> +++ b/drivers/input/misc/amd_sfh_tabletmode.c
[ ... ]
> +static int amd_sfh_tabletmode_event(struct hid_device *hid, struct hid_field *field,
> + struct hid_usage *usage, __s32 value)
> +{
> + input_report_switch(field->hidinput->input, SW_TABLET_MODE, value);
[Severity: High]
Does this unconditionally dereference field->hidinput, which could be NULL
for unmapped usages?
Because the driver struct below lacks a .usage_table, hid_match_usage()
will default to true for all usages, meaning this callback intercepts every
incoming HID report field. This could lead to a NULL pointer dereference,
and will also blindly emit a SW_TABLET_MODE event for any report, causing
standard buttons or battery reports to randomly toggle tablet mode.
Since .input_mapping already maps the usage to EV_SW, allowing the generic
HID core to automatically report the event, could this callback be removed
entirely?
> +
> + return 0;
> +}
> +
> +static const struct hid_device_id amd_sfh_tabletmode_devices[] = {
> + { HID_DEVICE(BUS_AMD_SFH, HID_GROUP_GENERIC, AMD_SFH_HID_VENDOR, AMD_SFH_HID_PRODUCT) },
> + { }
> +};
> +MODULE_DEVICE_TABLE(hid, amd_sfh_tabletmode_devices);
> +
> +static struct hid_driver amd_sfh_tabletmode_driver = {
> + .name = "amd_sfh_tabletmode",
> + .id_table = amd_sfh_tabletmode_devices,
> + .probe = amd_sfh_tabletmode_probe,
> + .remove = amd_sfh_tabletmode_remove,
> + .input_mapping = amd_sfh_tabletmode_input_mapping,
> + .event = amd_sfh_tabletmode_event,
[Severity: High]
Should this struct include a .usage_table?
Without it, all device usages are routed to the .event callback above.
> +};
--
Sashiko AI review · https://sashiko.dev/#/patchset/ecadaac8-e222-420a-ac5b-4d529fb3317e@amd.com?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk
2026-07-21 14:29 ` [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk Helge Bahmann
@ 2026-07-21 14:47 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-21 14:47 UTC (permalink / raw)
To: Helge Bahmann; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Medium] Missing HID Input Mapping for tablet mode switch
- [Low] Incomplete Implementation / Dead Code for Feature Report
- [Low] Unintentional Parameter Override via DMI Quirk
- [Low] Unused Macros for Tablet State Evaluated by Magic Number
- [Low] Introduction of Non-English Comments
Pre-existing issues:
- [High] Double Free / Use-After-Free in amdtp_hid_probe error path
--
--- Patch [1]: [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk ---
commit 8025fc07a2e48355e8daeee6c47f30c940eb5a30
Author: Helge Bahmann <hcb@chaoticmind.net>
amd-sfh-hid: tablet mode hid report and asus quirk
Add an input driver that interprets the "operation mode" sensor offered
by the amd sfh as a hid driver to generate a custom hid report (tablet
mode switch).
Add a quirk to restore compatibility of the driver with the Asus
VivoBook (turn off the "disable interrupts flag). Expose the
intr_disable flag as a module parameter in case it turns out to be
needed on further laptop models.
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
> index 96ae792beeb6c..8c03279646023 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
[ ... ]
> @@ -327,12 +314,6 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
>
> for (i = 0; i < cl_data->num_hid_devices; i++) {
> cl_data->cur_hid_dev = i;
> - if (cl_data->sensor_idx[i] == op_idx) {
> - dev_dbg(dev, "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]);
> - continue;
> - }
>
> if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
> rc = amdtp_hid_probe(i, cl_data);
[Severity: High]
This isn't a bug introduced by this patch, but is a pre-existing issue in the
driver's error-handling path.
If amdtp_hid_probe() fails (e.g., in hid_add_device()), it destroys the hid
device but does not clear the dangling pointer in
cli_data->hid_sensor_hubs[cur_hid_dev].
When this failure propagates up to amd_sfh_hid_client_init(), it jumps to the
cleanup label and invokes amd_sfh_hid_client_deinit() -> amdtp_hid_remove().
amdtp_hid_remove() will then iterate over the hid_sensor_hubs array and call
hid_destroy_device() again on the dangling pointer, which can cause a double
free.
Should the pointer be set to NULL in amdtp_hid_probe() on error?
> diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
> index 4b81cebdc3359..924329bfef8fe 100644
> --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
> +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
> @@ -39,6 +39,8 @@ module_param_named(sensor_mask, sensor_mask_override, int, 0444);
> MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
>
> static bool intr_disable = true;
> +module_param_named(intr_disable, intr_disable, bool, 0444);
> +MODULE_PARM_DESC(intr_disable, "override the interrupt disable sensor bit");
[Severity: Low]
Because intr_disable is implemented as a simple boolean, the DMI match quirk
for Asus VivoBook unconditionally invokes mp2_disable_intr(), which
overwrites intr_disable to false.
Does this prevent the driver from differentiating between the default state
and an explicit user override on the command line (e.g.,
amd_sfh.intr_disable=1)?
> diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
> index ef1f9be8b8938..7e807b92d2960 100644
> --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
> +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
[ ... ]
> @@ -164,6 +180,11 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report)
> memcpy(feature_report, &gyro_feature, sizeof(gyro_feature));
> report_size = sizeof(gyro_feature);
> break;
> + case op_idx: /* op */
> + get_common_features(&tablet_mode_feature.common_property, report_id);
> + memcpy(feature_report, &tablet_mode_feature, sizeof(tablet_mode_feature));
> + report_size = sizeof(tablet_mode_feature);
> + break;
[Severity: Low]
The driver allocates and populates a Feature report
(tablet_mode_feature_report) here, but the corresponding
tablet_mode_report_descriptor lacks any Feature items (0xB1).
Since the HID core strictly relies on the parsed descriptor to create reports,
will a feature report with ID 0x11 ever be requested, or is this unused
dead code?
> @@ -238,6 +260,15 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id,
> memcpy(input_report, &gyro_input, sizeof(gyro_input));
> report_size = sizeof(gyro_input);
> break;
> + case op_idx: /* op */
> + tablet_mode_input.report_id = 0x11;
> + if (sensor_virt_addr[0] == 3)
> + tablet_mode_input.tablet_state = 1;
> + else
> + tablet_mode_input.tablet_state = 0;
[Severity: Low]
This code relies on a hardcoded magic number 3. Should this evaluate the
sensor payload using the newly defined AMD_SFH_OP_IDX_MODE_TABLET macro
instead?
> diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
> index 882434b1501ff..ff82e47eb3ca4 100644
> --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
> +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
> @@ -114,4 +114,13 @@ struct hpd_input_report {
> u8 human_presence;
> } __packed;
>
> +struct tablet_mode_feature_report {
> + struct common_feature_property common_property;
> +} __packed;
> +
> +struct tablet_mode_input_report {
> + u8 report_id; /* Muss 0x11 (17) sein */
[Severity: Low]
Is this German comment intentional? Kernel coding style typically requires all
comments to be written in English.
> diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
> index 67ec2d6a417de..f95368534fc60 100644
> --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
> +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
> @@ -776,4 +776,22 @@ static const u8 hpd_report_descriptor[] = {
> 0X81, 0x02, /* HID Input (Data_Var_Abs) */
> 0xC0 /* HID end collection */
> };
> +
> +/* tablet mode switch */
> +static const u8 tablet_mode_report_descriptor[] = {
> +0x05, 0x0C, /* HID usage consumer electronics */
> +0x09, 0x01, /* HID usage */
> +0xA1, 0x00, /* HID collection (Physical) */
> +0x85, 0x11, /* HID report id */
> +0x0A, 0xFF, 0x02, /* HID usage (unallocated -- (ab)used for tablet mode) */
[Severity: Medium]
Because the driver does not provide a custom .input_mapping callback to
intercept and map this unallocated Consumer usage to SW_TABLET_MODE, will the
standard hid-input core ignore it or map it to KEY_UNKNOWN (240)?
Could this result in physical changes in tablet mode emitting spurious unknown
key events instead of a standard EV_SW switch event?
--
Sashiko AI review · https://sashiko.dev/#/patchset/ecadaac8-e222-420a-ac5b-4d529fb3317e@amd.com?part=1
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 0/2] asus vivobook tablet mode
2026-07-21 14:28 ` [PATCH 0/2] asus vivobook tablet mode Helge Bahmann
@ 2026-07-21 17:52 ` Basavaraj Natikar
0 siblings, 0 replies; 13+ messages in thread
From: Basavaraj Natikar @ 2026-07-21 17:52 UTC (permalink / raw)
To: Helge Bahmann, Jiri Kosina, linux-input; +Cc: Basavaraj Natikar
On 7/21/2026 7:58 PM, Helge Bahmann wrote:
> Hi Basavaraj,
>
> Here the two patches implementing the split hid/input
> gdriver for the tablet mode support. I mulled over what
> exactly to use as report descriptor in the hid driver, and
> there is no clean and easy solution -- I hope this is what
> you had in mind? I could not figure a way how to do
> a stand-alone input driver without doing any hid indirection,
Hi Helge,
Please find below the series adding SW_TABLET_MODE support via the AMD
Sensor Fusion Hub, covering all currently supported AMD convertibles
https://lore.kernel.org/all/20260721174422.3109166-1-Basavaraj.Natikar@amd.com/
Once this is merged, the ASUS VivoBook quirk can go on top of it.
Thanks,
--
Basavaraj
> as I simply could not find a way to bind to the physical
> dev twice.
>
> Please let me know in comments to the patches how you
> would like to proceed.
>
> Thanks!
> Helge
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-07-21 17:52 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-27 6:22 [PATCH] amd-sfh-hid: tablet mode switch and asus quirk Helge Bahmann
2026-05-12 16:06 ` Jiri Kosina
2026-05-12 17:09 ` Basavaraj Natikar
2026-05-14 7:59 ` Helge Bahmann
2026-06-10 17:12 ` Basavaraj Natikar
2026-06-12 4:22 ` Helge Bahmann
2026-07-21 14:28 ` [PATCH 0/2] asus vivobook tablet mode Helge Bahmann
2026-07-21 17:52 ` Basavaraj Natikar
2026-07-21 14:29 ` [PATCH 1/2] amd-sfh-hid: tablet mode hid report and asus quirk Helge Bahmann
2026-07-21 14:47 ` sashiko-bot
2026-07-21 14:29 ` [PATCH 2/2] amd-sfh-tabletmode: interpret sfh tablet mode hid report Helge Bahmann
2026-07-21 14:41 ` sashiko-bot
2026-06-10 16:33 ` [PATCH] amd-sfh-hid: tablet mode switch and asus quirk Jiri Kosina
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox