mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-15 22:31:47 -04:00
Merge tag 'platform-drivers-x86-v7.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86
Pull x86 platform driver fixes from Ilpo Järvinen:
- asus-nb-wmi:
- Use existing keyboard quirk for ASUS Zenbook Duo UX8407AA
- hp-wmi:
- Add support for Victus 16-r0xxx (8BC2)
- intel/vsec_tpmi:
- Move debugfs register before creating devices
- Prevent fault during unbind
- lenovo-wmi-*:
- Fix memory leak in lwmi_dev_evaluate_int()
- Balance IDA id allocation and free
- Balance component bind and unbind
- Prevent sending uninitialized WMI arguments to the device
- Decouple lenovo-wmi-gamezone and lenovo-wmi-other to simplify
module dependency graph
- Limit adding attributes to supported devices
- samsung-galaxybook:
- Handle kbd backlight, mic mute and camera block hotkeys
* tag 'platform-drivers-x86-v7.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86:
platform/x86: asus-nb-wmi: add DMI quirk for ASUS Zenbook Duo UX8407AA
platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
platform/x86: lenovo-wmi-other: Add Attribute ID helper functions
platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
platform/x86: lenovo-wmi-other: Balance component bind and unbind
platform/x86: lenovo-wmi-other: Balance IDA id allocation and free
platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int()
platform/x86: hp-wmi: Add support for Victus 16-r0xxx (8BC2)
platform/x86/intel/tpmi/plr: Prevent fault during unbind
platform/x86: intel: Add notifiers support
platform/x86: intel: Move debugfs register before creating devices
platform/x86: samsung-galaxybook: Handle ACPI hotkey notifications
platform/x86: samsung-galaxybook: Refactor camera lens cover input device
This commit is contained in:
@@ -544,6 +544,15 @@ static const struct dmi_system_id asus_quirks[] = {
|
||||
},
|
||||
.driver_data = &quirk_asus_zenbook_duo_kbd,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUS Zenbook Duo UX8407AA",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook Duo UX8407AA"),
|
||||
},
|
||||
.driver_data = &quirk_asus_zenbook_duo_kbd,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUS ROG Z13",
|
||||
|
||||
@@ -205,6 +205,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") },
|
||||
.driver_data = (void *)&victus_s_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BC2") },
|
||||
.driver_data = (void *)&omen_v1_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCA") },
|
||||
.driver_data = (void *)&omen_v1_thermal_params,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/types.h>
|
||||
@@ -60,6 +61,8 @@ struct tpmi_plr {
|
||||
struct tpmi_plr_die *die_info;
|
||||
int num_dies;
|
||||
struct auxiliary_device *auxdev;
|
||||
struct notifier_block nb;
|
||||
struct mutex lock; /* Protect access to dbgfs_dir */
|
||||
};
|
||||
|
||||
static const char * const plr_coarse_reasons[] = {
|
||||
@@ -255,6 +258,30 @@ static ssize_t plr_status_write(struct file *filp, const char __user *ubuf,
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(plr_status);
|
||||
|
||||
static int intel_plr_notify(struct notifier_block *self, unsigned long action, void *data)
|
||||
{
|
||||
struct tpmi_plr *plr = container_of(self, struct tpmi_plr, nb);
|
||||
|
||||
if (action == TPMI_CORE_EXIT) {
|
||||
guard(mutex)(&plr->lock);
|
||||
plr->dbgfs_dir = NULL;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int intel_plr_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
nb->notifier_call = intel_plr_notify;
|
||||
nb->priority = 0;
|
||||
return tpmi_register_notifier(nb);
|
||||
}
|
||||
|
||||
static void intel_plr_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
tpmi_unregister_notifier(nb);
|
||||
}
|
||||
|
||||
static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct oobmsm_plat_info *plat_info;
|
||||
@@ -282,10 +309,18 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia
|
||||
if (!plr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devm_mutex_init(&auxdev->dev, &plr->lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
intel_plr_register_notifier(&plr->nb);
|
||||
|
||||
plr->die_info = devm_kcalloc(&auxdev->dev, num_resources, sizeof(*plr->die_info),
|
||||
GFP_KERNEL);
|
||||
if (!plr->die_info)
|
||||
return -ENOMEM;
|
||||
if (!plr->die_info) {
|
||||
err = -ENOMEM;
|
||||
goto err_notify;
|
||||
}
|
||||
|
||||
plr->num_dies = num_resources;
|
||||
plr->dbgfs_dir = debugfs_create_dir("plr", dentry);
|
||||
@@ -326,6 +361,9 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia
|
||||
|
||||
err:
|
||||
debugfs_remove_recursive(plr->dbgfs_dir);
|
||||
err_notify:
|
||||
intel_plr_unregister_notifier(&plr->nb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -333,6 +371,9 @@ static void intel_plr_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct tpmi_plr *plr = auxiliary_get_drvdata(auxdev);
|
||||
|
||||
intel_plr_unregister_notifier(&plr->nb);
|
||||
|
||||
guard(mutex)(&plr->lock);
|
||||
debugfs_remove_recursive(plr->dbgfs_dir);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/sizes.h>
|
||||
@@ -188,6 +189,20 @@ struct tpmi_feature_state {
|
||||
/* Used during auxbus device creation */
|
||||
static DEFINE_IDA(intel_vsec_tpmi_ida);
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(tpmi_notify_list);
|
||||
|
||||
int tpmi_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&tpmi_notify_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_register_notifier, "INTEL_TPMI");
|
||||
|
||||
int tpmi_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&tpmi_notify_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_unregister_notifier, "INTEL_TPMI");
|
||||
|
||||
struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
|
||||
@@ -817,10 +832,6 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
|
||||
|
||||
auxiliary_set_drvdata(auxdev, tpmi_info);
|
||||
|
||||
ret = tpmi_create_devices(tpmi_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Allow debugfs when security policy allows. Everything this debugfs
|
||||
* interface provides, can also be done via /dev/mem access. If
|
||||
@@ -830,6 +841,14 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
|
||||
if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO))
|
||||
tpmi_dbgfs_register(tpmi_info);
|
||||
|
||||
ret = tpmi_create_devices(tpmi_info);
|
||||
if (ret) {
|
||||
debugfs_remove_recursive(tpmi_info->dbgfs_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_INIT, auxdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -843,6 +862,8 @@ static void tpmi_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(auxdev);
|
||||
|
||||
blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_EXIT, auxdev);
|
||||
|
||||
debugfs_remove_recursive(tpmi_info->dbgfs_dir);
|
||||
}
|
||||
|
||||
|
||||
@@ -252,7 +252,6 @@ config LENOVO_WMI_GAMEZONE
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
select LENOVO_WMI_EVENTS
|
||||
select LENOVO_WMI_HELPERS
|
||||
select LENOVO_WMI_TUNING
|
||||
help
|
||||
Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
|
||||
platform-profile firmware interface to manage power usage.
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/component.h>
|
||||
@@ -48,6 +47,7 @@
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include "wmi-capdata.h"
|
||||
#include "wmi-helpers.h"
|
||||
|
||||
#define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
|
||||
#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
|
||||
@@ -57,9 +57,9 @@
|
||||
|
||||
#define LWMI_FEATURE_ID_FAN_TEST 0x05
|
||||
|
||||
#define LWMI_ATTR_ID_FAN_TEST \
|
||||
(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) | \
|
||||
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_TEST))
|
||||
#define LWMI_ATTR_ID_FAN_TEST \
|
||||
lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_TEST, \
|
||||
LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE)
|
||||
|
||||
enum lwmi_cd_type {
|
||||
LENOVO_CAPABILITY_DATA_00,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define _LENOVO_WMI_CAPDATA_H_
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define LWMI_SUPP_VALID BIT(0)
|
||||
@@ -19,6 +20,8 @@
|
||||
|
||||
#define LWMI_DEVICE_ID_FAN 0x04
|
||||
|
||||
#define LWMI_TYPE_ID_NONE 0x00
|
||||
|
||||
struct component_match;
|
||||
struct device;
|
||||
struct cd_list;
|
||||
@@ -57,6 +60,23 @@ struct lwmi_cd_binder {
|
||||
cd_list_cb_t cd_fan_list_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* lwmi_attr_id() - Formats a capability data attribute ID
|
||||
* @dev_id: The u8 corresponding to the device ID.
|
||||
* @feat_id: The u8 corresponding to the feature ID on the device.
|
||||
* @mode_id: The u8 corresponding to the wmi-gamezone mode for set/get.
|
||||
* @type_id: The u8 corresponding to the sub-device.
|
||||
*
|
||||
* Return: encoded capability data attribute ID.
|
||||
*/
|
||||
static inline u32 lwmi_attr_id(u8 dev_id, u8 feat_id, u8 mode_id, u8 type_id)
|
||||
{
|
||||
return (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, dev_id) |
|
||||
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, feat_id) |
|
||||
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode_id) |
|
||||
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, type_id));
|
||||
}
|
||||
|
||||
void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr);
|
||||
int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output);
|
||||
int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include "wmi-events.h"
|
||||
#include "wmi-gamezone.h"
|
||||
#include "wmi-helpers.h"
|
||||
|
||||
#define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F"
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include "wmi-events.h"
|
||||
#include "wmi-gamezone.h"
|
||||
#include "wmi-helpers.h"
|
||||
#include "wmi-other.h"
|
||||
|
||||
#define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
|
||||
|
||||
@@ -201,7 +199,7 @@ static int lwmi_gz_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct lwmi_gz_priv *priv = dev_get_drvdata(dev);
|
||||
struct wmi_method_args_32 args;
|
||||
struct wmi_method_args_32 args = {};
|
||||
enum thermal_mode mode;
|
||||
int ret;
|
||||
|
||||
@@ -383,7 +381,7 @@ static int lwmi_gz_probe(struct wmi_device *wdev, const void *context)
|
||||
return ret;
|
||||
|
||||
priv->mode_nb.notifier_call = lwmi_gz_mode_call;
|
||||
return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb);
|
||||
return devm_lwmi_tm_register_notifier(&wdev->dev, &priv->mode_nb);
|
||||
}
|
||||
|
||||
static const struct wmi_device_id lwmi_gz_id_table[] = {
|
||||
@@ -405,7 +403,6 @@ module_wmi_driver(lwmi_gz_driver);
|
||||
|
||||
MODULE_IMPORT_NS("LENOVO_WMI_EVENTS");
|
||||
MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
|
||||
MODULE_IMPORT_NS("LENOVO_WMI_OTHER");
|
||||
MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table);
|
||||
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||
MODULE_DESCRIPTION("Lenovo GameZone WMI Driver");
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||
|
||||
#ifndef _LENOVO_WMI_GAMEZONE_H_
|
||||
#define _LENOVO_WMI_GAMEZONE_H_
|
||||
|
||||
enum gamezone_events_type {
|
||||
LWMI_GZ_GET_THERMAL_MODE = 1,
|
||||
};
|
||||
|
||||
enum thermal_mode {
|
||||
LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
|
||||
LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
|
||||
LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
|
||||
LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */
|
||||
LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF,
|
||||
};
|
||||
|
||||
#endif /* !_LENOVO_WMI_GAMEZONE_H_ */
|
||||
@@ -21,11 +21,15 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include "wmi-helpers.h"
|
||||
|
||||
/* Thermal mode notifier chain. */
|
||||
static BLOCKING_NOTIFIER_HEAD(tm_chain_head);
|
||||
|
||||
/**
|
||||
* lwmi_dev_evaluate_int() - Helper function for calling WMI methods that
|
||||
* return an integer.
|
||||
@@ -46,7 +50,6 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||
unsigned char *buf, size_t size, u32 *retval)
|
||||
{
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *ret_obj __free(kfree) = NULL;
|
||||
struct acpi_buffer input = { size, buf };
|
||||
acpi_status status;
|
||||
|
||||
@@ -55,8 +58,9 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
union acpi_object *ret_obj __free(kfree) = output.pointer;
|
||||
|
||||
if (retval) {
|
||||
ret_obj = output.pointer;
|
||||
if (!ret_obj)
|
||||
return -ENODATA;
|
||||
|
||||
@@ -84,6 +88,103 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS");
|
||||
|
||||
/**
|
||||
* lwmi_tm_register_notifier() - Add a notifier to the blocking notifier chain
|
||||
* @nb: The notifier_block struct to register
|
||||
*
|
||||
* Call blocking_notifier_chain_register to register the notifier block to the
|
||||
* thermal mode notifier chain.
|
||||
*
|
||||
* Return: 0 on success, %-EEXIST on error.
|
||||
*/
|
||||
int lwmi_tm_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&tm_chain_head, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
|
||||
|
||||
/**
|
||||
* lwmi_tm_unregister_notifier() - Remove a notifier from the blocking notifier
|
||||
* chain.
|
||||
* @nb: The notifier_block struct to register
|
||||
*
|
||||
* Call blocking_notifier_chain_unregister to unregister the notifier block from the
|
||||
* thermal mode notifier chain.
|
||||
*
|
||||
* Return: 0 on success, %-ENOENT on error.
|
||||
*/
|
||||
int lwmi_tm_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&tm_chain_head, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_tm_unregister_notifier, "LENOVO_WMI_HELPERS");
|
||||
|
||||
/**
|
||||
* devm_lwmi_tm_unregister_notifier() - Remove a notifier from the blocking
|
||||
* notifier chain.
|
||||
* @data: Void pointer to the notifier_block struct to register.
|
||||
*
|
||||
* Call lwmi_tm_unregister_notifier to unregister the notifier block from the
|
||||
* thermal mode notifier chain.
|
||||
*
|
||||
* Return: 0 on success, %-ENOENT on error.
|
||||
*/
|
||||
static void devm_lwmi_tm_unregister_notifier(void *data)
|
||||
{
|
||||
struct notifier_block *nb = data;
|
||||
|
||||
lwmi_tm_unregister_notifier(nb);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_lwmi_tm_register_notifier() - Add a notifier to the blocking notifier
|
||||
* chain.
|
||||
* @dev: The parent device of the notifier_block struct.
|
||||
* @nb: The notifier_block struct to register
|
||||
*
|
||||
* Call lwmi_tm_register_notifier to register the notifier block to the
|
||||
* thermal mode notifier chain. Then add devm_lwmi_tm_unregister_notifier
|
||||
* as a device managed action to automatically unregister the notifier block
|
||||
* upon parent device removal.
|
||||
*
|
||||
* Return: 0 on success, or an error code.
|
||||
*/
|
||||
int devm_lwmi_tm_register_notifier(struct device *dev,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lwmi_tm_register_notifier(nb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, devm_lwmi_tm_unregister_notifier,
|
||||
nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
|
||||
|
||||
/**
|
||||
* lwmi_tm_notifier_call() - Call functions for the notifier call chain.
|
||||
* @mode: Pointer to a thermal mode enum to retrieve the data from.
|
||||
*
|
||||
* Call blocking_notifier_call_chain to retrieve the thermal mode from the
|
||||
* lenovo-wmi-gamezone driver.
|
||||
*
|
||||
* Return: 0 on success, or an error code.
|
||||
*/
|
||||
int lwmi_tm_notifier_call(enum thermal_mode *mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = blocking_notifier_call_chain(&tm_chain_head,
|
||||
LWMI_GZ_GET_THERMAL_MODE, &mode);
|
||||
if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
|
||||
|
||||
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||
MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device;
|
||||
struct notifier_block;
|
||||
struct wmi_device;
|
||||
|
||||
struct wmi_method_args_32 {
|
||||
@@ -14,7 +16,26 @@ struct wmi_method_args_32 {
|
||||
u32 arg1;
|
||||
};
|
||||
|
||||
enum lwmi_event_type {
|
||||
LWMI_GZ_GET_THERMAL_MODE = 0x01,
|
||||
};
|
||||
|
||||
enum thermal_mode {
|
||||
LWMI_GZ_THERMAL_MODE_NONE = 0x00,
|
||||
LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
|
||||
LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
|
||||
LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
|
||||
LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */
|
||||
LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF,
|
||||
};
|
||||
|
||||
int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||
unsigned char *buf, size_t size, u32 *retval);
|
||||
|
||||
int lwmi_tm_register_notifier(struct notifier_block *nb);
|
||||
int lwmi_tm_unregister_notifier(struct notifier_block *nb);
|
||||
int devm_lwmi_tm_register_notifier(struct device *dev,
|
||||
struct notifier_block *nb);
|
||||
int lwmi_tm_notifier_call(enum thermal_mode *mode);
|
||||
|
||||
#endif /* !_LENOVO_WMI_HELPERS_H_ */
|
||||
|
||||
@@ -40,16 +40,13 @@
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include "wmi-capdata.h"
|
||||
#include "wmi-events.h"
|
||||
#include "wmi-gamezone.h"
|
||||
#include "wmi-helpers.h"
|
||||
#include "wmi-other.h"
|
||||
#include "../firmware_attributes_class.h"
|
||||
|
||||
#define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
|
||||
@@ -62,8 +59,6 @@
|
||||
|
||||
#define LWMI_FEATURE_ID_FAN_RPM 0x03
|
||||
|
||||
#define LWMI_TYPE_ID_NONE 0x00
|
||||
|
||||
#define LWMI_FEATURE_VALUE_GET 17
|
||||
#define LWMI_FEATURE_VALUE_SET 18
|
||||
|
||||
@@ -71,17 +66,15 @@
|
||||
#define LWMI_FAN_NR 4
|
||||
#define LWMI_FAN_ID(x) ((x) + LWMI_FAN_ID_BASE)
|
||||
|
||||
#define LWMI_ATTR_ID_FAN_RPM(x) \
|
||||
(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) | \
|
||||
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) | \
|
||||
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x)))
|
||||
|
||||
#define LWMI_FAN_DIV 100
|
||||
|
||||
#define LWMI_ATTR_ID_FAN_RPM(x) \
|
||||
lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \
|
||||
LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x))
|
||||
|
||||
#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
|
||||
#define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(om_chain_head);
|
||||
static DEFINE_IDA(lwmi_om_ida);
|
||||
|
||||
enum attribute_property {
|
||||
@@ -109,7 +102,6 @@ struct lwmi_om_priv {
|
||||
struct device *hwmon_dev;
|
||||
struct device *fw_attr_dev;
|
||||
struct kset *fw_attr_kset;
|
||||
struct notifier_block nb;
|
||||
struct wmi_device *wdev;
|
||||
int ida_id;
|
||||
|
||||
@@ -166,7 +158,7 @@ MODULE_PARM_DESC(relax_fan_constraint,
|
||||
*/
|
||||
static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
|
||||
{
|
||||
struct wmi_method_args_32 args;
|
||||
struct wmi_method_args_32 args = {};
|
||||
u32 method_id, retval;
|
||||
int err;
|
||||
|
||||
@@ -548,13 +540,26 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
|
||||
/* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
|
||||
|
||||
struct tunable_attr_01 {
|
||||
struct capdata01 *capdata;
|
||||
struct device *dev;
|
||||
u32 feature_id;
|
||||
u32 device_id;
|
||||
u32 type_id;
|
||||
u8 feature_id;
|
||||
u8 device_id;
|
||||
u8 type_id;
|
||||
u8 cd_mode_id; /* mode arg for searching capdata */
|
||||
u8 cv_mode_id; /* mode arg for set/get current_value */
|
||||
};
|
||||
|
||||
/**
|
||||
* tunable_attr_01_id() - Formats a tunable_attr_01 to a capdata attribute ID
|
||||
* @attr: The tunable_attr_01 to format.
|
||||
* @mode: The u8 corresponding to the wmi-gamezone mode for set/get.
|
||||
*
|
||||
* Return: encoded capability data attribute ID.
|
||||
*/
|
||||
static u32 tunable_attr_01_id(struct tunable_attr_01 *attr, u8 mode)
|
||||
{
|
||||
return lwmi_attr_id(attr->device_id, attr->feature_id, mode, attr->type_id);
|
||||
}
|
||||
|
||||
static struct tunable_attr_01 ppt_pl1_spl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_SPL,
|
||||
@@ -578,102 +583,6 @@ struct capdata01_attr_group {
|
||||
struct tunable_attr_01 *tunable_attr;
|
||||
};
|
||||
|
||||
/**
|
||||
* lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain
|
||||
* @nb: The notifier_block struct to register
|
||||
*
|
||||
* Call blocking_notifier_chain_register to register the notifier block to the
|
||||
* lenovo-wmi-other driver notifier chain.
|
||||
*
|
||||
* Return: 0 on success, %-EEXIST on error.
|
||||
*/
|
||||
int lwmi_om_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&om_chain_head, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
|
||||
|
||||
/**
|
||||
* lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier
|
||||
* chain.
|
||||
* @nb: The notifier_block struct to register
|
||||
*
|
||||
* Call blocking_notifier_chain_unregister to unregister the notifier block from the
|
||||
* lenovo-wmi-other driver notifier chain.
|
||||
*
|
||||
* Return: 0 on success, %-ENOENT on error.
|
||||
*/
|
||||
int lwmi_om_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&om_chain_head, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER");
|
||||
|
||||
/**
|
||||
* devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking
|
||||
* notifier chain.
|
||||
* @data: Void pointer to the notifier_block struct to register.
|
||||
*
|
||||
* Call lwmi_om_unregister_notifier to unregister the notifier block from the
|
||||
* lenovo-wmi-other driver notifier chain.
|
||||
*
|
||||
* Return: 0 on success, %-ENOENT on error.
|
||||
*/
|
||||
static void devm_lwmi_om_unregister_notifier(void *data)
|
||||
{
|
||||
struct notifier_block *nb = data;
|
||||
|
||||
lwmi_om_unregister_notifier(nb);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier
|
||||
* chain.
|
||||
* @dev: The parent device of the notifier_block struct.
|
||||
* @nb: The notifier_block struct to register
|
||||
*
|
||||
* Call lwmi_om_register_notifier to register the notifier block to the
|
||||
* lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier
|
||||
* as a device managed action to automatically unregister the notifier block
|
||||
* upon parent device removal.
|
||||
*
|
||||
* Return: 0 on success, or an error code.
|
||||
*/
|
||||
int devm_lwmi_om_register_notifier(struct device *dev,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lwmi_om_register_notifier(nb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier,
|
||||
nb);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
|
||||
|
||||
/**
|
||||
* lwmi_om_notifier_call() - Call functions for the notifier call chain.
|
||||
* @mode: Pointer to a thermal mode enum to retrieve the data from.
|
||||
*
|
||||
* Call blocking_notifier_call_chain to retrieve the thermal mode from the
|
||||
* lenovo-wmi-gamezone driver.
|
||||
*
|
||||
* Return: 0 on success, or an error code.
|
||||
*/
|
||||
static int lwmi_om_notifier_call(enum thermal_mode *mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = blocking_notifier_call_chain(&om_chain_head,
|
||||
LWMI_GZ_GET_THERMAL_MODE, &mode);
|
||||
if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Attribute Methods */
|
||||
|
||||
/**
|
||||
@@ -718,12 +627,7 @@ static ssize_t attr_capdata01_show(struct kobject *kobj,
|
||||
u32 attribute_id;
|
||||
int value, ret;
|
||||
|
||||
attribute_id =
|
||||
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
|
||||
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
|
||||
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK,
|
||||
LWMI_GZ_THERMAL_MODE_CUSTOM) |
|
||||
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
|
||||
attribute_id = tunable_attr_01_id(tunable_attr, tunable_attr->cd_mode_id);
|
||||
|
||||
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
|
||||
if (ret)
|
||||
@@ -775,27 +679,22 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
|
||||
struct tunable_attr_01 *tunable_attr)
|
||||
{
|
||||
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
|
||||
struct wmi_method_args_32 args;
|
||||
struct wmi_method_args_32 args = {};
|
||||
struct capdata01 capdata;
|
||||
enum thermal_mode mode;
|
||||
u32 attribute_id;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
ret = lwmi_om_notifier_call(&mode);
|
||||
ret = lwmi_tm_notifier_call(&mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
|
||||
return -EBUSY;
|
||||
|
||||
attribute_id =
|
||||
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
|
||||
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
|
||||
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
|
||||
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
|
||||
args.arg0 = tunable_attr_01_id(tunable_attr, tunable_attr->cd_mode_id);
|
||||
|
||||
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
|
||||
ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -806,7 +705,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
|
||||
if (value < capdata.min_value || value > capdata.max_value)
|
||||
return -EINVAL;
|
||||
|
||||
args.arg0 = attribute_id;
|
||||
args.arg0 = tunable_attr_01_id(tunable_attr, tunable_attr->cv_mode_id);
|
||||
args.arg1 = value;
|
||||
|
||||
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
|
||||
@@ -838,23 +737,20 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
|
||||
struct tunable_attr_01 *tunable_attr)
|
||||
{
|
||||
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
|
||||
struct wmi_method_args_32 args;
|
||||
struct wmi_method_args_32 args = {};
|
||||
enum thermal_mode mode;
|
||||
u32 attribute_id;
|
||||
int retval;
|
||||
int ret;
|
||||
|
||||
ret = lwmi_om_notifier_call(&mode);
|
||||
ret = lwmi_tm_notifier_call(&mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
attribute_id =
|
||||
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
|
||||
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
|
||||
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
|
||||
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
|
||||
/* If "no-mode" is the supported mode, ensure we never send current mode */
|
||||
if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
|
||||
mode = tunable_attr->cv_mode_id;
|
||||
|
||||
args.arg0 = attribute_id;
|
||||
args.arg0 = tunable_attr_01_id(tunable_attr, mode);
|
||||
|
||||
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
|
||||
(unsigned char *)&args, sizeof(args),
|
||||
@@ -865,6 +761,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
|
||||
return sysfs_emit(buf, "%d\n", retval);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
|
||||
* @tunable_attr: The attribute to verify.
|
||||
*
|
||||
* For an attribute to be supported it must have a functional get/set method,
|
||||
* as well as associated capability data stored in the capdata01 table.
|
||||
*
|
||||
* First check if the attribute has a corresponding data table under custom mode
|
||||
* (0xff), then under no mode (0x00). If either of those passes, check if the
|
||||
* supported field of the capdata struct is > 0. If it is supported, store the
|
||||
* successful mode in the cd_mode_id field of tunable_attr.
|
||||
*
|
||||
* If the attribute capdata shows it is supported, attempt to determine the mode
|
||||
* for the current value property get/set methods using a similar pattern to the
|
||||
* capdata table check. If the value returned by either mode is 0 or an error,
|
||||
* assume that mode is not supported. Otherwise, store the successful mode in the
|
||||
* cv_mode_id field of tunable_attr.
|
||||
*
|
||||
* If any of the above checks fail then the attribute is not fully supported.
|
||||
*
|
||||
* Return: true if capdata and set/get modes are found, otherwise false.
|
||||
*/
|
||||
static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
|
||||
{
|
||||
u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
|
||||
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
|
||||
struct wmi_method_args_32 args = {};
|
||||
bool cd_mode_found = false;
|
||||
bool cv_mode_found = false;
|
||||
struct capdata01 capdata;
|
||||
int retval, ret, i;
|
||||
|
||||
/* Determine tunable_attr->cd_mode_id */
|
||||
for (i = 0; i < ARRAY_SIZE(modes); i++) {
|
||||
args.arg0 = tunable_attr_01_id(tunable_attr, modes[i]);
|
||||
|
||||
ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
|
||||
if (ret || !capdata.supported)
|
||||
continue;
|
||||
|
||||
tunable_attr->cd_mode_id = modes[i];
|
||||
cd_mode_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cd_mode_found)
|
||||
return cd_mode_found;
|
||||
|
||||
dev_dbg(tunable_attr->dev,
|
||||
"cd_mode_id: %#010x\n", args.arg0);
|
||||
|
||||
/* Determine tunable_attr->cv_mode_id, returns 1 if supported */
|
||||
for (i = 0; i < ARRAY_SIZE(modes); i++) {
|
||||
args.arg0 = tunable_attr_01_id(tunable_attr, modes[i]);
|
||||
|
||||
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
|
||||
(u8 *)&args, sizeof(args),
|
||||
&retval);
|
||||
if (ret || !retval)
|
||||
continue;
|
||||
|
||||
tunable_attr->cv_mode_id = modes[i];
|
||||
cv_mode_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cv_mode_found)
|
||||
return cv_mode_found;
|
||||
|
||||
dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
|
||||
args.arg0, capdata.supported);
|
||||
|
||||
return capdata.supported > 0;
|
||||
}
|
||||
|
||||
/* Lenovo WMI Other Mode Attribute macros */
|
||||
#define __LWMI_ATTR_RO(_func, _name) \
|
||||
{ \
|
||||
@@ -959,17 +930,17 @@ static struct capdata01_attr_group cd01_attr_groups[] = {
|
||||
/**
|
||||
* lwmi_om_fw_attr_add() - Register all firmware_attributes_class members
|
||||
* @priv: The Other Mode driver data.
|
||||
*
|
||||
* Return: Either 0, or an error code.
|
||||
*/
|
||||
static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
||||
static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
|
||||
if (priv->ida_id < 0)
|
||||
return priv->ida_id;
|
||||
err = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
|
||||
if (err < 0)
|
||||
goto err_no_ida;
|
||||
|
||||
priv->ida_id = err;
|
||||
|
||||
priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
|
||||
MKDEV(0, 0), NULL, "%s-%u",
|
||||
@@ -988,14 +959,16 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
|
||||
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
|
||||
if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
|
||||
continue;
|
||||
|
||||
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
|
||||
cd01_attr_groups[i].attr_group);
|
||||
if (err)
|
||||
goto err_remove_groups;
|
||||
|
||||
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
|
||||
}
|
||||
return 0;
|
||||
return;
|
||||
|
||||
err_remove_groups:
|
||||
while (i--)
|
||||
@@ -1009,7 +982,12 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
||||
|
||||
err_free_ida:
|
||||
ida_free(&lwmi_om_ida, priv->ida_id);
|
||||
return err;
|
||||
|
||||
err_no_ida:
|
||||
priv->ida_id = -EIDRM;
|
||||
|
||||
dev_warn(&priv->wdev->dev,
|
||||
"failed to register firmware-attributes device: %d\n", err);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1018,12 +996,17 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
||||
*/
|
||||
static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
|
||||
{
|
||||
if (priv->ida_id < 0)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
|
||||
sysfs_remove_group(&priv->fw_attr_kset->kobj,
|
||||
cd01_attr_groups[i].attr_group);
|
||||
|
||||
kset_unregister(priv->fw_attr_kset);
|
||||
device_unregister(priv->fw_attr_dev);
|
||||
ida_free(&lwmi_om_ida, priv->ida_id);
|
||||
priv->ida_id = -EIDRM;
|
||||
}
|
||||
|
||||
/* ======== Self (master: lenovo-wmi-other) ======== */
|
||||
@@ -1060,12 +1043,17 @@ static int lwmi_om_master_bind(struct device *dev)
|
||||
|
||||
priv->cd00_list = binder.cd00_list;
|
||||
priv->cd01_list = binder.cd01_list;
|
||||
if (!priv->cd00_list || !priv->cd01_list)
|
||||
if (!priv->cd00_list || !priv->cd01_list) {
|
||||
component_unbind_all(dev, NULL);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
lwmi_om_fan_info_collect_cd00(priv);
|
||||
|
||||
return lwmi_om_fw_attr_add(priv);
|
||||
lwmi_om_fw_attr_add(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1117,13 +1105,7 @@ static int lwmi_other_probe(struct wmi_device *wdev, const void *context)
|
||||
|
||||
static void lwmi_other_remove(struct wmi_device *wdev)
|
||||
{
|
||||
struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
component_master_del(&wdev->dev, &lwmi_om_master_ops);
|
||||
|
||||
/* No IDA to free if the driver is never bound to its components. */
|
||||
if (priv->ida_id >= 0)
|
||||
ida_free(&lwmi_om_ida, priv->ida_id);
|
||||
}
|
||||
|
||||
static const struct wmi_device_id lwmi_other_id_table[] = {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||
|
||||
#ifndef _LENOVO_WMI_OTHER_H_
|
||||
#define _LENOVO_WMI_OTHER_H_
|
||||
|
||||
struct device;
|
||||
struct notifier_block;
|
||||
|
||||
int lwmi_om_register_notifier(struct notifier_block *nb);
|
||||
int lwmi_om_unregister_notifier(struct notifier_block *nb);
|
||||
int devm_lwmi_om_register_notifier(struct device *dev,
|
||||
struct notifier_block *nb);
|
||||
|
||||
#endif /* !_LENOVO_WMI_OTHER_H_ */
|
||||
@@ -53,7 +53,7 @@ struct samsung_galaxybook {
|
||||
void *i8042_filter_ptr;
|
||||
|
||||
struct work_struct block_recording_hotkey_work;
|
||||
struct input_dev *camera_lens_cover_switch;
|
||||
struct input_dev *input;
|
||||
|
||||
struct acpi_battery_hook battery_hook;
|
||||
|
||||
@@ -197,6 +197,9 @@ static const guid_t performance_mode_guid =
|
||||
#define GB_ACPI_NOTIFY_DEVICE_ON_TABLE 0x6c
|
||||
#define GB_ACPI_NOTIFY_DEVICE_OFF_TABLE 0x6d
|
||||
#define GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE 0x70
|
||||
#define GB_ACPI_NOTIFY_HOTKEY_KBD_BACKLIGHT 0x7d
|
||||
#define GB_ACPI_NOTIFY_HOTKEY_MICMUTE 0x6e
|
||||
#define GB_ACPI_NOTIFY_HOTKEY_CAMERA 0x6f
|
||||
|
||||
#define GB_KEY_KBD_BACKLIGHT_KEYDOWN 0x2c
|
||||
#define GB_KEY_KBD_BACKLIGHT_KEYUP 0xac
|
||||
@@ -859,13 +862,29 @@ static int block_recording_acpi_set(struct samsung_galaxybook *galaxybook, const
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
input_report_switch(galaxybook->camera_lens_cover_switch,
|
||||
input_report_switch(galaxybook->input,
|
||||
SW_CAMERA_LENS_COVER, value ? 1 : 0);
|
||||
input_sync(galaxybook->camera_lens_cover_switch);
|
||||
input_sync(galaxybook->input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int galaxybook_input_init(struct samsung_galaxybook *galaxybook)
|
||||
{
|
||||
galaxybook->input = devm_input_allocate_device(&galaxybook->platform->dev);
|
||||
if (!galaxybook->input)
|
||||
return -ENOMEM;
|
||||
|
||||
galaxybook->input->name = "Samsung Galaxy Book Camera Lens Cover";
|
||||
galaxybook->input->phys = DRIVER_NAME "/input0";
|
||||
galaxybook->input->id.bustype = BUS_HOST;
|
||||
|
||||
input_set_capability(galaxybook->input, EV_KEY, KEY_MICMUTE);
|
||||
input_set_capability(galaxybook->input, EV_SW, SW_CAMERA_LENS_COVER);
|
||||
|
||||
return input_register_device(galaxybook->input);
|
||||
}
|
||||
|
||||
static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook)
|
||||
{
|
||||
bool value;
|
||||
@@ -887,24 +906,8 @@ static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook
|
||||
return GB_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
galaxybook->camera_lens_cover_switch =
|
||||
devm_input_allocate_device(&galaxybook->platform->dev);
|
||||
if (!galaxybook->camera_lens_cover_switch)
|
||||
return -ENOMEM;
|
||||
|
||||
galaxybook->camera_lens_cover_switch->name = "Samsung Galaxy Book Camera Lens Cover";
|
||||
galaxybook->camera_lens_cover_switch->phys = DRIVER_NAME "/input0";
|
||||
galaxybook->camera_lens_cover_switch->id.bustype = BUS_HOST;
|
||||
|
||||
input_set_capability(galaxybook->camera_lens_cover_switch, EV_SW, SW_CAMERA_LENS_COVER);
|
||||
|
||||
err = input_register_device(galaxybook->camera_lens_cover_switch);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
input_report_switch(galaxybook->camera_lens_cover_switch,
|
||||
SW_CAMERA_LENS_COVER, value ? 1 : 0);
|
||||
input_sync(galaxybook->camera_lens_cover_switch);
|
||||
input_report_switch(galaxybook->input, SW_CAMERA_LENS_COVER, value ? 1 : 0);
|
||||
input_sync(galaxybook->input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1260,6 +1263,25 @@ static void galaxybook_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||
if (galaxybook->has_performance_mode)
|
||||
platform_profile_cycle();
|
||||
break;
|
||||
case GB_ACPI_NOTIFY_HOTKEY_KBD_BACKLIGHT:
|
||||
if (galaxybook->has_kbd_backlight)
|
||||
schedule_work(&galaxybook->kbd_backlight_hotkey_work);
|
||||
break;
|
||||
case GB_ACPI_NOTIFY_HOTKEY_MICMUTE:
|
||||
input_report_key(galaxybook->input, KEY_MICMUTE, 1);
|
||||
input_sync(galaxybook->input);
|
||||
input_report_key(galaxybook->input, KEY_MICMUTE, 0);
|
||||
input_sync(galaxybook->input);
|
||||
break;
|
||||
case GB_ACPI_NOTIFY_HOTKEY_CAMERA:
|
||||
if (galaxybook->has_block_recording) {
|
||||
schedule_work(&galaxybook->block_recording_hotkey_work);
|
||||
} else {
|
||||
input_report_switch(galaxybook->input, SW_CAMERA_LENS_COVER,
|
||||
!test_bit(SW_CAMERA_LENS_COVER, galaxybook->input->sw));
|
||||
input_sync(galaxybook->input);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_warn(&galaxybook->platform->dev,
|
||||
"unknown ACPI notification event: 0x%x\n", event);
|
||||
@@ -1392,6 +1414,11 @@ static int galaxybook_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(&galaxybook->platform->dev, err,
|
||||
"failed to initialize kbd_backlight\n");
|
||||
|
||||
err = galaxybook_input_init(galaxybook);
|
||||
if (err)
|
||||
return dev_err_probe(&galaxybook->platform->dev, err,
|
||||
"failed to initialize input device\n");
|
||||
|
||||
err = galaxybook_fw_attrs_init(galaxybook);
|
||||
if (err)
|
||||
return dev_err_probe(&galaxybook->platform->dev, err,
|
||||
|
||||
@@ -28,6 +28,12 @@ enum intel_tpmi_id {
|
||||
TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
|
||||
};
|
||||
|
||||
#define TPMI_CORE_INIT 0
|
||||
#define TPMI_CORE_EXIT 1
|
||||
|
||||
int tpmi_register_notifier(struct notifier_block *nb);
|
||||
int tpmi_unregister_notifier(struct notifier_block *nb);
|
||||
|
||||
struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev);
|
||||
struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index);
|
||||
int tpmi_get_resource_count(struct auxiliary_device *auxdev);
|
||||
|
||||
Reference in New Issue
Block a user