platform-drivers-x86 for 4.8-1
Several new quirks and tweaks for new platforms to existing laptop drivers. A new ACPI virtual power button driver, similar to the intel-hid driver. A rework of the dell keymap, using a single sparse keymap for all machines. A few fixes and cleanups. intel-vbtn: - new driver for Intel Virtual Button intel_pmc_core: - Convert to DEFINE_DEBUGFS_ATTRIBUTE fujitsu-laptop: - Rework brightness of eco led asus-wmi: - Add quirk_no_rfkill_wapf4 for the Asus X456UA - Add quirk_no_rfkill_wapf4 for the Asus X456UF - Add quirk_no_rfkill for the Asus Z550MA - Add quirk_no_rfkill for the Asus U303LB - Add quirk_no_rfkill for the Asus N552VW - Create quirk for airplane_mode LED - Add ambient light sensor toggle key asus-wireless: - Toggle airplane mode LED intel_telemetry: - Remove Monitor MWAIT feature dependency intel-hid: - Remove duplicated acpi_remove_notify_handler fujitsu-laptop: - Add support for eco LED - Support touchpad toggle hotkey on Skylake-based models - Remove unused macros - Use module name in debug messages hp-wmi: - Fix wifi cannot be hard-unblocked toshiba_acpi: - Bump driver version and update copyright year - Remove the position sysfs entry - Add IIO interface for accelerometer axis data dell-wmi: - Add a WMI event code for display on/off - Generate one sparse keymap for all machines - Add information about other WMI event codes - Sort WMI event codes and update comments - Ignore WMI event code 0xe045 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJXl8kKAAoJEKbMaAwKp364UggH/R1vy4IQmvXh4A/VDlAUp4Cs 6Wi71WTBbzFm63wVdHFZpeXzlzBCLEp+XVQI6M4BSQYulfRCWogiRmV7npLXn8Mv M2JnmV87n0u3bHT5Fn920mC0OeTMHV5YaEap8N3ys0Ej56aHEgjsQYJjjSIz1OpM 1uQTxufiMgUhX0PQskhNnrz+wnIeKatvP/EyxeFsW58T+FXr4xF8VNaqXgNbz1NL UcWOh580nE92I1UuYjAnC2bi1oi4x63vNL2xgll8Tc0rRuPJWB15Sc7zxdhVLhqX rV9Mjiu8FLKilQgwzPHm3QGb8o54prJZIUtAKp1qunwlDiM3n+5qrIW7+4GUfyo= =JSkT -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v4.8-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86 Pull x8 platform driver updates from Darren Hart: "Several new quirks and tweaks for new platforms to existing laptop drivers. A new ACPI virtual power button driver, similar to the intel-hid driver. A rework of the dell keymap, using a single sparse keymap for all machines. A few fixes and cleanups. Summary: intel-vbtn: - new driver for Intel Virtual Button intel_pmc_core: - Convert to DEFINE_DEBUGFS_ATTRIBUTE fujitsu-laptop: - Rework brightness of eco led asus-wmi: - Add quirk_no_rfkill_wapf4 for the Asus X456UA - Add quirk_no_rfkill_wapf4 for the Asus X456UF - Add quirk_no_rfkill for the Asus Z550MA - Add quirk_no_rfkill for the Asus U303LB - Add quirk_no_rfkill for the Asus N552VW - Create quirk for airplane_mode LED - Add ambient light sensor toggle key asus-wireless: - Toggle airplane mode LED intel_telemetry: - Remove Monitor MWAIT feature dependency intel-hid: - Remove duplicated acpi_remove_notify_handler fujitsu-laptop: - Add support for eco LED - Support touchpad toggle hotkey on Skylake-based models - Remove unused macros - Use module name in debug messages hp-wmi: - Fix wifi cannot be hard-unblocked toshiba_acpi: - Bump driver version and update copyright year - Remove the position sysfs entry - Add IIO interface for accelerometer axis data dell-wmi: - Add a WMI event code for display on/off - Generate one sparse keymap for all machines - Add information about other WMI event codes - Sort WMI event codes and update comments - Ignore WMI event code 0xe045" * tag 'platform-drivers-x86-v4.8-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (26 commits) intel-vbtn: new driver for Intel Virtual Button intel_pmc_core: Convert to DEFINE_DEBUGFS_ATTRIBUTE fujitsu-laptop: Rework brightness of eco led asus-wmi: Add quirk_no_rfkill_wapf4 for the Asus X456UA asus-wmi: Add quirk_no_rfkill_wapf4 for the Asus X456UF asus-wmi: Add quirk_no_rfkill for the Asus Z550MA asus-wmi: Add quirk_no_rfkill for the Asus U303LB asus-wmi: Add quirk_no_rfkill for the Asus N552VW asus-wmi: Create quirk for airplane_mode LED asus-wireless: Toggle airplane mode LED intel_telemetry: Remove Monitor MWAIT feature dependency intel-hid: Remove duplicated acpi_remove_notify_handler asus-wmi: Add ambient light sensor toggle key fujitsu-laptop: Add support for eco LED fujitsu-laptop: Support touchpad toggle hotkey on Skylake-based models fujitsu-laptop: Remove unused macros fujitsu-laptop: Use module name in debug messages hp-wmi: Fix wifi cannot be hard-unblocked toshiba_acpi: Bump driver version and update copyright year toshiba_acpi: Remove the position sysfs entry ...
This commit is contained in:
Коммит
27b79027bc
|
@ -5921,6 +5921,12 @@ L: platform-driver-x86@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/intel-hid.c
|
||||
|
||||
INTEL VIRTUAL BUTTON DRIVER
|
||||
M: AceLan Kao <acelan.kao@canonical.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel-vbtn.c
|
||||
|
||||
INTEL IDLE DRIVER
|
||||
M: Len Brown <lenb@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
|
|
|
@ -603,6 +603,8 @@ config ASUS_WIRELESS
|
|||
tristate "Asus Wireless Radio Control Driver"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
---help---
|
||||
The Asus Wireless Radio Control handles the airplane mode hotkey
|
||||
present on some Asus laptops.
|
||||
|
@ -668,6 +670,7 @@ config ACPI_TOSHIBA
|
|||
depends on SERIO_I8042 || SERIO_I8042 = n
|
||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||
depends on RFKILL || RFKILL = n
|
||||
depends on IIO
|
||||
select INPUT_POLLDEV
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
|
@ -770,6 +773,18 @@ config INTEL_HID_EVENT
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel_hid.
|
||||
|
||||
config INTEL_VBTN
|
||||
tristate "INTEL VIRTUAL BUTTON"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
help
|
||||
This driver provides support for the Intel Virtual Button interface.
|
||||
Some laptops require this driver for power button support.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel_vbtn.
|
||||
|
||||
config INTEL_SCU_IPC
|
||||
bool "Intel SCU IPC Support"
|
||||
depends on X86_INTEL_MID
|
||||
|
|
|
@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
|||
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
|
||||
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
|
||||
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
|
||||
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
|
|
|
@ -78,6 +78,15 @@ static struct quirk_entry quirk_asus_x200ca = {
|
|||
.wapf = 2,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_no_rfkill = {
|
||||
.no_rfkill = true,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_no_rfkill_wapf4 = {
|
||||
.wapf = 4,
|
||||
.no_rfkill = true,
|
||||
};
|
||||
|
||||
static int dmi_matched(const struct dmi_system_id *dmi)
|
||||
{
|
||||
quirks = dmi->driver_data;
|
||||
|
@ -133,7 +142,7 @@ static const struct dmi_system_id asus_quirks[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"),
|
||||
},
|
||||
.driver_data = &quirk_asus_wapf4,
|
||||
.driver_data = &quirk_no_rfkill_wapf4,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
|
@ -142,7 +151,7 @@ static const struct dmi_system_id asus_quirks[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"),
|
||||
},
|
||||
.driver_data = &quirk_asus_wapf4,
|
||||
.driver_data = &quirk_no_rfkill_wapf4,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
|
@ -306,6 +315,42 @@ static const struct dmi_system_id asus_quirks[] = {
|
|||
},
|
||||
.driver_data = &quirk_asus_x200ca,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. X555UB",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X555UB"),
|
||||
},
|
||||
.driver_data = &quirk_no_rfkill,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. N552VW",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "N552VW"),
|
||||
},
|
||||
.driver_data = &quirk_no_rfkill,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. U303LB",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "U303LB"),
|
||||
},
|
||||
.driver_data = &quirk_no_rfkill,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. Z550MA",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z550MA"),
|
||||
},
|
||||
.driver_data = &quirk_no_rfkill,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -356,6 +401,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
|
|||
{ KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
|
||||
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
|
||||
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */
|
||||
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
|
||||
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
|
||||
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
|
||||
{ KE_KEY, 0x82, { KEY_CAMERA } },
|
||||
|
|
|
@ -15,11 +15,78 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#define ASUS_WIRELESS_LED_STATUS 0x2
|
||||
#define ASUS_WIRELESS_LED_OFF 0x4
|
||||
#define ASUS_WIRELESS_LED_ON 0x5
|
||||
|
||||
struct asus_wireless_data {
|
||||
struct input_dev *idev;
|
||||
struct acpi_device *adev;
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct led_work;
|
||||
struct led_classdev led;
|
||||
int led_state;
|
||||
};
|
||||
|
||||
static u64 asus_wireless_method(acpi_handle handle, const char *method,
|
||||
int param)
|
||||
{
|
||||
struct acpi_object_list p;
|
||||
union acpi_object obj;
|
||||
acpi_status s;
|
||||
u64 ret;
|
||||
|
||||
acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
|
||||
method, param);
|
||||
obj.type = ACPI_TYPE_INTEGER;
|
||||
obj.integer.value = param;
|
||||
p.count = 1;
|
||||
p.pointer = &obj;
|
||||
|
||||
s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret);
|
||||
if (ACPI_FAILURE(s))
|
||||
acpi_handle_err(handle,
|
||||
"Failed to eval method %s, param %#x (%d)\n",
|
||||
method, param, s);
|
||||
acpi_handle_debug(handle, "%s returned %#x\n", method, (uint) ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum led_brightness led_state_get(struct led_classdev *led)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
int s;
|
||||
|
||||
data = container_of(led, struct asus_wireless_data, led);
|
||||
s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
|
||||
ASUS_WIRELESS_LED_STATUS);
|
||||
if (s == ASUS_WIRELESS_LED_ON)
|
||||
return LED_FULL;
|
||||
return LED_OFF;
|
||||
}
|
||||
|
||||
static void led_state_update(struct work_struct *work)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
|
||||
data = container_of(work, struct asus_wireless_data, led_work);
|
||||
asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
|
||||
data->led_state);
|
||||
}
|
||||
|
||||
static void led_state_set(struct led_classdev *led,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
|
||||
data = container_of(led, struct asus_wireless_data, led);
|
||||
data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
|
||||
ASUS_WIRELESS_LED_ON;
|
||||
queue_work(data->wq, &data->led_work);
|
||||
}
|
||||
|
||||
static void asus_wireless_notify(struct acpi_device *adev, u32 event)
|
||||
{
|
||||
struct asus_wireless_data *data = acpi_driver_data(adev);
|
||||
|
@ -37,6 +104,7 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
|
|||
static int asus_wireless_add(struct acpi_device *adev)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev)
|
|||
data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
|
||||
set_bit(EV_KEY, data->idev->evbit);
|
||||
set_bit(KEY_RFKILL, data->idev->keybit);
|
||||
return input_register_device(data->idev);
|
||||
err = input_register_device(data->idev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->adev = adev;
|
||||
data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
|
||||
if (!data->wq)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&data->led_work, led_state_update);
|
||||
data->led.name = "asus-wireless::airplane";
|
||||
data->led.brightness_set = led_state_set;
|
||||
data->led.brightness_get = led_state_get;
|
||||
data->led.flags = LED_CORE_SUSPENDRESUME;
|
||||
data->led.max_brightness = 1;
|
||||
err = devm_led_classdev_register(&adev->dev, &data->led);
|
||||
if (err)
|
||||
destroy_workqueue(data->wq);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int asus_wireless_remove(struct acpi_device *adev)
|
||||
{
|
||||
struct asus_wireless_data *data = acpi_driver_data(adev);
|
||||
|
||||
if (data->wq)
|
||||
destroy_workqueue(data->wq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2069,9 +2069,11 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto fail_leds;
|
||||
|
||||
err = asus_wmi_rfkill_init(asus);
|
||||
if (err)
|
||||
goto fail_rfkill;
|
||||
if (!asus->driver->quirks->no_rfkill) {
|
||||
err = asus_wmi_rfkill_init(asus);
|
||||
if (err)
|
||||
goto fail_rfkill;
|
||||
}
|
||||
|
||||
/* Some Asus desktop boards export an acpi-video backlight interface,
|
||||
stop this from showing up */
|
||||
|
|
|
@ -38,6 +38,7 @@ struct key_entry;
|
|||
struct asus_wmi;
|
||||
|
||||
struct quirk_entry {
|
||||
bool no_rfkill;
|
||||
bool hotplug_wireless;
|
||||
bool scalar_panel_brightness;
|
||||
bool store_backlight_power;
|
||||
|
|
|
@ -80,66 +80,115 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Keymap for WMI events of type 0x0000
|
||||
*
|
||||
* Certain keys are flagged as KE_IGNORE. All of these are either
|
||||
* notifications (rather than requests for change) or are also sent
|
||||
* via the keyboard controller so should not be sent again.
|
||||
*/
|
||||
|
||||
static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
|
||||
static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
|
||||
{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
|
||||
|
||||
{ KE_KEY, 0xe045, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
|
||||
|
||||
/* These also contain the brightness level at offset 6 */
|
||||
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
|
||||
/* Key code is followed by brightness level */
|
||||
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
|
||||
|
||||
/* Battery health status button */
|
||||
{ KE_KEY, 0xe007, { KEY_BATTERY } },
|
||||
{ KE_KEY, 0xe007, { KEY_BATTERY } },
|
||||
|
||||
/* Radio devices state change */
|
||||
/* Radio devices state change, key code is followed by other values */
|
||||
{ KE_IGNORE, 0xe008, { KEY_RFKILL } },
|
||||
|
||||
/* The next device is at offset 6, the active devices are at
|
||||
offset 8 and the attached devices at offset 10 */
|
||||
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
|
||||
|
||||
/* Key code is followed by: next, active and attached devices */
|
||||
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
|
||||
|
||||
/* Key code is followed by keyboard illumination level */
|
||||
{ KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
|
||||
|
||||
/* BIOS error detected */
|
||||
{ KE_IGNORE, 0xe00d, { KEY_RESERVED } },
|
||||
|
||||
/* Unknown, defined in ACPI DSDT */
|
||||
/* { KE_IGNORE, 0xe00e, { KEY_RESERVED } }, */
|
||||
|
||||
/* Wifi Catcher */
|
||||
{ KE_KEY, 0xe011, {KEY_PROG2 } },
|
||||
{ KE_KEY, 0xe011, { KEY_PROG2 } },
|
||||
|
||||
/* Ambient light sensor toggle */
|
||||
{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
|
||||
|
||||
{ KE_IGNORE, 0xe020, { KEY_MUTE } },
|
||||
|
||||
/* Unknown, defined in ACPI DSDT */
|
||||
/* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */
|
||||
|
||||
/* Untested, Dell Instant Launch key on Inspiron 7520 */
|
||||
/* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */
|
||||
|
||||
/* Dell Instant Launch key */
|
||||
{ KE_KEY, 0xe025, { KEY_PROG4 } },
|
||||
{ KE_KEY, 0xe029, { KEY_PROG4 } },
|
||||
{ KE_KEY, 0xe025, { KEY_PROG4 } },
|
||||
|
||||
/* Audio panel key */
|
||||
{ KE_IGNORE, 0xe026, { KEY_RESERVED } },
|
||||
|
||||
/* LCD Display On/Off Control key */
|
||||
{ KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } },
|
||||
|
||||
/* Untested, Multimedia key on Dell Vostro 3560 */
|
||||
/* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */
|
||||
|
||||
/* Dell Instant Launch key */
|
||||
{ KE_KEY, 0xe029, { KEY_PROG4 } },
|
||||
|
||||
/* Untested, Windows Mobility Center button on Inspiron 7520 */
|
||||
/* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */
|
||||
|
||||
/* Unknown, defined in ACPI DSDT */
|
||||
/* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */
|
||||
|
||||
/* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */
|
||||
/* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */
|
||||
|
||||
{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
|
||||
{ KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
|
||||
{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
|
||||
{ KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
|
||||
{ KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
|
||||
|
||||
/* NIC Link is Up */
|
||||
{ KE_IGNORE, 0xe043, { KEY_RESERVED } },
|
||||
|
||||
/* NIC Link is Down */
|
||||
{ KE_IGNORE, 0xe044, { KEY_RESERVED } },
|
||||
|
||||
/*
|
||||
* This entry is very suspicious!
|
||||
* Originally Matthew Garrett created this dell-wmi driver specially for
|
||||
* "button with a picture of a battery" which has event code 0xe045.
|
||||
* Later Mario Limonciello from Dell told us that event code 0xe045 is
|
||||
* reported by Num Lock and should be ignored because key is send also
|
||||
* by keyboard controller.
|
||||
* So for now we will ignore this event to prevent potential double
|
||||
* Num Lock key press.
|
||||
*/
|
||||
{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
|
||||
|
||||
/* Scroll lock and also going to tablet mode on portable devices */
|
||||
{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
|
||||
|
||||
/* Untested, going from tablet mode on portable devices */
|
||||
/* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */
|
||||
|
||||
/* Dell Support Center key */
|
||||
{ KE_IGNORE, 0xe06e, { KEY_RESERVED } },
|
||||
|
||||
{ KE_IGNORE, 0xe0f7, { KEY_MUTE } },
|
||||
{ KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
|
||||
{ KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static bool dell_new_hk_type;
|
||||
|
||||
struct dell_bios_keymap_entry {
|
||||
u16 scancode;
|
||||
u16 keycode;
|
||||
|
@ -153,6 +202,7 @@ struct dell_bios_hotkey_table {
|
|||
|
||||
struct dell_dmi_results {
|
||||
int err;
|
||||
int keymap_size;
|
||||
struct key_entry *keymap;
|
||||
};
|
||||
|
||||
|
@ -201,10 +251,12 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Keymap for WMI events of type 0x0010
|
||||
*
|
||||
* These are applied if the 0xB2 DMI hotkey table is present and doesn't
|
||||
* override them.
|
||||
*/
|
||||
static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
|
||||
static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
|
||||
/* Fn-lock */
|
||||
{ KE_IGNORE, 0x151, { KEY_RESERVED } },
|
||||
|
||||
|
@ -224,21 +276,39 @@ static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
|
|||
{ KE_IGNORE, 0x155, { KEY_RESERVED } },
|
||||
};
|
||||
|
||||
/*
|
||||
* Keymap for WMI events of type 0x0011
|
||||
*/
|
||||
static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
|
||||
/* Battery unplugged */
|
||||
{ KE_IGNORE, 0xfff0, { KEY_RESERVED } },
|
||||
|
||||
/* Battery inserted */
|
||||
{ KE_IGNORE, 0xfff1, { KEY_RESERVED } },
|
||||
|
||||
/* Keyboard backlight level changed */
|
||||
{ KE_IGNORE, 0x01e1, { KEY_RESERVED } },
|
||||
{ KE_IGNORE, 0x02ea, { KEY_RESERVED } },
|
||||
{ KE_IGNORE, 0x02eb, { KEY_RESERVED } },
|
||||
{ KE_IGNORE, 0x02ec, { KEY_RESERVED } },
|
||||
{ KE_IGNORE, 0x02f6, { KEY_RESERVED } },
|
||||
};
|
||||
|
||||
static struct input_dev *dell_wmi_input_dev;
|
||||
|
||||
static void dell_wmi_process_key(int reported_key)
|
||||
static void dell_wmi_process_key(int type, int code)
|
||||
{
|
||||
const struct key_entry *key;
|
||||
|
||||
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
|
||||
reported_key);
|
||||
(type << 16) | code);
|
||||
if (!key) {
|
||||
pr_info("Unknown key with scancode 0x%x pressed\n",
|
||||
reported_key);
|
||||
pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
|
||||
type, code);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug("Key %x pressed\n", reported_key);
|
||||
pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code);
|
||||
|
||||
/* Don't report brightness notifications that will also come via ACPI */
|
||||
if ((key->keycode == KEY_BRIGHTNESSUP ||
|
||||
|
@ -246,7 +316,7 @@ static void dell_wmi_process_key(int reported_key)
|
|||
acpi_video_handles_brightness_key_presses())
|
||||
return;
|
||||
|
||||
if (reported_key == 0xe025 && !wmi_requires_smbios_request)
|
||||
if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
|
||||
return;
|
||||
|
||||
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
|
||||
|
@ -284,18 +354,6 @@ static void dell_wmi_notify(u32 value, void *context)
|
|||
|
||||
buffer_entry = (u16 *)obj->buffer.pointer;
|
||||
buffer_size = obj->buffer.length/2;
|
||||
|
||||
if (!dell_new_hk_type) {
|
||||
if (buffer_size >= 3 && buffer_entry[1] == 0x0)
|
||||
dell_wmi_process_key(buffer_entry[2]);
|
||||
else if (buffer_size >= 2)
|
||||
dell_wmi_process_key(buffer_entry[1]);
|
||||
else
|
||||
pr_info("Received unknown WMI event\n");
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_end = buffer_entry + buffer_size;
|
||||
|
||||
/*
|
||||
|
@ -330,62 +388,18 @@ static void dell_wmi_notify(u32 value, void *context)
|
|||
pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
|
||||
|
||||
switch (buffer_entry[1]) {
|
||||
case 0x00:
|
||||
for (i = 2; i < len; ++i) {
|
||||
switch (buffer_entry[i]) {
|
||||
case 0xe043:
|
||||
/* NIC Link is Up */
|
||||
pr_debug("NIC Link is Up\n");
|
||||
break;
|
||||
case 0xe044:
|
||||
/* NIC Link is Down */
|
||||
pr_debug("NIC Link is Down\n");
|
||||
break;
|
||||
case 0xe045:
|
||||
/* Unknown event but defined in DSDT */
|
||||
default:
|
||||
/* Unknown event */
|
||||
pr_info("Unknown WMI event type 0x00: "
|
||||
"0x%x\n", (int)buffer_entry[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 0x0000: /* One key pressed or event occurred */
|
||||
if (len > 2)
|
||||
dell_wmi_process_key(0x0000, buffer_entry[2]);
|
||||
/* Other entries could contain additional information */
|
||||
break;
|
||||
case 0x10:
|
||||
/* Keys pressed */
|
||||
case 0x0010: /* Sequence of keys pressed */
|
||||
case 0x0011: /* Sequence of events occurred */
|
||||
for (i = 2; i < len; ++i)
|
||||
dell_wmi_process_key(buffer_entry[i]);
|
||||
dell_wmi_process_key(buffer_entry[1],
|
||||
buffer_entry[i]);
|
||||
break;
|
||||
case 0x11:
|
||||
for (i = 2; i < len; ++i) {
|
||||
switch (buffer_entry[i]) {
|
||||
case 0xfff0:
|
||||
/* Battery unplugged */
|
||||
pr_debug("Battery unplugged\n");
|
||||
break;
|
||||
case 0xfff1:
|
||||
/* Battery inserted */
|
||||
pr_debug("Battery inserted\n");
|
||||
break;
|
||||
case 0x01e1:
|
||||
case 0x02ea:
|
||||
case 0x02eb:
|
||||
case 0x02ec:
|
||||
case 0x02f6:
|
||||
/* Keyboard backlight level changed */
|
||||
pr_debug("Keyboard backlight level "
|
||||
"changed\n");
|
||||
break;
|
||||
default:
|
||||
/* Unknown event */
|
||||
pr_info("Unknown WMI event type 0x11: "
|
||||
"0x%x\n", (int)buffer_entry[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Unknown event */
|
||||
default: /* Unknown event */
|
||||
pr_info("Unknown WMI event type 0x%x\n",
|
||||
(int)buffer_entry[1]);
|
||||
break;
|
||||
|
@ -410,7 +424,6 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
|
|||
}
|
||||
|
||||
static void __init handle_dmi_entry(const struct dmi_header *dm,
|
||||
|
||||
void *opaque)
|
||||
|
||||
{
|
||||
|
@ -418,7 +431,6 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
|
|||
struct dell_bios_hotkey_table *table;
|
||||
int hotkey_num, i, pos = 0;
|
||||
struct key_entry *keymap;
|
||||
int num_bios_keys;
|
||||
|
||||
if (results->err || results->keymap)
|
||||
return; /* We already found the hotkey table. */
|
||||
|
@ -442,8 +454,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
|
|||
return;
|
||||
}
|
||||
|
||||
keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
|
||||
sizeof(struct key_entry), GFP_KERNEL);
|
||||
keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL);
|
||||
if (!keymap) {
|
||||
results->err = -ENOMEM;
|
||||
return;
|
||||
|
@ -480,31 +491,15 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
|
|||
pos++;
|
||||
}
|
||||
|
||||
num_bios_keys = pos;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
|
||||
const struct key_entry *entry = &dell_wmi_extra_keymap[i];
|
||||
|
||||
/*
|
||||
* Check if we've already found this scancode. This takes
|
||||
* quadratic time, but it doesn't matter unless the list
|
||||
* of extra keys gets very long.
|
||||
*/
|
||||
if (!have_scancode(entry->code, keymap, num_bios_keys)) {
|
||||
keymap[pos] = *entry;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
keymap[pos].type = KE_END;
|
||||
|
||||
results->keymap = keymap;
|
||||
results->keymap_size = pos;
|
||||
}
|
||||
|
||||
static int __init dell_wmi_input_setup(void)
|
||||
{
|
||||
struct dell_dmi_results dmi_results = {};
|
||||
int err;
|
||||
struct key_entry *keymap;
|
||||
int err, i, pos = 0;
|
||||
|
||||
dell_wmi_input_dev = input_allocate_device();
|
||||
if (!dell_wmi_input_dev)
|
||||
|
@ -528,21 +523,71 @@ static int __init dell_wmi_input_setup(void)
|
|||
goto err_free_dev;
|
||||
}
|
||||
|
||||
if (dmi_results.keymap) {
|
||||
dell_new_hk_type = true;
|
||||
keymap = kcalloc(dmi_results.keymap_size +
|
||||
ARRAY_SIZE(dell_wmi_keymap_type_0000) +
|
||||
ARRAY_SIZE(dell_wmi_keymap_type_0010) +
|
||||
ARRAY_SIZE(dell_wmi_keymap_type_0011) +
|
||||
1,
|
||||
sizeof(struct key_entry), GFP_KERNEL);
|
||||
if (!keymap) {
|
||||
kfree(dmi_results.keymap);
|
||||
err = -ENOMEM;
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev,
|
||||
dmi_results.keymap, NULL);
|
||||
/* Append table with events of type 0x0010 which comes from DMI */
|
||||
for (i = 0; i < dmi_results.keymap_size; i++) {
|
||||
keymap[pos] = dmi_results.keymap[i];
|
||||
keymap[pos].code |= (0x0010 << 16);
|
||||
pos++;
|
||||
}
|
||||
|
||||
kfree(dmi_results.keymap);
|
||||
|
||||
/* Append table with extra events of type 0x0010 which are not in DMI */
|
||||
for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) {
|
||||
const struct key_entry *entry = &dell_wmi_keymap_type_0010[i];
|
||||
|
||||
/*
|
||||
* Sparse keymap library makes a copy of keymap so we
|
||||
* don't need the original one that was allocated.
|
||||
* Check if we've already found this scancode. This takes
|
||||
* quadratic time, but it doesn't matter unless the list
|
||||
* of extra keys gets very long.
|
||||
*/
|
||||
kfree(dmi_results.keymap);
|
||||
} else {
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev,
|
||||
dell_wmi_legacy_keymap, NULL);
|
||||
if (dmi_results.keymap_size &&
|
||||
have_scancode(entry->code | (0x0010 << 16),
|
||||
keymap, dmi_results.keymap_size)
|
||||
)
|
||||
continue;
|
||||
|
||||
keymap[pos] = *entry;
|
||||
keymap[pos].code |= (0x0010 << 16);
|
||||
pos++;
|
||||
}
|
||||
|
||||
/* Append table with events of type 0x0011 */
|
||||
for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) {
|
||||
keymap[pos] = dell_wmi_keymap_type_0011[i];
|
||||
keymap[pos].code |= (0x0011 << 16);
|
||||
pos++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now append also table with "legacy" events of type 0x0000. Some of
|
||||
* them are reported also on laptops which have scancodes in DMI.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) {
|
||||
keymap[pos] = dell_wmi_keymap_type_0000[i];
|
||||
pos++;
|
||||
}
|
||||
|
||||
keymap[pos].type = KE_END;
|
||||
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
|
||||
/*
|
||||
* Sparse keymap library makes a copy of keymap so we don't need the
|
||||
* original one that was allocated.
|
||||
*/
|
||||
kfree(keymap);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
|
|
|
@ -88,9 +88,6 @@
|
|||
|
||||
#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
|
||||
|
||||
#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
|
||||
#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
|
||||
|
||||
/* FUNC interface - command values */
|
||||
#define FUNC_RFKILL 0x1000
|
||||
#define FUNC_LEDS 0x1001
|
||||
|
@ -108,6 +105,8 @@
|
|||
#define LOGOLAMP_POWERON 0x2000
|
||||
#define LOGOLAMP_ALWAYS 0x4000
|
||||
#define RADIO_LED_ON 0x20
|
||||
#define ECO_LED 0x10000
|
||||
#define ECO_LED_ON 0x80000
|
||||
#endif
|
||||
|
||||
/* Hotkey details */
|
||||
|
@ -121,13 +120,6 @@
|
|||
#define RINGBUFFERSIZE 40
|
||||
|
||||
/* Debugging */
|
||||
#define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": "
|
||||
#define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG
|
||||
#define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG
|
||||
#define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG
|
||||
#define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG
|
||||
|
||||
#define FUJLAPTOP_DBG_ALL 0xffff
|
||||
#define FUJLAPTOP_DBG_ERROR 0x0001
|
||||
#define FUJLAPTOP_DBG_WARN 0x0002
|
||||
#define FUJLAPTOP_DBG_INFO 0x0004
|
||||
|
@ -136,7 +128,7 @@
|
|||
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
|
||||
#define vdbg_printk(a_dbg_level, format, arg...) \
|
||||
do { if (dbg_level & a_dbg_level) \
|
||||
printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
|
||||
printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
|
||||
} while (0)
|
||||
#else
|
||||
#define vdbg_printk(a_dbg_level, format, arg...) \
|
||||
|
@ -176,6 +168,7 @@ struct fujitsu_hotkey_t {
|
|||
int logolamp_registered;
|
||||
int kblamps_registered;
|
||||
int radio_led_registered;
|
||||
int eco_led_registered;
|
||||
};
|
||||
|
||||
static struct fujitsu_hotkey_t *fujitsu_hotkey;
|
||||
|
@ -212,6 +205,16 @@ static struct led_classdev radio_led = {
|
|||
.brightness_get = radio_led_get,
|
||||
.brightness_set = radio_led_set
|
||||
};
|
||||
|
||||
static enum led_brightness eco_led_get(struct led_classdev *cdev);
|
||||
static void eco_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness);
|
||||
|
||||
static struct led_classdev eco_led = {
|
||||
.name = "fujitsu::eco_led",
|
||||
.brightness_get = eco_led_get,
|
||||
.brightness_set = eco_led_set
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
|
||||
|
@ -296,6 +299,18 @@ static void radio_led_set(struct led_classdev *cdev,
|
|||
call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
|
||||
}
|
||||
|
||||
static void eco_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
int curr;
|
||||
|
||||
curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
|
||||
if (brightness >= LED_FULL)
|
||||
call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON);
|
||||
else
|
||||
call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON);
|
||||
}
|
||||
|
||||
static enum led_brightness logolamp_get(struct led_classdev *cdev)
|
||||
{
|
||||
enum led_brightness brightness = LED_OFF;
|
||||
|
@ -330,6 +345,16 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev)
|
|||
|
||||
return brightness;
|
||||
}
|
||||
|
||||
static enum led_brightness eco_led_get(struct led_classdev *cdev)
|
||||
{
|
||||
enum led_brightness brightness = LED_OFF;
|
||||
|
||||
if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
|
||||
brightness = LED_FULL;
|
||||
|
||||
return brightness;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Hardware access for LCD brightness control */
|
||||
|
@ -856,6 +881,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
|
|||
set_bit(fujitsu->keycode3, input->keybit);
|
||||
set_bit(fujitsu->keycode4, input->keybit);
|
||||
set_bit(fujitsu->keycode5, input->keybit);
|
||||
set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit);
|
||||
set_bit(KEY_UNKNOWN, input->keybit);
|
||||
|
||||
error = input_register_device(input);
|
||||
|
@ -943,6 +969,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
|
|||
result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Support for eco led is not always signaled in bit corresponding
|
||||
* to the bit used to control the led. According to the DSDT table,
|
||||
* bit 14 seems to indicate presence of said led as well.
|
||||
* Confirm by testing the status.
|
||||
*/
|
||||
if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
|
||||
(call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
|
||||
result = led_classdev_register(&fujitsu->pf_device->dev,
|
||||
&eco_led);
|
||||
if (result == 0) {
|
||||
fujitsu_hotkey->eco_led_registered = 1;
|
||||
} else {
|
||||
pr_err("Could not register LED handler for eco LED, error %i\n",
|
||||
result);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
|
@ -972,6 +1015,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
|
|||
|
||||
if (fujitsu_hotkey->radio_led_registered)
|
||||
led_classdev_unregister(&radio_led);
|
||||
|
||||
if (fujitsu_hotkey->eco_led_registered)
|
||||
led_classdev_unregister(&eco_led);
|
||||
#endif
|
||||
|
||||
input_unregister_device(input);
|
||||
|
@ -1060,6 +1106,19 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
|
|||
}
|
||||
}
|
||||
|
||||
/* On some models (first seen on the Skylake-based Lifebook
|
||||
* E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
|
||||
* handled in software; its state is queried using FUNC_RFKILL
|
||||
*/
|
||||
if ((fujitsu_hotkey->rfkill_supported & BIT(26)) &&
|
||||
(call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) {
|
||||
keycode = KEY_TOUCHPAD_TOGGLE;
|
||||
input_report_key(input, keycode, 1);
|
||||
input_sync(input);
|
||||
input_report_key(input, keycode, 0);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
keycode = KEY_UNKNOWN;
|
||||
|
|
|
@ -718,6 +718,11 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, &wireless,
|
||||
sizeof(wireless), 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (wireless & 0x1) {
|
||||
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
|
||||
RFKILL_TYPE_WLAN,
|
||||
|
@ -882,7 +887,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
|||
wwan_rfkill = NULL;
|
||||
rfkill2_count = 0;
|
||||
|
||||
if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
|
||||
if (hp_wmi_rfkill_setup(device))
|
||||
hp_wmi_rfkill2_setup(device);
|
||||
|
||||
err = device_create_file(&device->dev, &dev_attr_display);
|
||||
|
|
|
@ -122,8 +122,8 @@ static int intel_hid_input_setup(struct platform_device *device)
|
|||
return 0;
|
||||
|
||||
err_free_device:
|
||||
input_free_device(priv->input_dev);
|
||||
return ret;
|
||||
input_free_device(priv->input_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_hid_input_destroy(struct platform_device *device)
|
||||
|
@ -224,7 +224,6 @@ static int intel_hid_remove(struct platform_device *device)
|
|||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
intel_hid_input_destroy(device);
|
||||
intel_hid_set_enable(&device->dev, 0);
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
|
||||
/*
|
||||
* Even if we failed to shut off the event stream, we can still
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Intel Virtual Button driver for Windows 8.1+
|
||||
*
|
||||
* Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com>
|
||||
* Copyright (C) 2016 Alex Hung <alex.hung@canonical.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("AceLan Kao");
|
||||
|
||||
static const struct acpi_device_id intel_vbtn_ids[] = {
|
||||
{"INT33D6", 0},
|
||||
{"", 0},
|
||||
};
|
||||
|
||||
/* In theory, these are HID usages. */
|
||||
static const struct key_entry intel_vbtn_keymap[] = {
|
||||
{ KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
|
||||
{ KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
|
||||
{ KE_END },
|
||||
};
|
||||
|
||||
struct intel_vbtn_priv {
|
||||
struct input_dev *input_dev;
|
||||
};
|
||||
|
||||
static int intel_vbtn_input_setup(struct platform_device *device)
|
||||
{
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
int ret;
|
||||
|
||||
priv->input_dev = input_allocate_device();
|
||||
if (!priv->input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
|
||||
if (ret)
|
||||
goto err_free_device;
|
||||
|
||||
priv->input_dev->dev.parent = &device->dev;
|
||||
priv->input_dev->name = "Intel Virtual Button driver";
|
||||
priv->input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
ret = input_register_device(priv->input_dev);
|
||||
if (ret)
|
||||
goto err_free_device;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_device:
|
||||
input_free_device(priv->input_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_vbtn_input_destroy(struct platform_device *device)
|
||||
{
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
|
||||
input_unregister_device(priv->input_dev);
|
||||
}
|
||||
|
||||
static void notify_handler(acpi_handle handle, u32 event, void *context)
|
||||
{
|
||||
struct platform_device *device = context;
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
|
||||
if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
|
||||
dev_info(&device->dev, "unknown event index 0x%x\n",
|
||||
event);
|
||||
}
|
||||
|
||||
static int intel_vbtn_probe(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
struct intel_vbtn_priv *priv;
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&device->dev, priv);
|
||||
|
||||
err = intel_vbtn_input_setup(device);
|
||||
if (err) {
|
||||
pr_err("Failed to setup Intel Virtual Button\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
status = acpi_install_notify_handler(handle,
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
notify_handler,
|
||||
device);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err = -EBUSY;
|
||||
goto err_remove_input;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_input:
|
||||
intel_vbtn_input_destroy(device);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int intel_vbtn_remove(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
|
||||
intel_vbtn_input_destroy(device);
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
|
||||
/*
|
||||
* Even if we failed to shut off the event stream, we can still
|
||||
* safely detach from the device.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver intel_vbtn_pl_driver = {
|
||||
.driver = {
|
||||
.name = "intel-vbtn",
|
||||
.acpi_match_table = intel_vbtn_ids,
|
||||
},
|
||||
.probe = intel_vbtn_probe,
|
||||
.remove = intel_vbtn_remove,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids);
|
||||
|
||||
static acpi_status __init
|
||||
check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
{
|
||||
const struct acpi_device_id *ids = context;
|
||||
struct acpi_device *dev;
|
||||
|
||||
if (acpi_bus_get_device(handle, &dev) != 0)
|
||||
return AE_OK;
|
||||
|
||||
if (acpi_match_device_ids(dev, ids) == 0)
|
||||
if (acpi_create_platform_device(dev))
|
||||
dev_info(&dev->dev,
|
||||
"intel-vbtn: created platform device\n");
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int __init intel_vbtn_init(void)
|
||||
{
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, check_acpi_dev, NULL,
|
||||
(void *)intel_vbtn_ids, NULL);
|
||||
|
||||
return platform_driver_register(&intel_vbtn_pl_driver);
|
||||
}
|
||||
module_init(intel_vbtn_init);
|
||||
|
||||
static void __exit intel_vbtn_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&intel_vbtn_pl_driver);
|
||||
}
|
||||
module_exit(intel_vbtn_exit);
|
|
@ -23,7 +23,6 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
|
@ -78,30 +77,18 @@ int intel_pmc_slp_s0_counter_read(u32 *data)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
static int pmc_core_dev_state_show(struct seq_file *s, void *unused)
|
||||
static int pmc_core_dev_state_get(void *data, u64 *val)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
u32 counter_val;
|
||||
struct pmc_dev *pmcdev = data;
|
||||
u32 value;
|
||||
|
||||
counter_val = pmc_core_reg_read(pmcdev,
|
||||
SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
|
||||
seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val));
|
||||
value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
|
||||
*val = pmc_core_adjust_slp_s0_step(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_core_dev_state_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, pmc_core_dev_state_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations pmc_core_dev_state_ops = {
|
||||
.open = pmc_core_dev_state_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
|
||||
|
||||
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
|
||||
{
|
||||
|
@ -113,12 +100,12 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
|||
struct dentry *dir, *file;
|
||||
|
||||
dir = debugfs_create_dir("pmc_core", NULL);
|
||||
if (!dir)
|
||||
if (IS_ERR_OR_NULL(dir))
|
||||
return -ENOMEM;
|
||||
|
||||
pmcdev->dbgfs_dir = dir;
|
||||
file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
|
||||
dir, pmcdev, &pmc_core_dev_state_ops);
|
||||
dir, pmcdev, &pmc_core_dev_state);
|
||||
|
||||
if (!file) {
|
||||
pmc_core_dbgfs_unregister(pmcdev);
|
||||
|
@ -127,16 +114,6 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
static const struct x86_cpu_id intel_pmc_core_ids[] = {
|
||||
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT,
|
||||
|
@ -183,10 +160,8 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
}
|
||||
|
||||
err = pmc_core_dbgfs_register(pmcdev);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "PMC Core: debugfs register failed.\n");
|
||||
return err;
|
||||
}
|
||||
if (err < 0)
|
||||
dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n");
|
||||
|
||||
pmc.has_slp_s0_res = true;
|
||||
return 0;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
/* Sunrise Point Power Management Controller PCI Device ID */
|
||||
#define SPT_PMC_PCI_DEVICE_ID 0x9d21
|
||||
|
||||
#define SPT_PMC_BASE_ADDR_OFFSET 0x48
|
||||
#define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c
|
||||
#define SPT_PMC_MMIO_REG_LEN 0x100
|
||||
|
@ -42,9 +43,7 @@
|
|||
struct pmc_dev {
|
||||
u32 base_addr;
|
||||
void __iomem *regbase;
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
struct dentry *dbgfs_dir;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
bool has_slp_s0_res;
|
||||
};
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
#define TELEM_DEBUGFS_CPU(model, data) \
|
||||
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data}
|
||||
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data}
|
||||
|
||||
#define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \
|
||||
if (evtlog[index].telem_evtid == (EVTID)) { \
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
#define TELEM_SET_VERBOSITY_BITS(x, y) ((x) |= ((y) << 27))
|
||||
|
||||
#define TELEM_CPU(model, data) \
|
||||
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&data }
|
||||
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data }
|
||||
|
||||
enum telemetry_action {
|
||||
TELEM_UPDATE = 0,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (C) 2002-2004 John Belmonte
|
||||
* Copyright (C) 2008 Philip Langdale
|
||||
* Copyright (C) 2010 Pierre Ducroquet
|
||||
* Copyright (C) 2014-2015 Azael Avalos
|
||||
* Copyright (C) 2014-2016 Azael Avalos
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define TOSHIBA_ACPI_VERSION "0.23"
|
||||
#define TOSHIBA_ACPI_VERSION "0.24"
|
||||
#define PROC_INTERFACE_VERSION 1
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -53,6 +53,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/toshiba.h>
|
||||
#include <acpi/video.h>
|
||||
|
||||
|
@ -134,6 +135,7 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
/* Field definitions */
|
||||
#define HCI_ACCEL_MASK 0x7fff
|
||||
#define HCI_ACCEL_DIRECTION_MASK 0x8000
|
||||
#define HCI_HOTKEY_DISABLE 0x0b
|
||||
#define HCI_HOTKEY_ENABLE 0x09
|
||||
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
|
||||
|
@ -178,6 +180,7 @@ struct toshiba_acpi_dev {
|
|||
struct led_classdev eco_led;
|
||||
struct miscdevice miscdev;
|
||||
struct rfkill *wwan_rfk;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
int force_fan;
|
||||
int last_key_event;
|
||||
|
@ -1958,28 +1961,6 @@ static ssize_t touchpad_show(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR_RW(touchpad);
|
||||
|
||||
static ssize_t position_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
|
||||
u32 xyval, zval, tmp;
|
||||
u16 x, y, z;
|
||||
int ret;
|
||||
|
||||
xyval = zval = 0;
|
||||
ret = toshiba_accelerometer_get(toshiba, &xyval, &zval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
x = xyval & HCI_ACCEL_MASK;
|
||||
tmp = xyval >> HCI_MISC_SHIFT;
|
||||
y = tmp & HCI_ACCEL_MASK;
|
||||
z = zval & HCI_ACCEL_MASK;
|
||||
|
||||
return sprintf(buf, "%d %d %d\n", x, y, z);
|
||||
}
|
||||
static DEVICE_ATTR_RO(position);
|
||||
|
||||
static ssize_t usb_sleep_charge_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -2350,7 +2331,6 @@ static struct attribute *toshiba_attributes[] = {
|
|||
&dev_attr_available_kbd_modes.attr,
|
||||
&dev_attr_kbd_backlight_timeout.attr,
|
||||
&dev_attr_touchpad.attr,
|
||||
&dev_attr_position.attr,
|
||||
&dev_attr_usb_sleep_charge.attr,
|
||||
&dev_attr_sleep_functions_on_battery.attr,
|
||||
&dev_attr_usb_rapid_charge.attr,
|
||||
|
@ -2377,8 +2357,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
|
|||
exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
|
||||
else if (attr == &dev_attr_touchpad.attr)
|
||||
exists = (drv->touchpad_supported) ? true : false;
|
||||
else if (attr == &dev_attr_position.attr)
|
||||
exists = (drv->accelerometer_supported) ? true : false;
|
||||
else if (attr == &dev_attr_usb_sleep_charge.attr)
|
||||
exists = (drv->usb_sleep_charge_supported) ? true : false;
|
||||
else if (attr == &dev_attr_sleep_functions_on_battery.attr)
|
||||
|
@ -2419,6 +2397,81 @@ static void toshiba_acpi_kbd_bl_work(struct work_struct *work)
|
|||
0x92, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* IIO device
|
||||
*/
|
||||
|
||||
enum toshiba_iio_accel_chan {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z
|
||||
};
|
||||
|
||||
static int toshiba_iio_accel_get_axis(enum toshiba_iio_accel_chan chan)
|
||||
{
|
||||
u32 xyval, zval;
|
||||
int ret;
|
||||
|
||||
ret = toshiba_accelerometer_get(toshiba_acpi, &xyval, &zval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (chan) {
|
||||
case AXIS_X:
|
||||
return xyval & HCI_ACCEL_DIRECTION_MASK ?
|
||||
-(xyval & HCI_ACCEL_MASK) : xyval & HCI_ACCEL_MASK;
|
||||
case AXIS_Y:
|
||||
return (xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_DIRECTION_MASK ?
|
||||
-((xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK) :
|
||||
(xyval >> HCI_MISC_SHIFT) & HCI_ACCEL_MASK;
|
||||
case AXIS_Z:
|
||||
return zval & HCI_ACCEL_DIRECTION_MASK ?
|
||||
-(zval & HCI_ACCEL_MASK) : zval & HCI_ACCEL_MASK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int toshiba_iio_accel_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = toshiba_iio_accel_get_axis(chan->channel);
|
||||
if (ret == -EIO || ret == -ENODEV)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define TOSHIBA_IIO_ACCEL_CHANNEL(axis, chan) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel = chan, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec toshiba_iio_accel_channels[] = {
|
||||
TOSHIBA_IIO_ACCEL_CHANNEL(X, AXIS_X),
|
||||
TOSHIBA_IIO_ACCEL_CHANNEL(Y, AXIS_Y),
|
||||
TOSHIBA_IIO_ACCEL_CHANNEL(Z, AXIS_Z),
|
||||
};
|
||||
|
||||
static const struct iio_info toshiba_iio_accel_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &toshiba_iio_accel_read_raw,
|
||||
};
|
||||
|
||||
/*
|
||||
* Misc device
|
||||
*/
|
||||
|
@ -2904,6 +2957,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
|
|||
|
||||
remove_toshiba_proc_entries(dev);
|
||||
|
||||
if (dev->accelerometer_supported && dev->indio_dev) {
|
||||
iio_device_unregister(dev->indio_dev);
|
||||
iio_device_free(dev->indio_dev);
|
||||
}
|
||||
|
||||
if (dev->sysfs_created)
|
||||
sysfs_remove_group(&dev->acpi_dev->dev.kobj,
|
||||
&toshiba_attr_group);
|
||||
|
@ -3051,6 +3109,30 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
|||
dev->touchpad_supported = !ret;
|
||||
|
||||
toshiba_accelerometer_available(dev);
|
||||
if (dev->accelerometer_supported) {
|
||||
dev->indio_dev = iio_device_alloc(sizeof(*dev));
|
||||
if (!dev->indio_dev) {
|
||||
pr_err("Unable to allocate iio device\n");
|
||||
goto iio_error;
|
||||
}
|
||||
|
||||
pr_info("Registering Toshiba accelerometer iio device\n");
|
||||
|
||||
dev->indio_dev->info = &toshiba_iio_accel_info;
|
||||
dev->indio_dev->name = "Toshiba accelerometer";
|
||||
dev->indio_dev->dev.parent = &acpi_dev->dev;
|
||||
dev->indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
dev->indio_dev->channels = toshiba_iio_accel_channels;
|
||||
dev->indio_dev->num_channels =
|
||||
ARRAY_SIZE(toshiba_iio_accel_channels);
|
||||
|
||||
ret = iio_device_register(dev->indio_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("Unable to register iio device\n");
|
||||
iio_device_free(dev->indio_dev);
|
||||
}
|
||||
}
|
||||
iio_error:
|
||||
|
||||
toshiba_usb_sleep_charge_available(dev);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче