1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AMD SFH tablet-mode switch driver 4 * 5 * Copyright (c) 2026, Advanced Micro Devices, Inc. 6 * All Rights Reserved. 7 * 8 * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com> 9 */ 10 11 #include <linux/amd-pmf-io.h> 12 #include <linux/auxiliary_bus.h> 13 #include <linux/input.h> 14 #include <linux/module.h> 15 #include <linux/pci_ids.h> 16 17 #define POLL_INTERVAL_MS 200 18 19 static void sfh_tm_poll(struct input_dev *input) 20 { 21 struct amd_sfh_info info = {}; 22 23 if (amd_get_sfh_info(&info, MT_OP_MODE)) 24 return; 25 26 input_report_switch(input, SW_TABLET_MODE, 27 info.op_mode == SFH_MODE_TABLET); 28 input_sync(input); 29 } 30 31 static int sfh_tm_probe(struct auxiliary_device *auxdev, 32 const struct auxiliary_device_id *id) 33 { 34 struct device *dev = &auxdev->dev; 35 struct amd_sfh_info info = {}; 36 struct input_dev *input; 37 int error; 38 39 error = amd_get_sfh_info(&info, MT_OP_MODE); 40 if (error) 41 return error == -EINVAL ? -ENODEV : error; 42 43 input = devm_input_allocate_device(dev); 44 if (!input) 45 return -ENOMEM; 46 47 input->name = "AMD SFH tablet mode switch"; 48 input->phys = "amd-sfh/tabletmode"; 49 input->id.bustype = BUS_HOST; 50 input->id.vendor = PCI_VENDOR_ID_AMD; 51 input_set_capability(input, EV_SW, SW_TABLET_MODE); 52 53 error = input_setup_polling(input, sfh_tm_poll); 54 if (error) 55 return error; 56 input_set_poll_interval(input, POLL_INTERVAL_MS); 57 58 sfh_tm_poll(input); 59 60 error = input_register_device(input); 61 if (error) 62 return error; 63 64 return 0; 65 } 66 67 static const struct auxiliary_device_id sfh_tm_id_table[] = { 68 { .name = "amd_sfh.tabletmode" }, 69 {} 70 }; 71 MODULE_DEVICE_TABLE(auxiliary, sfh_tm_id_table); 72 73 static struct auxiliary_driver sfh_tm_driver = { 74 .name = "tabletmode", 75 .id_table = sfh_tm_id_table, 76 .probe = sfh_tm_probe, 77 }; 78 module_auxiliary_driver(sfh_tm_driver); 79 80 MODULE_DESCRIPTION("AMD SFH tablet mode switch"); 81 MODULE_LICENSE("GPL"); 82