[PATCH v3 3/5] HID: amd_sfh: Add accessor to read the operating-mode sensor

Basavaraj Natikar <[email protected]> Mon, 3 Aug 2026 23:22:28 +0530
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
Allow other drivers to query the operating mode (laptop or tablet)
reported by the Sensor Fusion Hub. This is the interface used by the
tablet-mode switch driver to report the device posture to userspace.

Signed-off-by: Basavaraj Natikar <[email protected]>
---
 drivers/hid/amd-sfh-hid/amd_sfh_client.c      | 16 +++++++++
 drivers/hid/amd-sfh-hid/amd_sfh_common.h      |  5 +++
 drivers/hid/amd-sfh-hid/amd_sfh_pcie.c        |  4 +++
 .../amd-sfh-hid/sfh1_1/amd_sfh_interface.c    | 33 +++++++++++++++++++
 .../amd-sfh-hid/sfh1_1/amd_sfh_interface.h    |  1 -
 include/linux/amd-pmf-io.h                    | 14 ++++++++
 6 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
index 96ae792beeb6..ae6add0b9ce3 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
@@ -383,3 +383,19 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
 
 	return 0;
 }
+
+bool amd_sfh_op_idx_enabled(struct amd_mp2_dev *mp2)
+{
+	struct amdtp_cl_data *cl = mp2->cl_data;
+	int i;
+
+	if (!cl)
+		return false;
+
+	for (i = 0; i < cl->num_hid_devices; i++)
+		if (cl->sensor_idx[i] == op_idx &&
+		    READ_ONCE(cl->sensor_sts[i]) == SENSOR_ENABLED)
+			return true;
+
+	return false;
+}
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_common.h b/drivers/hid/amd-sfh-hid/amd_sfh_common.h
index 68586f08ab23..0ca3254151ac 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_common.h
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_common.h
@@ -106,4 +106,9 @@ static inline u64 amd_get_p2c_val(struct amd_mp2_dev *mp2, u32 idx)
 {
 	return mp2->rver == 1 ? AMD_P2C_MSG_V1(idx) :  AMD_P2C_MSG(idx);
 }
+
+bool amd_sfh_op_idx_enabled(struct amd_mp2_dev *mp2);
+void sfh_set_emp2(struct amd_mp2_dev *mp2);
+void sfh_deinit_emp2(void);
+
 #endif
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
index 92801ca38957..4b1cd260410d 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
@@ -251,6 +251,8 @@ int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
 static void amd_mp2_pci_remove(void *privdata)
 {
 	struct amd_mp2_dev *mp2 = privdata;
+
+	sfh_deinit_emp2();
 	amd_sfh_hid_client_deinit(privdata);
 	mp2->mp2_ops->stop_all(mp2);
 	pcim_intx(mp2->pdev, false);
@@ -419,6 +421,7 @@ static void sfh_init_work(struct work_struct *work)
 		return;
 	}
 
+	sfh_set_emp2(mp2);
 	amd_sfh_clear_intr(mp2);
 	mp2->init_done = 1;
 }
@@ -448,6 +451,7 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
 
 	privdata->pdev = pdev;
 	dev_set_drvdata(&pdev->dev, privdata);
+
 	rc = pcim_enable_device(pdev);
 	if (rc)
 		return rc;
diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c
index dd2720bae65c..097c5513ccd8 100644
--- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c
+++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c
@@ -84,6 +84,12 @@ void sfh_deinit_emp2(void)
 	emp2 = NULL;
 }
 
+void sfh_set_emp2(struct amd_mp2_dev *mp2)
+{
+	guard(mutex)(&emp2_lock);
+	emp2 = mp2;
+}
+
 void sfh_interface_init(struct amd_mp2_dev *mp2)
 {
 	mp2->mp2_ops = &amd_sfh_ops;
@@ -91,6 +97,31 @@ void sfh_interface_init(struct amd_mp2_dev *mp2)
 	emp2 = mp2;
 }
 
+static int amd_sfh_op_mode_info(u32 *op_mode)
+{
+	struct sfh_op_mode mode;
+	bool present;
+
+	if (!op_mode)
+		return -EINVAL;
+	if (!emp2)
+		return -ENODEV;
+
+	present = emp2->sfh1_1_ops ? emp2->dev_en.is_sra_present
+				   : (emp2->mp2_ver == MP2_VER_V2 &&
+				      amd_sfh_op_idx_enabled(emp2));
+	if (!present)
+		return -ENODEV;
+
+	mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3));
+	dev_dbg(&emp2->pdev->dev, "op-mode: %s (mode=%u)\n",
+		mode.op_mode.mode == SFH_MODE_TABLET ? "tablet" : "laptop",
+		mode.op_mode.mode);
+	*op_mode = mode.op_mode.mode;
+
+	return 0;
+}
+
 static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement)
 {
 	struct sfh_op_mode mode;
@@ -175,6 +206,8 @@ int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
 		case MT_SRA:
 			return amd_sfh_mode_info(&sfh_info->platform_type,
 						 &sfh_info->laptop_placement);
+		case MT_OP_MODE:
+			return amd_sfh_op_mode_info(&sfh_info->op_mode);
 		}
 	}
 	return -EINVAL;
diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h
index 665c99ad779f..56258c4d1b3a 100644
--- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h
+++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h
@@ -185,7 +185,6 @@ struct sfh_op_mode {
 };
 
 void sfh_interface_init(struct amd_mp2_dev *mp2);
-void sfh_deinit_emp2(void);
 void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops);
 int amd_sfh_float_to_int(u32 flt32_val);
 #endif
diff --git a/include/linux/amd-pmf-io.h b/include/linux/amd-pmf-io.h
index 55198d2875cc..dc59c43bd8f2 100644
--- a/include/linux/amd-pmf-io.h
+++ b/include/linux/amd-pmf-io.h
@@ -19,11 +19,13 @@
  * @MT_HPD: Message ID to know the Human presence info from MP2 FW
  * @MT_ALS: Message ID to know the Ambient light info from MP2 FW
  * @MT_SRA: Message ID to know the SRA data from MP2 FW
+ * @MT_OP_MODE: Message ID to know the operating-mode (tablet/laptop) info
  */
 enum sfh_message_type {
 	MT_HPD,
 	MT_ALS,
 	MT_SRA,
+	MT_OP_MODE,
 };
 
 /**
@@ -44,12 +46,24 @@ enum sfh_hpd_info {
  * @user_present: Populates the user presence information
  * @platform_type: Operating modes (clamshell, flat, tent, etc.)
  * @laptop_placement: Device states (ontable, onlap, outbag)
+ * @op_mode: Operating-mode field (see enum sfh_dev_mode); used for tablet detection
  */
 struct amd_sfh_info {
 	u32 ambient_light;
 	u8 user_present;
 	u32 platform_type;
 	u32 laptop_placement;
+	u32 op_mode;
+};
+
+/**
+ * enum sfh_dev_mode - SFH operating-mode field (sfh_op_mode.mode, bits 0-2)
+ * @SFH_MODE_LAPTOP: Device is in laptop/clamshell posture
+ * @SFH_MODE_TABLET: Device is in tablet posture
+ */
+enum sfh_dev_mode {
+	SFH_MODE_LAPTOP	= 1,
+	SFH_MODE_TABLET	= 3,
 };
 
 enum laptop_placement {
-- 
2.34.1