Merge tag 'platform-drivers-x86-v3.19-1' of git://git.infradead.org/users/dvhart...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 19 Dec 2014 04:24:55 +0000 (20:24 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 19 Dec 2014 04:24:55 +0000 (20:24 -0800)
Pull x86 platform driver update from Darren Hart:
 - thinkpad-acpi: Switch to software mute, cleanups
 - acerhdf: Bang-bang thermal governor, new models, cleanups
 - dell-laptop: New keyboard backlight support and documentation
 - toshiba_acpi: Keyboard backlight updates, hotkey handling
 - dell-wmi: Keypress filtering, WMI event processing
 - eeepc-laptop: Multiple cleanups, improved error handling, documentation
 - hp_wireless: Inform the user if hp_wireless_input_setup()/add() fails
 - misc: Code cleanups, quirks, various new IDs

* tag 'platform-drivers-x86-v3.19-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (33 commits)
  platform/x86/acerhdf: Still depends on THERMAL
  Documentation: Add entry for dell-laptop sysfs interface
  acpi: Remove _OSI(Linux) for ThinkPads
  thinkpad-acpi: Try to use full software mute control
  acerhdf: minor clean up
  acerhdf: added critical trip point
  acerhdf: Use bang-bang thermal governor
  acerhdf: Adding support for new models
  acerhdf: Adding support for "manual mode"
  dell-smo8800: Add more ACPI ids and change description of driver
  platform: x86: dell-laptop: Add support for keyboard backlight
  toshiba_acpi: Add keyboard backlight mode change event
  toshiba_acpi: Change notify funtion to handle more events
  toshiba_acpi: Move hotkey enabling code to its own function
  dell-wmi: Don't report keypresses on keybord illumination change
  dell-wmi: Don't report keypresses for radio state changes
  hp_wireless: Inform the user if hp_wireless_input_setup()/add() fails
  toshiba-acpi: Add missing ID (TOS6207)
  Sony-laptop: Deletion of an unnecessary check before the function call "pci_dev_put"
  platform: x86: Deletion of checks before backlight_device_unregister()
  ...

22 files changed:
Documentation/ABI/testing/sysfs-platform-dell-laptop [new file with mode: 0644]
drivers/acpi/blacklist.c
drivers/platform/x86/Kconfig
drivers/platform/x86/acerhdf.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-smo8800.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp-wireless.c
drivers/platform/x86/hp_accel.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_ips.c
drivers/platform/x86/intel_oaktrail.c
drivers/platform/x86/msi-laptop.c
drivers/platform/x86/msi-wmi.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/toshiba_acpi.c

diff --git a/Documentation/ABI/testing/sysfs-platform-dell-laptop b/Documentation/ABI/testing/sysfs-platform-dell-laptop
new file mode 100644 (file)
index 0000000..7969443
--- /dev/null
@@ -0,0 +1,60 @@
+What:          /sys/class/leds/dell::kbd_backlight/als_setting
+Date:          December 2014
+KernelVersion: 3.19
+Contact:       Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+               Pali Rohár <pali.rohar@gmail.com>
+Description:
+               This file allows to control the automatic keyboard
+               illumination mode on some systems that have an ambient
+               light sensor. Write 1 to this file to enable the auto
+               mode, 0 to disable it.
+
+What:          /sys/class/leds/dell::kbd_backlight/start_triggers
+Date:          December 2014
+KernelVersion: 3.19
+Contact:       Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+               Pali Rohár <pali.rohar@gmail.com>
+Description:
+               This file allows to control the input triggers that
+               turn on the keyboard backlight illumination that is
+               disabled because of inactivity.
+               Read the file to see the triggers available. The ones
+               enabled are preceded by '+', those disabled by '-'.
+
+               To enable a trigger, write its name preceded by '+' to
+               this file. To disable a trigger, write its name preceded
+               by '-' instead.
+
+               For example, to enable the keyboard as trigger run:
+                   echo +keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
+               To disable it:
+                   echo -keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
+
+               Note that not all the available triggers can be configured.
+
+What:          /sys/class/leds/dell::kbd_backlight/stop_timeout
+Date:          December 2014
+KernelVersion: 3.19
+Contact:       Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+               Pali Rohár <pali.rohar@gmail.com>
+Description:
+               This file allows to specify the interval after which the
+               keyboard illumination is disabled because of inactivity.
+               The timeouts are expressed in seconds, minutes, hours and
+               days, for which the symbols are 's', 'm', 'h' and 'd'
+               respectively.
+
+               To configure the timeout, write to this file a value along
+               with any the above units. If no unit is specified, the value
+               is assumed to be expressed in seconds.
+
+               For example, to set the timeout to 10 minutes run:
+                   echo 10m > /sys/class/leds/dell::kbd_backlight/stop_timeout
+
+               Note that when this file is read, the returned value might be
+               expressed in a different unit than the one used when the timeout
+               was set.
+
+               Also note that only some timeouts are supported and that
+               some systems might fall back to a specific timeout in case
+               an invalid timeout is written to this file.
index 7556e7c4a055cd0c2dad68fa11a3f8746e6ab16e..9b693d54c743edbca512fc7b37086a809de9ccd7 100644 (file)
@@ -304,60 +304,6 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
         * Linux ignores it, except for the machines enumerated below.
         */
 
-       /*
-        * Lenovo has a mix of systems OSI(Linux) situations
-        * and thus we can not wildcard the vendor.
-        *
-        * _OSI(Linux) helps sound
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
-        * T400, T500
-        * _OSI(Linux) has Linux specific hooks
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"),
-        * _OSI(Linux) is a NOP:
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"),
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"),
-        */
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad R61",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad T61",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad X61",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad T400",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T400"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad T500",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"),
-               },
-       },
        /*
         * Without this this EEEpc exports a non working WMI interface, with
         * this it exports a working "good old" eeepc_laptop interface, fixing
index a2eabe6ff9ada9b7bff3991a0116c8137a78b92d..638e797037da973f7756b416a47f37c3ae314025 100644 (file)
@@ -38,7 +38,8 @@ config ACER_WMI
 
 config ACERHDF
        tristate "Acer Aspire One temperature and fan driver"
-       depends on THERMAL && ACPI
+       depends on ACPI && THERMAL
+       select THERMAL_GOV_BANG_BANG
        ---help---
          This is a driver for Acer Aspire One netbooks. It allows to access
          the temperature sensor and to control the fan.
@@ -128,10 +129,10 @@ config DELL_WMI_AIO
          be called dell-wmi-aio.
 
 config DELL_SMO8800
-       tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"
+       tristate "Dell Latitude freefall driver (ACPI SMO88XX)"
        depends on ACPI
        ---help---
-         Say Y here if you want to support SMO8800/SMO8810 freefall device
+         Say Y here if you want to support SMO88XX freefall devices
          on Dell Latitude laptops.
 
          To compile this driver as a module, choose M here: the module will
index aaf37c5f12f97bd6e548a8cf687a88f745ed7d4f..594c918b553d382fa06519d83a51274894644693 100644 (file)
@@ -50,7 +50,7 @@
  */
 #undef START_IN_KERNEL_MODE
 
-#define DRV_VER "0.5.26"
+#define DRV_VER "0.7.0"
 
 /*
  * According to the Atom N270 datasheet,
@@ -119,116 +119,152 @@ struct fancmd {
        u8 cmd_auto;
 };
 
+struct manualcmd {
+       u8 mreg;
+       u8 moff;
+};
+
+/* default register and command to disable fan in manual mode */
+static const struct manualcmd mcmd = {
+       .mreg = 0x94,
+       .moff = 0xff,
+};
+
 /* BIOS settings */
-struct bios_settings_t {
+struct bios_settings {
        const char *vendor;
        const char *product;
        const char *version;
-       unsigned char fanreg;
-       unsigned char tempreg;
+       u8 fanreg;
+       u8 tempreg;
        struct fancmd cmd;
+       int mcmd_enable;
 };
 
 /* Register addresses and values for different BIOS versions */
-static const struct bios_settings_t bios_tbl[] = {
+static const struct bios_settings bios_tbl[] = {
        /* AOA110 */
-       {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
-       {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
-       {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
-       {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
-       {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+       {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00}, 0},
        /* AOA150 */
-       {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
-       {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+       {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00}, 0},
        /* LT1005u */
-       {"Acer", "LT-10Q", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+       {"Acer", "LT-10Q", "v0.3310", 0x55, 0x58, {0x20, 0x00}, 0},
        /* Acer 1410 */
-       {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0},
        /* Acer 1810xx */
-       {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0},
+       /* Acer 5755G */
+       {"Acer", "Aspire 5755G",  "V1.20",   0xab, 0xb4, {0x00, 0x08}, 0},
+       {"Acer", "Aspire 5755G",  "V1.21",   0xab, 0xb3, {0x00, 0x08}, 0},
+       /* Acer 521 */
+       {"Acer", "AO521", "V1.11", 0x55, 0x58, {0x1f, 0x00}, 0},
        /* Acer 531 */
-       {"Acer", "AO531h", "v0.3104", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AO531h", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
+       {"Acer", "AO531h", "v0.3104", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AO531h", "v0.3304", 0x55, 0x58, {0x20, 0x00}, 0},
        /* Acer 751 */
-       {"Acer", "AO751h", "V0.3212", 0x55, 0x58, {0x21, 0x00} },
+       {"Acer", "AO751h", "V0.3206", 0x55, 0x58, {0x21, 0x00}, 0},
+       {"Acer", "AO751h", "V0.3212", 0x55, 0x58, {0x21, 0x00}, 0},
+       /* Acer 753 */
+       {"Acer", "Aspire One 753", "V1.24", 0x93, 0xac, {0x14, 0x04}, 1},
        /* Acer 1825 */
-       {"Acer", "Aspire 1825PTZ", "V1.3118", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1825PTZ", "V1.3127", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1825PTZ", "V1.3118", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1825PTZ", "V1.3127", 0x55, 0x58, {0x9e, 0x00}, 0},
+       /* Acer Extensa 5420 */
+       {"Acer", "Extensa 5420", "V1.17", 0x93, 0xac, {0x14, 0x04}, 1},
+       /* Acer Aspire 5315 */
+       {"Acer", "Aspire 5315", "V1.19", 0x93, 0xac, {0x14, 0x04}, 1},
+       /* Acer Aspire 5739 */
+       {"Acer", "Aspire 5739G", "V1.3311", 0x55, 0x58, {0x20, 0x00}, 0},
        /* Acer TravelMate 7730 */
-       {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00} },
+       {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00}, 0},
+       /* Acer TravelMate TM8573T */
+       {"Acer", "TM8573T", "V1.13", 0x93, 0xa8, {0x14, 0x04}, 1},
        /* Gateway */
-       {"Gateway", "AOA110", "v0.3103",  0x55, 0x58, {0x21, 0x00} },
-       {"Gateway", "AOA150", "v0.3103",  0x55, 0x58, {0x20, 0x00} },
-       {"Gateway", "LT31",   "v1.3103",  0x55, 0x58, {0x9e, 0x00} },
-       {"Gateway", "LT31",   "v1.3201",  0x55, 0x58, {0x9e, 0x00} },
-       {"Gateway", "LT31",   "v1.3302",  0x55, 0x58, {0x9e, 0x00} },
-       {"Gateway", "LT31",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00} },
+       {"Gateway", "AOA110", "v0.3103",  0x55, 0x58, {0x21, 0x00}, 0},
+       {"Gateway", "AOA150", "v0.3103",  0x55, 0x58, {0x20, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3103",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3201",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3302",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0},
        /* Packard Bell */
-       {"Packard Bell", "DOA150",  "v0.3104",  0x55, 0x58, {0x21, 0x00} },
-       {"Packard Bell", "DOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00} },
-       {"Packard Bell", "AOA110",  "v0.3105",  0x55, 0x58, {0x21, 0x00} },
-       {"Packard Bell", "AOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00} },
-       {"Packard Bell", "ENBFT",   "V1.3118",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "ENBFT",   "V1.3127",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v1.3303",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3120",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3108",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3113",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3115",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3117",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3119",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v1.3204",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMA",   "v1.3201",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMA",   "v1.3302",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMA",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTVR46", "v1.3308",  0x55, 0x58, {0x9e, 0x00} },
+       {"Packard Bell", "DOA150",  "v0.3104",  0x55, 0x58, {0x21, 0x00}, 0},
+       {"Packard Bell", "DOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00}, 0},
+       {"Packard Bell", "AOA110",  "v0.3105",  0x55, 0x58, {0x21, 0x00}, 0},
+       {"Packard Bell", "AOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00}, 0},
+       {"Packard Bell", "ENBFT",   "V1.3118",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "ENBFT",   "V1.3127",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v1.3303",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3120",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3108",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3113",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3115",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3117",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3119",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v1.3204",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMA",   "v1.3201",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMA",   "v1.3302",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMA",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTVR46", "v1.3308",  0x55, 0x58, {0x9e, 0x00}, 0},
        /* pewpew-terminator */
-       {"", "", "", 0, 0, {0, 0} }
+       {"", "", "", 0, 0, {0, 0}, 0}
 };
 
-static const struct bios_settings_t *bios_cfg __read_mostly;
+static const struct bios_settings *bios_cfg __read_mostly;
+
+/*
+ * this struct is used to instruct thermal layer to use bang_bang instead of
+ * default governor for acerhdf
+ */
+static struct thermal_zone_params acerhdf_zone_params = {
+       .governor_name = "bang_bang",
+};
 
 static int acerhdf_get_temp(int *temp)
 {
@@ -275,6 +311,12 @@ static void acerhdf_change_fanstate(int state)
        fanstate = state;
 
        ec_write(bios_cfg->fanreg, cmd);
+
+       if (bios_cfg->mcmd_enable && state == ACERHDF_FAN_OFF) {
+               if (verbose)
+                       pr_notice("turning off fan manually\n");
+               ec_write(mcmd.mreg, mcmd.moff);
+       }
 }
 
 static void acerhdf_check_param(struct thermal_zone_device *thermal)
@@ -401,6 +443,21 @@ static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
 {
        if (trip == 0)
                *type = THERMAL_TRIP_ACTIVE;
+       else if (trip == 1)
+               *type = THERMAL_TRIP_CRITICAL;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
+                                unsigned long *temp)
+{
+       if (trip != 0)
+               return -EINVAL;
+
+       *temp = fanon - fanoff;
 
        return 0;
 }
@@ -410,6 +467,10 @@ static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
 {
        if (trip == 0)
                *temp = fanon;
+       else if (trip == 1)
+               *temp = ACERHDF_TEMP_CRIT;
+       else
+               return -EINVAL;
 
        return 0;
 }
@@ -429,6 +490,7 @@ static struct thermal_zone_device_ops acerhdf_dev_ops = {
        .get_mode = acerhdf_get_mode,
        .set_mode = acerhdf_set_mode,
        .get_trip_type = acerhdf_get_trip_type,
+       .get_trip_hyst = acerhdf_get_trip_hyst,
        .get_trip_temp = acerhdf_get_trip_temp,
        .get_crit_temp = acerhdf_get_crit_temp,
 };
@@ -481,9 +543,7 @@ static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
        }
 
        if (state == 0) {
-               /* turn fan off only if below fanoff temperature */
-               if ((cur_state == ACERHDF_FAN_AUTO) &&
-                   (cur_temp < fanoff))
+               if (cur_state == ACERHDF_FAN_AUTO)
                        acerhdf_change_fanstate(ACERHDF_FAN_OFF);
        } else {
                if (cur_state == ACERHDF_FAN_OFF)
@@ -558,7 +618,7 @@ static int str_starts_with(const char *str, const char *start)
 static int acerhdf_check_hardware(void)
 {
        char const *vendor, *version, *product;
-       const struct bios_settings_t *bt = NULL;
+       const struct bios_settings *bt = NULL;
 
        /* get BIOS data */
        vendor  = dmi_get_system_info(DMI_SYS_VENDOR);
@@ -660,12 +720,20 @@ static int acerhdf_register_thermal(void)
        if (IS_ERR(cl_dev))
                return -EINVAL;
 
-       thz_dev = thermal_zone_device_register("acerhdf", 1, 0, NULL,
-                                             &acerhdf_dev_ops, NULL, 0,
+       thz_dev = thermal_zone_device_register("acerhdf", 2, 0, NULL,
+                                             &acerhdf_dev_ops,
+                                             &acerhdf_zone_params, 0,
                                              (kernelmode) ? interval*1000 : 0);
        if (IS_ERR(thz_dev))
                return -EINVAL;
 
+       if (strcmp(thz_dev->governor->name,
+                               acerhdf_zone_params.governor_name)) {
+               pr_err("Didn't get thermal governor %s, perhaps not compiled into thermal subsystem.\n",
+                               acerhdf_zone_params.governor_name);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -722,9 +790,15 @@ MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5755G:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1825PTZ:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAO521*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5739G:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*One*753:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5315:");
 MODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:");
+MODULE_ALIAS("dmi:*:*Acer*:TM8573T:");
 MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
 MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:");
@@ -733,6 +807,7 @@ MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:");
 
 module_init(acerhdf_init);
 module_exit(acerhdf_exit);
index 05647f1a427e075dcb862ea94f97c53ea01ab876..f71700e0d13212cb3a93701d939c9fced2b0f6ca 100644 (file)
@@ -843,8 +843,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
 
 static void asus_backlight_exit(struct asus_laptop *asus)
 {
-       if (asus->backlight_device)
-               backlight_device_unregister(asus->backlight_device);
+       backlight_device_unregister(asus->backlight_device);
        asus->backlight_device = NULL;
 }
 
index c1a6cd66af421753668c0746a78c0870048ff7f9..abdaed34c7285116ffb573102880d55fafdfa8c1 100644 (file)
@@ -189,6 +189,15 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_wapf4,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X551CA",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X551CA"),
+               },
+               .driver_data = &quirk_asus_wapf4,
+       },
        {
                .callback = dmi_matched,
                .ident = "ASUSTeK COMPUTER INC. X55A",
index 21fc932da3a1461d35697cc2ee2c78085128b434..7543a56e0f4593c74fa74caee310a0ddbce4062c 100644 (file)
@@ -1308,8 +1308,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus)
 
 static void asus_wmi_backlight_exit(struct asus_wmi *asus)
 {
-       if (asus->backlight_device)
-               backlight_device_unregister(asus->backlight_device);
+       backlight_device_unregister(asus->backlight_device);
 
        asus->backlight_device = NULL;
 }
index f6a28d7161f5def0a5a3bf6164607b11cb1f8291..9411eae39a4ec5fc32f993d1583a3c329e2365b1 100644 (file)
@@ -2,9 +2,11 @@
  *  Driver for Dell laptop extras
  *
  *  Copyright (c) Red Hat <mjg@redhat.com>
+ *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
  *
- *  Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
- *  Inc.
+ *  Based on documentation in the libsmbios package:
+ *  Copyright (C) 2005-2014 Dell Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include "../../firmware/dcdbas.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
+#define KBD_LED_OFF_TOKEN 0x01E1
+#define KBD_LED_ON_TOKEN 0x01E2
+#define KBD_LED_AUTO_TOKEN 0x01E3
+#define KBD_LED_AUTO_25_TOKEN 0x02EA
+#define KBD_LED_AUTO_50_TOKEN 0x02EB
+#define KBD_LED_AUTO_75_TOKEN 0x02EC
+#define KBD_LED_AUTO_100_TOKEN 0x02F6
 
 /* This structure will be modified by the firmware when we enter
  * system management mode, hence the volatiles */
@@ -62,6 +71,13 @@ struct calling_interface_structure {
 
 struct quirk_entry {
        u8 touchpad_led;
+
+       int needs_kbd_timeouts;
+       /*
+        * Ordered list of timeouts expressed in seconds.
+        * The list must end with -1
+        */
+       int kbd_timeouts[];
 };
 
 static struct quirk_entry *quirks;
@@ -76,6 +92,15 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
        return 1;
 }
 
+/*
+ * These values come from Windows utility provided by Dell. If any other value
+ * is used then BIOS silently set timeout to 0 without any error message.
+ */
+static struct quirk_entry quirk_dell_xps13_9333 = {
+       .needs_kbd_timeouts = 1,
+       .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
+};
+
 static int da_command_address;
 static int da_command_code;
 static int da_num_tokens;
@@ -267,6 +292,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
                },
                .driver_data = &quirk_dell_vostro_v130,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell XPS13 9333",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
+               },
+               .driver_data = &quirk_dell_xps13_9333,
+       },
        { }
 };
 
@@ -331,17 +365,29 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
        }
 }
 
-static int find_token_location(int tokenid)
+static int find_token_id(int tokenid)
 {
        int i;
+
        for (i = 0; i < da_num_tokens; i++) {
                if (da_tokens[i].tokenID == tokenid)
-                       return da_tokens[i].location;
+                       return i;
        }
 
        return -1;
 }
 
+static int find_token_location(int tokenid)
+{
+       int id;
+
+       id = find_token_id(tokenid);
+       if (id == -1)
+               return -1;
+
+       return da_tokens[id].location;
+}
+
 static struct calling_interface_buffer *
 dell_send_request(struct calling_interface_buffer *buffer, int class,
                  int select)
@@ -362,6 +408,20 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
        return buffer;
 }
 
+static inline int dell_smi_error(int value)
+{
+       switch (value) {
+       case 0: /* Completed successfully */
+               return 0;
+       case -1: /* Completed with error */
+               return -EIO;
+       case -2: /* Function not supported */
+               return -ENXIO;
+       default: /* Unknown error */
+               return -EINVAL;
+       }
+}
+
 /* Derived from information in DellWirelessCtl.cpp:
    Class 17, select 11 is radio control. It returns an array of 32-bit values.
 
@@ -563,7 +623,7 @@ static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
 {
        static bool extended;
 
-       if (str & 0x20)
+       if (str & I8042_STR_AUXDATA)
                return false;
 
        if (unlikely(data == 0xe0)) {
@@ -716,7 +776,7 @@ static int dell_send_intensity(struct backlight_device *bd)
        else
                dell_send_request(buffer, 1, 1);
 
-out:
+ out:
        release_buffer();
        return ret;
 }
@@ -740,7 +800,7 @@ static int dell_get_intensity(struct backlight_device *bd)
 
        ret = buffer->output[1];
 
-out:
+ out:
        release_buffer();
        return ret;
 }
@@ -789,6 +849,984 @@ static void touchpad_led_exit(void)
        led_classdev_unregister(&touchpad_led);
 }
 
+/*
+ * Derived from information in smbios-keyboard-ctl:
+ *
+ * cbClass 4
+ * cbSelect 11
+ * Keyboard illumination
+ * cbArg1 determines the function to be performed
+ *
+ * cbArg1 0x0 = Get Feature Information
+ *  cbRES1         Standard return codes (0, -1, -2)
+ *  cbRES2, word0  Bitmap of user-selectable modes
+ *     bit 0     Always off (All systems)
+ *     bit 1     Always on (Travis ATG, Siberia)
+ *     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
+ *     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
+ *     bit 4     Auto: Input-activity-based On; input-activity based Off
+ *     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
+ *     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
+ *     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
+ *     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
+ *     bits 9-15 Reserved for future use
+ *  cbRES2, byte2  Reserved for future use
+ *  cbRES2, byte3  Keyboard illumination type
+ *     0         Reserved
+ *     1         Tasklight
+ *     2         Backlight
+ *     3-255     Reserved for future use
+ *  cbRES3, byte0  Supported auto keyboard illumination trigger bitmap.
+ *     bit 0     Any keystroke
+ *     bit 1     Touchpad activity
+ *     bit 2     Pointing stick
+ *     bit 3     Any mouse
+ *     bits 4-7  Reserved for future use
+ *  cbRES3, byte1  Supported timeout unit bitmap
+ *     bit 0     Seconds
+ *     bit 1     Minutes
+ *     bit 2     Hours
+ *     bit 3     Days
+ *     bits 4-7  Reserved for future use
+ *  cbRES3, byte2  Number of keyboard light brightness levels
+ *  cbRES4, byte0  Maximum acceptable seconds value (0 if seconds not supported).
+ *  cbRES4, byte1  Maximum acceptable minutes value (0 if minutes not supported).
+ *  cbRES4, byte2  Maximum acceptable hours value (0 if hours not supported).
+ *  cbRES4, byte3  Maximum acceptable days value (0 if days not supported)
+ *
+ * cbArg1 0x1 = Get Current State
+ *  cbRES1         Standard return codes (0, -1, -2)
+ *  cbRES2, word0  Bitmap of current mode state
+ *     bit 0     Always off (All systems)
+ *     bit 1     Always on (Travis ATG, Siberia)
+ *     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
+ *     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
+ *     bit 4     Auto: Input-activity-based On; input-activity based Off
+ *     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
+ *     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
+ *     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
+ *     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
+ *     bits 9-15 Reserved for future use
+ *     Note: Only One bit can be set
+ *  cbRES2, byte2  Currently active auto keyboard illumination triggers.
+ *     bit 0     Any keystroke
+ *     bit 1     Touchpad activity
+ *     bit 2     Pointing stick
+ *     bit 3     Any mouse
+ *     bits 4-7  Reserved for future use
+ *  cbRES2, byte3  Current Timeout
+ *     bits 7:6  Timeout units indicator:
+ *     00b       Seconds
+ *     01b       Minutes
+ *     10b       Hours
+ *     11b       Days
+ *     bits 5:0  Timeout value (0-63) in sec/min/hr/day
+ *     NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte
+ *     are set upon return from the [Get feature information] call.
+ *  cbRES3, byte0  Current setting of ALS value that turns the light on or off.
+ *  cbRES3, byte1  Current ALS reading
+ *  cbRES3, byte2  Current keyboard light level.
+ *
+ * cbArg1 0x2 = Set New State
+ *  cbRES1         Standard return codes (0, -1, -2)
+ *  cbArg2, word0  Bitmap of current mode state
+ *     bit 0     Always off (All systems)
+ *     bit 1     Always on (Travis ATG, Siberia)
+ *     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
+ *     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
+ *     bit 4     Auto: Input-activity-based On; input-activity based Off
+ *     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
+ *     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
+ *     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
+ *     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
+ *     bits 9-15 Reserved for future use
+ *     Note: Only One bit can be set
+ *  cbArg2, byte2  Desired auto keyboard illumination triggers. Must remain inactive to allow
+ *                 keyboard to turn off automatically.
+ *     bit 0     Any keystroke
+ *     bit 1     Touchpad activity
+ *     bit 2     Pointing stick
+ *     bit 3     Any mouse
+ *     bits 4-7  Reserved for future use
+ *  cbArg2, byte3  Desired Timeout
+ *     bits 7:6  Timeout units indicator:
+ *     00b       Seconds
+ *     01b       Minutes
+ *     10b       Hours
+ *     11b       Days
+ *     bits 5:0  Timeout value (0-63) in sec/min/hr/day
+ *  cbArg3, byte0  Desired setting of ALS value that turns the light on or off.
+ *  cbArg3, byte2  Desired keyboard light level.
+ */
+
+
+enum kbd_timeout_unit {
+       KBD_TIMEOUT_SECONDS = 0,
+       KBD_TIMEOUT_MINUTES,
+       KBD_TIMEOUT_HOURS,
+       KBD_TIMEOUT_DAYS,
+};
+
+enum kbd_mode_bit {
+       KBD_MODE_BIT_OFF = 0,
+       KBD_MODE_BIT_ON,
+       KBD_MODE_BIT_ALS,
+       KBD_MODE_BIT_TRIGGER_ALS,
+       KBD_MODE_BIT_TRIGGER,
+       KBD_MODE_BIT_TRIGGER_25,
+       KBD_MODE_BIT_TRIGGER_50,
+       KBD_MODE_BIT_TRIGGER_75,
+       KBD_MODE_BIT_TRIGGER_100,
+};
+
+#define kbd_is_als_mode_bit(bit) \
+       ((bit) == KBD_MODE_BIT_ALS || (bit) == KBD_MODE_BIT_TRIGGER_ALS)
+#define kbd_is_trigger_mode_bit(bit) \
+       ((bit) >= KBD_MODE_BIT_TRIGGER_ALS && (bit) <= KBD_MODE_BIT_TRIGGER_100)
+#define kbd_is_level_mode_bit(bit) \
+       ((bit) >= KBD_MODE_BIT_TRIGGER_25 && (bit) <= KBD_MODE_BIT_TRIGGER_100)
+
+struct kbd_info {
+       u16 modes;
+       u8 type;
+       u8 triggers;
+       u8 levels;
+       u8 seconds;
+       u8 minutes;
+       u8 hours;
+       u8 days;
+};
+
+struct kbd_state {
+       u8 mode_bit;
+       u8 triggers;
+       u8 timeout_value;
+       u8 timeout_unit;
+       u8 als_setting;
+       u8 als_value;
+       u8 level;
+};
+
+static const int kbd_tokens[] = {
+       KBD_LED_OFF_TOKEN,
+       KBD_LED_AUTO_25_TOKEN,
+       KBD_LED_AUTO_50_TOKEN,
+       KBD_LED_AUTO_75_TOKEN,
+       KBD_LED_AUTO_100_TOKEN,
+       KBD_LED_ON_TOKEN,
+};
+
+static u16 kbd_token_bits;
+
+static struct kbd_info kbd_info;
+static bool kbd_als_supported;
+static bool kbd_triggers_supported;
+
+static u8 kbd_mode_levels[16];
+static int kbd_mode_levels_count;
+
+static u8 kbd_previous_level;
+static u8 kbd_previous_mode_bit;
+
+static bool kbd_led_present;
+
+/*
+ * NOTE: there are three ways to set the keyboard backlight level.
+ * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value).
+ * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels).
+ * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens)
+ *
+ * There are laptops which support only one of these methods. If we want to
+ * support as many machines as possible we need to implement all three methods.
+ * The first two methods use the kbd_state structure. The third uses SMBIOS
+ * tokens. If kbd_info.levels == 0, the machine does not support setting the
+ * keyboard backlight level via kbd_state.level.
+ */
+
+static int kbd_get_info(struct kbd_info *info)
+{
+       u8 units;
+       int ret;
+
+       get_buffer();
+
+       buffer->input[0] = 0x0;
+       dell_send_request(buffer, 4, 11);
+       ret = buffer->output[0];
+
+       if (ret) {
+               ret = dell_smi_error(ret);
+               goto out;
+       }
+
+       info->modes = buffer->output[1] & 0xFFFF;
+       info->type = (buffer->output[1] >> 24) & 0xFF;
+       info->triggers = buffer->output[2] & 0xFF;
+       units = (buffer->output[2] >> 8) & 0xFF;
+       info->levels = (buffer->output[2] >> 16) & 0xFF;
+
+       if (units & BIT(0))
+               info->seconds = (buffer->output[3] >> 0) & 0xFF;
+       if (units & BIT(1))
+               info->minutes = (buffer->output[3] >> 8) & 0xFF;
+       if (units & BIT(2))
+               info->hours = (buffer->output[3] >> 16) & 0xFF;
+       if (units & BIT(3))
+               info->days = (buffer->output[3] >> 24) & 0xFF;
+
+ out:
+       release_buffer();
+       return ret;
+}
+
+static unsigned int kbd_get_max_level(void)
+{
+       if (kbd_info.levels != 0)
+               return kbd_info.levels;
+       if (kbd_mode_levels_count > 0)
+               return kbd_mode_levels_count - 1;
+       return 0;
+}
+
+static int kbd_get_level(struct kbd_state *state)
+{
+       int i;
+
+       if (kbd_info.levels != 0)
+               return state->level;
+
+       if (kbd_mode_levels_count > 0) {
+               for (i = 0; i < kbd_mode_levels_count; ++i)
+                       if (kbd_mode_levels[i] == state->mode_bit)
+                               return i;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int kbd_set_level(struct kbd_state *state, u8 level)
+{
+       if (kbd_info.levels != 0) {
+               if (level != 0)
+                       kbd_previous_level = level;
+               if (state->level == level)
+                       return 0;
+               state->level = level;
+               if (level != 0 && state->mode_bit == KBD_MODE_BIT_OFF)
+                       state->mode_bit = kbd_previous_mode_bit;
+               else if (level == 0 && state->mode_bit != KBD_MODE_BIT_OFF) {
+                       kbd_previous_mode_bit = state->mode_bit;
+                       state->mode_bit = KBD_MODE_BIT_OFF;
+               }
+               return 0;
+       }
+
+       if (kbd_mode_levels_count > 0 && level < kbd_mode_levels_count) {
+               if (level != 0)
+                       kbd_previous_level = level;
+               state->mode_bit = kbd_mode_levels[level];
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int kbd_get_state(struct kbd_state *state)
+{
+       int ret;
+
+       get_buffer();
+
+       buffer->input[0] = 0x1;
+       dell_send_request(buffer, 4, 11);
+       ret = buffer->output[0];
+
+       if (ret) {
+               ret = dell_smi_error(ret);
+               goto out;
+       }
+
+       state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
+       if (state->mode_bit != 0)
+               state->mode_bit--;
+
+       state->triggers = (buffer->output[1] >> 16) & 0xFF;
+       state->timeout_value = (buffer->output[1] >> 24) & 0x3F;
+       state->timeout_unit = (buffer->output[1] >> 30) & 0x3;
+       state->als_setting = buffer->output[2] & 0xFF;
+       state->als_value = (buffer->output[2] >> 8) & 0xFF;
+       state->level = (buffer->output[2] >> 16) & 0xFF;
+
+ out:
+       release_buffer();
+       return ret;
+}
+
+static int kbd_set_state(struct kbd_state *state)
+{
+       int ret;
+
+       get_buffer();
+       buffer->input[0] = 0x2;
+       buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
+       buffer->input[1] |= (state->triggers & 0xFF) << 16;
+       buffer->input[1] |= (state->timeout_value & 0x3F) << 24;
+       buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
+       buffer->input[2] = state->als_setting & 0xFF;
+       buffer->input[2] |= (state->level & 0xFF) << 16;
+       dell_send_request(buffer, 4, 11);
+       ret = buffer->output[0];
+       release_buffer();
+
+       return dell_smi_error(ret);
+}
+
+static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
+{
+       int ret;
+
+       ret = kbd_set_state(state);
+       if (ret == 0)
+               return 0;
+
+       /*
+        * When setting the new state fails,try to restore the previous one.
+        * This is needed on some machines where BIOS sets a default state when
+        * setting a new state fails. This default state could be all off.
+        */
+
+       if (kbd_set_state(old))
+               pr_err("Setting old previous keyboard state failed\n");
+
+       return ret;
+}
+
+static int kbd_set_token_bit(u8 bit)
+{
+       int id;
+       int ret;
+
+       if (bit >= ARRAY_SIZE(kbd_tokens))
+               return -EINVAL;
+
+       id = find_token_id(kbd_tokens[bit]);
+       if (id == -1)
+               return -EINVAL;
+
+       get_buffer();
+       buffer->input[0] = da_tokens[id].location;
+       buffer->input[1] = da_tokens[id].value;
+       dell_send_request(buffer, 1, 0);
+       ret = buffer->output[0];
+       release_buffer();
+
+       return dell_smi_error(ret);
+}
+
+static int kbd_get_token_bit(u8 bit)
+{
+       int id;
+       int ret;
+       int val;
+
+       if (bit >= ARRAY_SIZE(kbd_tokens))
+               return -EINVAL;
+
+       id = find_token_id(kbd_tokens[bit]);
+       if (id == -1)
+               return -EINVAL;
+
+       get_buffer();
+       buffer->input[0] = da_tokens[id].location;
+       dell_send_request(buffer, 0, 0);
+       ret = buffer->output[0];
+       val = buffer->output[1];
+       release_buffer();
+
+       if (ret)
+               return dell_smi_error(ret);
+
+       return (val == da_tokens[id].value);
+}
+
+static int kbd_get_first_active_token_bit(void)
+{
+       int i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) {
+               ret = kbd_get_token_bit(i);
+               if (ret == 1)
+                       return i;
+       }
+
+       return ret;
+}
+
+static int kbd_get_valid_token_counts(void)
+{
+       return hweight16(kbd_token_bits);
+}
+
+static inline int kbd_init_info(void)
+{
+       struct kbd_state state;
+       int ret;
+       int i;
+
+       ret = kbd_get_info(&kbd_info);
+       if (ret)
+               return ret;
+
+       kbd_get_state(&state);
+
+       /* NOTE: timeout value is stored in 6 bits so max value is 63 */
+       if (kbd_info.seconds > 63)
+               kbd_info.seconds = 63;
+       if (kbd_info.minutes > 63)
+               kbd_info.minutes = 63;
+       if (kbd_info.hours > 63)
+               kbd_info.hours = 63;
+       if (kbd_info.days > 63)
+               kbd_info.days = 63;
+
+       /* NOTE: On tested machines ON mode did not work and caused
+        *       problems (turned backlight off) so do not use it
+        */
+       kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON);
+
+       kbd_previous_level = kbd_get_level(&state);
+       kbd_previous_mode_bit = state.mode_bit;
+
+       if (kbd_previous_level == 0 && kbd_get_max_level() != 0)
+               kbd_previous_level = 1;
+
+       if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) {
+               kbd_previous_mode_bit =
+                       ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF));
+               if (kbd_previous_mode_bit != 0)
+                       kbd_previous_mode_bit--;
+       }
+
+       if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) |
+                             BIT(KBD_MODE_BIT_TRIGGER_ALS)))
+               kbd_als_supported = true;
+
+       if (kbd_info.modes & (
+           BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) |
+           BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) |
+           BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100)
+          ))
+               kbd_triggers_supported = true;
+
+       /* kbd_mode_levels[0] is reserved, see below */
+       for (i = 0; i < 16; ++i)
+               if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes))
+                       kbd_mode_levels[1 + kbd_mode_levels_count++] = i;
+
+       /*
+        * Find the first supported mode and assign to kbd_mode_levels[0].
+        * This should be 0 (off), but we cannot depend on the BIOS to
+        * support 0.
+        */
+       if (kbd_mode_levels_count > 0) {
+               for (i = 0; i < 16; ++i) {
+                       if (BIT(i) & kbd_info.modes) {
+                               kbd_mode_levels[0] = i;
+                               break;
+                       }
+               }
+               kbd_mode_levels_count++;
+       }
+
+       return 0;
+
+}
+
+static inline void kbd_init_tokens(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
+               if (find_token_id(kbd_tokens[i]) != -1)
+                       kbd_token_bits |= BIT(i);
+}
+
+static void kbd_init(void)
+{
+       int ret;
+
+       ret = kbd_init_info();
+       kbd_init_tokens();
+
+       if (kbd_token_bits != 0 || ret == 0)
+               kbd_led_present = true;
+}
+
+static ssize_t kbd_led_timeout_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct kbd_state new_state;
+       struct kbd_state state;
+       bool convert;
+       int value;
+       int ret;
+       char ch;
+       u8 unit;
+       int i;
+
+       ret = sscanf(buf, "%d %c", &value, &ch);
+       if (ret < 1)
+               return -EINVAL;
+       else if (ret == 1)
+               ch = 's';
+
+       if (value < 0)
+               return -EINVAL;
+
+       convert = false;
+
+       switch (ch) {
+       case 's':
+               if (value > kbd_info.seconds)
+                       convert = true;
+               unit = KBD_TIMEOUT_SECONDS;
+               break;
+       case 'm':
+               if (value > kbd_info.minutes)
+                       convert = true;
+               unit = KBD_TIMEOUT_MINUTES;
+               break;
+       case 'h':
+               if (value > kbd_info.hours)
+                       convert = true;
+               unit = KBD_TIMEOUT_HOURS;
+               break;
+       case 'd':
+               if (value > kbd_info.days)
+                       convert = true;
+               unit = KBD_TIMEOUT_DAYS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (quirks && quirks->needs_kbd_timeouts)
+               convert = true;
+
+       if (convert) {
+               /* Convert value from current units to seconds */
+               switch (unit) {
+               case KBD_TIMEOUT_DAYS:
+                       value *= 24;
+               case KBD_TIMEOUT_HOURS:
+                       value *= 60;
+               case KBD_TIMEOUT_MINUTES:
+                       value *= 60;
+                       unit = KBD_TIMEOUT_SECONDS;
+               }
+
+               if (quirks && quirks->needs_kbd_timeouts) {
+                       for (i = 0; quirks->kbd_timeouts[i] != -1; i++) {
+                               if (value <= quirks->kbd_timeouts[i]) {
+                                       value = quirks->kbd_timeouts[i];
+                                       break;
+                               }
+                       }
+               }
+
+               if (value <= kbd_info.seconds && kbd_info.seconds) {
+                       unit = KBD_TIMEOUT_SECONDS;
+               } else if (value / 60 <= kbd_info.minutes && kbd_info.minutes) {
+                       value /= 60;
+                       unit = KBD_TIMEOUT_MINUTES;
+               } else if (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) {
+                       value /= (60 * 60);
+                       unit = KBD_TIMEOUT_HOURS;
+               } else if (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) {
+                       value /= (60 * 60 * 24);
+                       unit = KBD_TIMEOUT_DAYS;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       new_state = state;
+       new_state.timeout_value = value;
+       new_state.timeout_unit = unit;
+
+       ret = kbd_set_state_safe(&new_state, &state);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static ssize_t kbd_led_timeout_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct kbd_state state;
+       int ret;
+       int len;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       len = sprintf(buf, "%d", state.timeout_value);
+
+       switch (state.timeout_unit) {
+       case KBD_TIMEOUT_SECONDS:
+               return len + sprintf(buf+len, "s\n");
+       case KBD_TIMEOUT_MINUTES:
+               return len + sprintf(buf+len, "m\n");
+       case KBD_TIMEOUT_HOURS:
+               return len + sprintf(buf+len, "h\n");
+       case KBD_TIMEOUT_DAYS:
+               return len + sprintf(buf+len, "d\n");
+       default:
+               return -EINVAL;
+       }
+
+       return len;
+}
+
+static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR,
+                  kbd_led_timeout_show, kbd_led_timeout_store);
+
+static const char * const kbd_led_triggers[] = {
+       "keyboard",
+       "touchpad",
+       /*"trackstick"*/ NULL, /* NOTE: trackstick is just alias for touchpad */
+       "mouse",
+};
+
+static ssize_t kbd_led_triggers_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct kbd_state new_state;
+       struct kbd_state state;
+       bool triggers_enabled = false;
+       bool als_enabled = false;
+       bool disable_als = false;
+       bool enable_als = false;
+       int trigger_bit = -1;
+       char trigger[21];
+       int i, ret;
+
+       ret = sscanf(buf, "%20s", trigger);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (trigger[0] != '+' && trigger[0] != '-')
+               return -EINVAL;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       if (kbd_als_supported)
+               als_enabled = kbd_is_als_mode_bit(state.mode_bit);
+
+       if (kbd_triggers_supported)
+               triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
+
+       if (kbd_als_supported) {
+               if (strcmp(trigger, "+als") == 0) {
+                       if (als_enabled)
+                               return count;
+                       enable_als = true;
+               } else if (strcmp(trigger, "-als") == 0) {
+                       if (!als_enabled)
+                               return count;
+                       disable_als = true;
+               }
+       }
+
+       if (enable_als || disable_als) {
+               new_state = state;
+               if (enable_als) {
+                       if (triggers_enabled)
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS;
+                       else
+                               new_state.mode_bit = KBD_MODE_BIT_ALS;
+               } else {
+                       if (triggers_enabled) {
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
+                               kbd_set_level(&new_state, kbd_previous_level);
+                       } else {
+                               new_state.mode_bit = KBD_MODE_BIT_ON;
+                       }
+               }
+               if (!(kbd_info.modes & BIT(new_state.mode_bit)))
+                       return -EINVAL;
+               ret = kbd_set_state_safe(&new_state, &state);
+               if (ret)
+                       return ret;
+               kbd_previous_mode_bit = new_state.mode_bit;
+               return count;
+       }
+
+       if (kbd_triggers_supported) {
+               for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
+                       if (!(kbd_info.triggers & BIT(i)))
+                               continue;
+                       if (!kbd_led_triggers[i])
+                               continue;
+                       if (strcmp(trigger+1, kbd_led_triggers[i]) != 0)
+                               continue;
+                       if (trigger[0] == '+' &&
+                           triggers_enabled && (state.triggers & BIT(i)))
+                               return count;
+                       if (trigger[0] == '-' &&
+                           (!triggers_enabled || !(state.triggers & BIT(i))))
+                               return count;
+                       trigger_bit = i;
+                       break;
+               }
+       }
+
+       if (trigger_bit != -1) {
+               new_state = state;
+               if (trigger[0] == '+')
+                       new_state.triggers |= BIT(trigger_bit);
+               else {
+                       new_state.triggers &= ~BIT(trigger_bit);
+                       /* NOTE: trackstick bit (2) must be disabled when
+                        *       disabling touchpad bit (1), otherwise touchpad
+                        *       bit (1) will not be disabled */
+                       if (trigger_bit == 1)
+                               new_state.triggers &= ~BIT(2);
+               }
+               if ((kbd_info.triggers & new_state.triggers) !=
+                   new_state.triggers)
+                       return -EINVAL;
+               if (new_state.triggers && !triggers_enabled) {
+                       if (als_enabled)
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS;
+                       else {
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
+                               kbd_set_level(&new_state, kbd_previous_level);
+                       }
+               } else if (new_state.triggers == 0) {
+                       if (als_enabled)
+                               new_state.mode_bit = KBD_MODE_BIT_ALS;
+                       else
+                               kbd_set_level(&new_state, 0);
+               }
+               if (!(kbd_info.modes & BIT(new_state.mode_bit)))
+                       return -EINVAL;
+               ret = kbd_set_state_safe(&new_state, &state);
+               if (ret)
+                       return ret;
+               if (new_state.mode_bit != KBD_MODE_BIT_OFF)
+                       kbd_previous_mode_bit = new_state.mode_bit;
+               return count;
+       }
+
+       return -EINVAL;
+}
+
+static ssize_t kbd_led_triggers_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct kbd_state state;
+       bool triggers_enabled;
+       int level, i, ret;
+       int len = 0;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       len = 0;
+
+       if (kbd_triggers_supported) {
+               triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
+               level = kbd_get_level(&state);
+               for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
+                       if (!(kbd_info.triggers & BIT(i)))
+                               continue;
+                       if (!kbd_led_triggers[i])
+                               continue;
+                       if ((triggers_enabled || level <= 0) &&
+                           (state.triggers & BIT(i)))
+                               buf[len++] = '+';
+                       else
+                               buf[len++] = '-';
+                       len += sprintf(buf+len, "%s ", kbd_led_triggers[i]);
+               }
+       }
+
+       if (kbd_als_supported) {
+               if (kbd_is_als_mode_bit(state.mode_bit))
+                       len += sprintf(buf+len, "+als ");
+               else
+                       len += sprintf(buf+len, "-als ");
+       }
+
+       if (len)
+               buf[len - 1] = '\n';
+
+       return len;
+}
+
+static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR,
+                  kbd_led_triggers_show, kbd_led_triggers_store);
+
+static ssize_t kbd_led_als_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct kbd_state state;
+       struct kbd_state new_state;
+       u8 setting;
+       int ret;
+
+       ret = kstrtou8(buf, 10, &setting);
+       if (ret)
+               return ret;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       new_state = state;
+       new_state.als_setting = setting;
+
+       ret = kbd_set_state_safe(&new_state, &state);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static ssize_t kbd_led_als_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct kbd_state state;
+       int ret;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n", state.als_setting);
+}
+
+static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR,
+                  kbd_led_als_show, kbd_led_als_store);
+
+static struct attribute *kbd_led_attrs[] = {
+       &dev_attr_stop_timeout.attr,
+       &dev_attr_start_triggers.attr,
+       &dev_attr_als_setting.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(kbd_led);
+
+static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev)
+{
+       int ret;
+       u16 num;
+       struct kbd_state state;
+
+       if (kbd_get_max_level()) {
+               ret = kbd_get_state(&state);
+               if (ret)
+                       return 0;
+               ret = kbd_get_level(&state);
+               if (ret < 0)
+                       return 0;
+               return ret;
+       }
+
+       if (kbd_get_valid_token_counts()) {
+               ret = kbd_get_first_active_token_bit();
+               if (ret < 0)
+                       return 0;
+               for (num = kbd_token_bits; num != 0 && ret > 0; --ret)
+                       num &= num - 1; /* clear the first bit set */
+               if (num == 0)
+                       return 0;
+               return ffs(num) - 1;
+       }
+
+       pr_warn("Keyboard brightness level control not supported\n");
+       return 0;
+}
+
+static void kbd_led_level_set(struct led_classdev *led_cdev,
+                             enum led_brightness value)
+{
+       struct kbd_state state;
+       struct kbd_state new_state;
+       u16 num;
+
+       if (kbd_get_max_level()) {
+               if (kbd_get_state(&state))
+                       return;
+               new_state = state;
+               if (kbd_set_level(&new_state, value))
+                       return;
+               kbd_set_state_safe(&new_state, &state);
+               return;
+       }
+
+       if (kbd_get_valid_token_counts()) {
+               for (num = kbd_token_bits; num != 0 && value > 0; --value)
+                       num &= num - 1; /* clear the first bit set */
+               if (num == 0)
+                       return;
+               kbd_set_token_bit(ffs(num) - 1);
+               return;
+       }
+
+       pr_warn("Keyboard brightness level control not supported\n");
+}
+
+static struct led_classdev kbd_led = {
+       .name           = "dell::kbd_backlight",
+       .brightness_set = kbd_led_level_set,
+       .brightness_get = kbd_led_level_get,
+       .groups         = kbd_led_groups,
+};
+
+static int __init kbd_led_init(struct device *dev)
+{
+       kbd_init();
+       if (!kbd_led_present)
+               return -ENODEV;
+       kbd_led.max_brightness = kbd_get_max_level();
+       if (!kbd_led.max_brightness) {
+               kbd_led.max_brightness = kbd_get_valid_token_counts();
+               if (kbd_led.max_brightness)
+                       kbd_led.max_brightness--;
+       }
+       return led_classdev_register(dev, &kbd_led);
+}
+
+static void brightness_set_exit(struct led_classdev *led_cdev,
+                               enum led_brightness value)
+{
+       /* Don't change backlight level on exit */
+};
+
+static void kbd_led_exit(void)
+{
+       if (!kbd_led_present)
+               return;
+       kbd_led.brightness_set = brightness_set_exit;
+       led_classdev_unregister(&kbd_led);
+}
+
 static int __init dell_init(void)
 {
        int max_intensity = 0;
@@ -841,6 +1879,8 @@ static int __init dell_init(void)
        if (quirks && quirks->touchpad_led)
                touchpad_led_init(&platform_device->dev);
 
+       kbd_led_init(&platform_device->dev);
+
        dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
        if (dell_laptop_dir != NULL)
                debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
@@ -908,6 +1948,7 @@ static void __exit dell_exit(void)
        debugfs_remove_recursive(dell_laptop_dir);
        if (quirks && quirks->touchpad_led)
                touchpad_led_exit();
+       kbd_led_exit();
        i8042_remove_filter(dell_laptop_i8042_filter);
        cancel_delayed_work_sync(&dell_rfkill_work);
        backlight_device_unregister(dell_backlight_device);
@@ -924,5 +1965,7 @@ module_init(dell_init);
 module_exit(dell_exit);
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
 MODULE_DESCRIPTION("Dell laptop driver");
 MODULE_LICENSE("GPL");
index a653716055d1e6a79cddb68f259a9f8625a1b268..0aec4fd4c48ecf5788792b8052b2c8eea8bd7d8c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver
+ *  dell-smo8800.c - Dell Latitude ACPI SMO88XX freefall sensor driver
  *
  *  Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
  *  Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
@@ -209,7 +209,13 @@ static int smo8800_remove(struct acpi_device *device)
 
 static const struct acpi_device_id smo8800_ids[] = {
        { "SMO8800", 0 },
+       { "SMO8801", 0 },
        { "SMO8810", 0 },
+       { "SMO8811", 0 },
+       { "SMO8820", 0 },
+       { "SMO8821", 0 },
+       { "SMO8830", 0 },
+       { "SMO8831", 0 },
        { "", 0 },
 };
 
@@ -228,6 +234,6 @@ static struct acpi_driver smo8800_driver = {
 
 module_acpi_driver(smo8800_driver);
 
-MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)");
+MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO88XX)");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Sonal Santan, Pali Rohár");
index 25721bf20092dc47865e08a6b21c3011c0c0f05a..6512a06bc0535d28e312ae7a697b6507de5dec40 100644 (file)
@@ -65,10 +65,8 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
        /* Battery health status button */
        { KE_KEY, 0xe007, { KEY_BATTERY } },
 
-       /* This is actually for all radios. Although physically a
-        * switch, the notification does not provide an indication of
-        * state and so it should be reported as a key */
-       { KE_KEY, 0xe008, { KEY_WLAN } },
+       /* Radio devices state change */
+       { 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 */
@@ -145,57 +143,154 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
 
 static struct input_dev *dell_wmi_input_dev;
 
+static void dell_wmi_process_key(int reported_key)
+{
+       const struct key_entry *key;
+
+       key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
+                                               reported_key);
+       if (!key) {
+               pr_info("Unknown key %x pressed\n", reported_key);
+               return;
+       }
+
+       pr_debug("Key %x pressed\n", reported_key);
+
+       /* Don't report brightness notifications that will also come via ACPI */
+       if ((key->keycode == KEY_BRIGHTNESSUP ||
+            key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
+               return;
+
+       sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
+}
+
 static void dell_wmi_notify(u32 value, void *context)
 {
        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *obj;
        acpi_status status;
+       acpi_size buffer_size;
+       u16 *buffer_entry, *buffer_end;
+       int len, i;
 
        status = wmi_get_event_data(value, &response);
        if (status != AE_OK) {
-               pr_info("bad event status 0x%x\n", status);
+               pr_warn("bad event status 0x%x\n", status);
                return;
        }
 
        obj = (union acpi_object *)response.pointer;
+       if (!obj) {
+               pr_warn("no response\n");
+               return;
+       }
 
-       if (obj && obj->type == ACPI_TYPE_BUFFER) {
-               const struct key_entry *key;
-               int reported_key;
-               u16 *buffer_entry = (u16 *)obj->buffer.pointer;
-               int buffer_size = obj->buffer.length/2;
-
-               if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
-                       pr_info("Received unknown WMI event (0x%x)\n",
-                               buffer_entry[1]);
-                       kfree(obj);
-                       return;
-               }
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               pr_warn("bad response type %x\n", obj->type);
+               kfree(obj);
+               return;
+       }
 
-               if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0))
-                       reported_key = (int)buffer_entry[2];
+       pr_debug("Received WMI event (%*ph)\n",
+               obj->buffer.length, obj->buffer.pointer);
+
+       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)
-                       reported_key = (int)buffer_entry[1] & 0xffff;
-               else {
+                       dell_wmi_process_key(buffer_entry[1]);
+               else
                        pr_info("Received unknown WMI event\n");
-                       kfree(obj);
-                       return;
+               kfree(obj);
+               return;
+       }
+
+       buffer_end = buffer_entry + buffer_size;
+
+       while (buffer_entry < buffer_end) {
+
+               len = buffer_entry[0];
+               if (len == 0)
+                       break;
+
+               len++;
+
+               if (buffer_entry + len > buffer_end) {
+                       pr_warn("Invalid length of WMI event\n");
+                       break;
                }
 
-               key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
-                                                       reported_key);
-               if (!key) {
-                       pr_info("Unknown key %x pressed\n", reported_key);
-               } else if ((key->keycode == KEY_BRIGHTNESSUP ||
-                           key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
-                       /* Don't report brightness notifications that will also
-                        * come via ACPI */
-                       ;
-               } else {
-                       sparse_keymap_report_entry(dell_wmi_input_dev, key,
-                                                  1, true);
+               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;
+                               }
+                       }
+                       break;
+               case 0x10:
+                       /* Keys pressed */
+                       for (i = 2; i < len; ++i)
+                               dell_wmi_process_key(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 */
+                       pr_info("Unknown WMI event type 0x%x\n",
+                               (int)buffer_entry[1]);
+                       break;
                }
+
+               buffer_entry += len;
+
        }
+
        kfree(obj);
 }
 
@@ -213,11 +308,16 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
        for (i = 0; i < hotkey_num; i++) {
                const struct dell_bios_keymap_entry *bios_entry =
                                        &dell_bios_hotkey_table->keymap[i];
-               keymap[i].type = KE_KEY;
-               keymap[i].code = bios_entry->scancode;
-               keymap[i].keycode = bios_entry->keycode < 256 ?
+               u16 keycode = bios_entry->keycode < 256 ?
                                    bios_to_linux_keycode[bios_entry->keycode] :
                                    KEY_RESERVED;
+
+               if (keycode == KEY_KBDILLUMTOGGLE)
+                       keymap[i].type = KE_IGNORE;
+               else
+                       keymap[i].type = KE_KEY;
+               keymap[i].code = bios_entry->scancode;
+               keymap[i].keycode = keycode;
        }
 
        keymap[hotkey_num].type = KE_END;
index 5a54d35a61de6e4a8abc50f8619c9fa150ca24b7..844c2096bde9244c3daaf504892f2bbcefad3db0 100644 (file)
@@ -417,8 +417,7 @@ static ssize_t cpufv_disabled_store(struct device *dev,
        switch (value) {
        case 0:
                if (eeepc->cpufv_disabled)
-                       pr_warn("cpufv enabled (not officially supported "
-                               "on this model)\n");
+                       pr_warn("cpufv enabled (not officially supported on this model)\n");
                eeepc->cpufv_disabled = false;
                return count;
        case 1:
@@ -580,59 +579,58 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
        mutex_lock(&eeepc->hotplug_lock);
        pci_lock_rescan_remove();
 
-       if (eeepc->hotplug_slot) {
-               port = acpi_get_pci_dev(handle);
-               if (!port) {
-                       pr_warning("Unable to find port\n");
-                       goto out_unlock;
-               }
+       if (!eeepc->hotplug_slot)
+               goto out_unlock;
 
-               bus = port->subordinate;
+       port = acpi_get_pci_dev(handle);
+       if (!port) {
+               pr_warning("Unable to find port\n");
+               goto out_unlock;
+       }
 
-               if (!bus) {
-                       pr_warn("Unable to find PCI bus 1?\n");
-                       goto out_put_dev;
-               }
+       bus = port->subordinate;
 
-               if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
-                       pr_err("Unable to read PCI config space?\n");
-                       goto out_put_dev;
-               }
+       if (!bus) {
+               pr_warn("Unable to find PCI bus 1?\n");
+               goto out_put_dev;
+       }
+
+       if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
+               pr_err("Unable to read PCI config space?\n");
+               goto out_put_dev;
+       }
 
-               absent = (l == 0xffffffff);
+       absent = (l == 0xffffffff);
 
-               if (blocked != absent) {
-                       pr_warn("BIOS says wireless lan is %s, "
-                               "but the pci device is %s\n",
-                               blocked ? "blocked" : "unblocked",
-                               absent ? "absent" : "present");
-                       pr_warn("skipped wireless hotplug as probably "
-                               "inappropriate for this model\n");
+       if (blocked != absent) {
+               pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
+                       blocked ? "blocked" : "unblocked",
+                       absent ? "absent" : "present");
+               pr_warn("skipped wireless hotplug as probably inappropriate for this model\n");
+               goto out_put_dev;
+       }
+
+       if (!blocked) {
+               dev = pci_get_slot(bus, 0);
+               if (dev) {
+                       /* Device already present */
+                       pci_dev_put(dev);
                        goto out_put_dev;
                }
-
-               if (!blocked) {
-                       dev = pci_get_slot(bus, 0);
-                       if (dev) {
-                               /* Device already present */
-                               pci_dev_put(dev);
-                               goto out_put_dev;
-                       }
-                       dev = pci_scan_single_device(bus, 0);
-                       if (dev) {
-                               pci_bus_assign_resources(bus);
-                               pci_bus_add_device(dev);
-                       }
-               } else {
-                       dev = pci_get_slot(bus, 0);
-                       if (dev) {
-                               pci_stop_and_remove_bus_device(dev);
-                               pci_dev_put(dev);
-                       }
+               dev = pci_scan_single_device(bus, 0);
+               if (dev) {
+                       pci_bus_assign_resources(bus);
+                       pci_bus_add_device(dev);
+               }
+       } else {
+               dev = pci_get_slot(bus, 0);
+               if (dev) {
+                       pci_stop_and_remove_bus_device(dev);
+                       pci_dev_put(dev);
                }
-out_put_dev:
-               pci_dev_put(port);
        }
+out_put_dev:
+       pci_dev_put(port);
 
 out_unlock:
        pci_unlock_rescan_remove();
@@ -821,11 +819,15 @@ static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
        return 0;
 }
 
+static char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5";
+static char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6";
+static char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7";
+
 static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
 {
-       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
-       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
-       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+       eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
+       eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
+       eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
        if (eeepc->wlan_rfkill) {
                rfkill_unregister(eeepc->wlan_rfkill);
                rfkill_destroy(eeepc->wlan_rfkill);
@@ -897,9 +899,9 @@ static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
        if (result == -EBUSY)
                result = 0;
 
-       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
-       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
-       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+       eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
+       eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
+       eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
 
 exit:
        if (result && result != -ENODEV)
@@ -915,7 +917,7 @@ static int eeepc_hotk_thaw(struct device *device)
        struct eeepc_laptop *eeepc = dev_get_drvdata(device);
 
        if (eeepc->wlan_rfkill) {
-               bool wlan;
+               int wlan;
 
                /*
                 * Work around bios bug - acpi _PTS turns off the wireless led
@@ -923,7 +925,8 @@ static int eeepc_hotk_thaw(struct device *device)
                 * we should kick it ourselves in case hibernation is aborted.
                 */
                wlan = get_acpi(eeepc, CM_ASL_WLAN);
-               set_acpi(eeepc, CM_ASL_WLAN, wlan);
+               if (wlan >= 0)
+                       set_acpi(eeepc, CM_ASL_WLAN, wlan);
        }
 
        return 0;
@@ -935,9 +938,9 @@ static int eeepc_hotk_restore(struct device *device)
 
        /* Refresh both wlan rfkill state and pci hotplug */
        if (eeepc->wlan_rfkill) {
-               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P5");
-               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P6");
-               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P7");
+               eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1);
+               eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2);
+               eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3);
        }
 
        if (eeepc->bluetooth_rfkill)
@@ -977,18 +980,28 @@ static struct platform_driver platform_driver = {
 #define EEEPC_EC_SFB0      0xD0
 #define EEEPC_EC_FAN_CTRL  (EEEPC_EC_SFB0 + 3) /* Byte containing SF25  */
 
+static inline int eeepc_pwm_to_lmsensors(int value)
+{
+       return value * 255 / 100;
+}
+
+static inline int eeepc_lmsensors_to_pwm(int value)
+{
+       value = clamp_val(value, 0, 255);
+       return value * 100 / 255;
+}
+
 static int eeepc_get_fan_pwm(void)
 {
        u8 value = 0;
 
        ec_read(EEEPC_EC_FAN_PWM, &value);
-       return value * 255 / 100;
+       return eeepc_pwm_to_lmsensors(value);
 }
 
 static void eeepc_set_fan_pwm(int value)
 {
-       value = clamp_val(value, 0, 255);
-       value = value * 100 / 255;
+       value = eeepc_lmsensors_to_pwm(value);
        ec_write(EEEPC_EC_FAN_PWM, value);
 }
 
@@ -1002,15 +1015,19 @@ static int eeepc_get_fan_rpm(void)
        return high << 8 | low;
 }
 
+#define EEEPC_EC_FAN_CTRL_BIT  0x02
+#define EEEPC_FAN_CTRL_MANUAL  1
+#define EEEPC_FAN_CTRL_AUTO    2
+
 static int eeepc_get_fan_ctrl(void)
 {
        u8 value = 0;
 
        ec_read(EEEPC_EC_FAN_CTRL, &value);
-       if (value & 0x02)
-               return 1; /* manual */
+       if (value & EEEPC_EC_FAN_CTRL_BIT)
+               return EEEPC_FAN_CTRL_MANUAL;
        else
-               return 2; /* automatic */
+               return EEEPC_FAN_CTRL_AUTO;
 }
 
 static void eeepc_set_fan_ctrl(int manual)
@@ -1018,10 +1035,10 @@ static void eeepc_set_fan_ctrl(int manual)
        u8 value = 0;
 
        ec_read(EEEPC_EC_FAN_CTRL, &value);
-       if (manual == 1)
-               value |= 0x02;
+       if (manual == EEEPC_FAN_CTRL_MANUAL)
+               value |= EEEPC_EC_FAN_CTRL_BIT;
        else
-               value &= ~0x02;
+               value &= ~EEEPC_EC_FAN_CTRL_BIT;
        ec_write(EEEPC_EC_FAN_CTRL, value);
 }
 
@@ -1156,8 +1173,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
 
 static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
 {
-       if (eeepc->backlight_device)
-               backlight_device_unregister(eeepc->backlight_device);
+       backlight_device_unregister(eeepc->backlight_device);
        eeepc->backlight_device = NULL;
 }
 
@@ -1216,7 +1232,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
 static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
 {
        if (!eeepc->inputdev)
-               return ;
+               return;
        if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))
                pr_info("Unknown key %x pressed\n", event);
 }
@@ -1224,6 +1240,7 @@ static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
 static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
 {
        struct eeepc_laptop *eeepc = acpi_driver_data(device);
+       int old_brightness, new_brightness;
        u16 count;
 
        if (event > ACPI_MAX_SYS_NOTIFY)
@@ -1234,34 +1251,32 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
                                        count);
 
        /* Brightness events are special */
-       if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
-
-               /* Ignore them completely if the acpi video driver is used */
-               if (eeepc->backlight_device != NULL) {
-                       int old_brightness, new_brightness;
-
-                       /* Update the backlight device. */
-                       old_brightness = eeepc_backlight_notify(eeepc);
-
-                       /* Convert event to keypress (obsolescent hack) */
-                       new_brightness = event - NOTIFY_BRN_MIN;
-
-                       if (new_brightness < old_brightness) {
-                               event = NOTIFY_BRN_MIN; /* brightness down */
-                       } else if (new_brightness > old_brightness) {
-                               event = NOTIFY_BRN_MAX; /* brightness up */
-                       } else {
-                               /*
-                               * no change in brightness - already at min/max,
-                               * event will be desired value (or else ignored)
-                               */
-                       }
-                       eeepc_input_notify(eeepc, event);
-               }
-       } else {
-               /* Everything else is a bona-fide keypress event */
+       if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) {
                eeepc_input_notify(eeepc, event);
+               return;
+       }
+
+       /* Ignore them completely if the acpi video driver is used */
+       if (!eeepc->backlight_device)
+               return;
+
+       /* Update the backlight device. */
+       old_brightness = eeepc_backlight_notify(eeepc);
+
+       /* Convert event to keypress (obsolescent hack) */
+       new_brightness = event - NOTIFY_BRN_MIN;
+
+       if (new_brightness < old_brightness) {
+               event = NOTIFY_BRN_MIN; /* brightness down */
+       } else if (new_brightness > old_brightness) {
+               event = NOTIFY_BRN_MAX; /* brightness up */
+       } else {
+               /*
+                * no change in brightness - already at min/max,
+                * event will be desired value (or else ignored)
+                */
        }
+       eeepc_input_notify(eeepc, event);
 }
 
 static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
@@ -1293,8 +1308,8 @@ static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
         */
        if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
                eeepc->cpufv_disabled = true;
-               pr_info("model %s does not officially support setting cpu "
-                       "speed\n", model);
+               pr_info("model %s does not officially support setting cpu speed\n",
+                       model);
                pr_info("cpufv disabled to avoid instability\n");
        }
 
@@ -1320,8 +1335,8 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
           Check if cm_getv[cm] works and, if yes, assume cm should be set. */
        if (!(eeepc->cm_supported & (1 << cm))
            && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
-               pr_info("%s (%x) not reported by BIOS,"
-                       " enabling anyway\n", name, 1 << cm);
+               pr_info("%s (%x) not reported by BIOS, enabling anyway\n",
+                       name, 1 << cm);
                eeepc->cm_supported |= 1 << cm;
        }
 }
index be55bd78b5036c547dface3fbd2995a7acb47323..7c21c1c44dfa9acc05bcdf235f12fff4fd8969b0 100644 (file)
@@ -1153,8 +1153,7 @@ fail_hotkey1:
 fail_hotkey:
        platform_driver_unregister(&fujitsupf_driver);
 fail_backlight:
-       if (fujitsu->bl_device)
-               backlight_device_unregister(fujitsu->bl_device);
+       backlight_device_unregister(fujitsu->bl_device);
 fail_sysfs_group:
        sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
                           &fujitsupf_attribute_group);
@@ -1178,8 +1177,7 @@ static void __exit fujitsu_cleanup(void)
 
        platform_driver_unregister(&fujitsupf_driver);
 
-       if (fujitsu->bl_device)
-               backlight_device_unregister(fujitsu->bl_device);
+       backlight_device_unregister(fujitsu->bl_device);
 
        sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
                           &fujitsupf_attribute_group);
index 415348fc1210367ec52eec232b22c5d195a60572..4e4cc8bd7557d75f4c7aa76ffd2ed734ad54699a 100644 (file)
@@ -85,6 +85,9 @@ static int hpwl_add(struct acpi_device *device)
        int err;
 
        err = hp_wireless_input_setup();
+       if (err)
+               pr_err("Failed to setup hp wireless hotkeys\n");
+
        return err;
 }
 
index 6bec745b6b92dc6e3f849a12da003b639d1900ed..10ce6cba4455c206e8ab23906db520b880d4a95a 100644 (file)
@@ -246,6 +246,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
        AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap),
        AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
        AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
+       AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted),
        { NULL, }
 /* Laptop models without axis info (yet):
  * "NC6910" "HP Compaq 6910"
index c860eace1ce3fe56c3636330bbb8e4aa992c16dc..b3d419a8472341dabee36c3cb40765157be443a9 100644 (file)
@@ -729,8 +729,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
 
 static void ideapad_backlight_exit(struct ideapad_private *priv)
 {
-       if (priv->blightdev)
-               backlight_device_unregister(priv->blightdev);
+       backlight_device_unregister(priv->blightdev);
        priv->blightdev = NULL;
 }
 
index ecd36e332c3c163eaccf362b304cd20f9319aaf6..e2065e06a3f308ab7050d714edea381572debc71 100644 (file)
@@ -33,7 +33,7 @@
  * performance by allocating more power or thermal budget to the CPU or GPU
  * based on available headroom and activity.
  *
- * The basic algorithm is driven by a 5s moving average of tempurature.  If
+ * The basic algorithm is driven by a 5s moving average of temperature.  If
  * thermal headroom is available, the CPU and/or GPU power clamps may be
  * adjusted upwards.  If we hit the thermal ceiling or a thermal trigger,
  * we scale back the clamp.  Aside from trigger events (when we're critically
index 0afaaef5711fb6e2b0c489ed480e09bfdf1de275..a4a4258f613447a83bd90bfcb9408aa5c557c39d 100644 (file)
@@ -271,8 +271,7 @@ static int oaktrail_backlight_init(void)
 
 static void oaktrail_backlight_exit(void)
 {
-       if (oaktrail_bl_device)
-               backlight_device_unregister(oaktrail_bl_device);
+       backlight_device_unregister(oaktrail_bl_device);
 }
 
 static int oaktrail_probe(struct platform_device *pdev)
index a3f06cb1063fda0655da8ca60f715d6cc35486c0..085987730aabbe6ac6e15f588ad11b04ce311e39 100644 (file)
@@ -820,7 +820,7 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
 {
        static bool extended;
 
-       if (str & 0x20)
+       if (str & I8042_STR_AUXDATA)
                return false;
 
        /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
index 70222f265f680db68f124e645102297c32c93064..6d2bac0c463cd41f17eef2e4cfe41e16cc4f2658 100644 (file)
@@ -354,8 +354,7 @@ static void __exit msi_wmi_exit(void)
                sparse_keymap_free(msi_wmi_input_dev);
                input_unregister_device(msi_wmi_input_dev);
        }
-       if (backlight)
-               backlight_device_unregister(backlight);
+       backlight_device_unregister(backlight);
 }
 
 module_init(msi_wmi_init);
index a1a0fd72e9bf671516544c65421c175d85d12c92..6dd1c0e7dcd9af81a43db55f0c4ba87956e37dde 100644 (file)
@@ -3140,8 +3140,7 @@ static void sony_nc_backlight_setup(void)
 
 static void sony_nc_backlight_cleanup(void)
 {
-       if (sony_bl_props.dev)
-               backlight_device_unregister(sony_bl_props.dev);
+       backlight_device_unregister(sony_bl_props.dev);
 }
 
 static int sony_nc_add(struct acpi_device *device)
@@ -3716,8 +3715,7 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
        dev->event_types = type2_events;
 
 out:
-       if (pcidev)
-               pci_dev_put(pcidev);
+       pci_dev_put(pcidev);
 
        pr_info("detected Type%d model\n",
                dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
index 6414cfe5d8489e951d343b853fb5a6bcc3103299..c3d11fabc46f21c98c6122497958837ae291b171 100644 (file)
@@ -6557,6 +6557,17 @@ static struct ibm_struct brightness_driver_data = {
  * bits 3-0 (volume).  Other bits in NVRAM may have other functions,
  * such as bit 7 which is used to detect repeated presses of MUTE,
  * and we leave them unchanged.
+ *
+ * On newer Lenovo ThinkPads, the EC can automatically change the volume
+ * in response to user input.  Unfortunately, this rarely works well.
+ * The laptop changes the state of its internal MUTE gate and, on some
+ * models, sends KEY_MUTE, causing any user code that responds to the
+ * mute button to get confused.  The hardware MUTE gate is also
+ * unnecessary, since user code can handle the mute button without
+ * kernel or EC help.
+ *
+ * To avoid confusing userspace, we simply disable all EC-based mute
+ * and volume controls when possible.
  */
 
 #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
@@ -6611,11 +6622,21 @@ enum tpacpi_volume_capabilities {
        TPACPI_VOL_CAP_MAX
 };
 
+enum tpacpi_mute_btn_mode {
+       TP_EC_MUTE_BTN_LATCH  = 0,      /* Mute mutes; up/down unmutes */
+       /* We don't know what mode 1 is. */
+       TP_EC_MUTE_BTN_NONE   = 2,      /* Mute and up/down are just keys */
+       TP_EC_MUTE_BTN_TOGGLE = 3,      /* Mute toggles; up/down unmutes */
+};
+
 static enum tpacpi_volume_access_mode volume_mode =
        TPACPI_VOL_MODE_MAX;
 
 static enum tpacpi_volume_capabilities volume_capabilities;
 static bool volume_control_allowed;
+static bool software_mute_requested = true;
+static bool software_mute_active;
+static int software_mute_orig_mode;
 
 /*
  * Used to syncronize writers to TP_EC_AUDIO and
@@ -6633,6 +6654,8 @@ static void tpacpi_volume_checkpoint_nvram(void)
                return;
        if (!volume_control_allowed)
                return;
+       if (software_mute_active)
+               return;
 
        vdbg_printk(TPACPI_DBG_MIXER,
                "trying to checkpoint mixer state to NVRAM...\n");
@@ -6694,6 +6717,12 @@ static int volume_set_status_ec(const u8 status)
 
        dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status);
 
+       /*
+        * On X200s, and possibly on others, it can take a while for
+        * reads to become correct.
+        */
+       msleep(1);
+
        return 0;
 }
 
@@ -6776,6 +6805,57 @@ unlock:
        return rc;
 }
 
+static int volume_set_software_mute(bool startup)
+{
+       int result;
+
+       if (!tpacpi_is_lenovo())
+               return -ENODEV;
+
+       if (startup) {
+               if (!acpi_evalf(ec_handle, &software_mute_orig_mode,
+                               "HAUM", "qd"))
+                       return -EIO;
+
+               dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+                           "Initial HAUM setting was %d\n",
+                           software_mute_orig_mode);
+       }
+
+       if (!acpi_evalf(ec_handle, &result, "SAUM", "qdd",
+                       (int)TP_EC_MUTE_BTN_NONE))
+               return -EIO;
+
+       if (result != TP_EC_MUTE_BTN_NONE)
+               pr_warn("Unexpected SAUM result %d\n",
+                       result);
+
+       /*
+        * In software mute mode, the standard codec controls take
+        * precendence, so we unmute the ThinkPad HW switch at
+        * startup.  Just on case there are SAUM-capable ThinkPads
+        * with level controls, set max HW volume as well.
+        */
+       if (tp_features.mixer_no_level_control)
+               result = volume_set_mute(false);
+       else
+               result = volume_set_status(TP_EC_VOLUME_MAX);
+
+       if (result != 0)
+               pr_warn("Failed to unmute the HW mute switch\n");
+
+       return 0;
+}
+
+static void volume_exit_software_mute(void)
+{
+       int r;
+
+       if (!acpi_evalf(ec_handle, &r, "SAUM", "qdd", software_mute_orig_mode)
+           || r != software_mute_orig_mode)
+               pr_warn("Failed to restore mute mode\n");
+}
+
 static int volume_alsa_set_volume(const u8 vol)
 {
        dbg_printk(TPACPI_DBG_MIXER,
@@ -6883,7 +6963,12 @@ static void volume_suspend(void)
 
 static void volume_resume(void)
 {
-       volume_alsa_notify_change();
+       if (software_mute_active) {
+               if (volume_set_software_mute(false) < 0)
+                       pr_warn("Failed to restore software mute\n");
+       } else {
+               volume_alsa_notify_change();
+       }
 }
 
 static void volume_shutdown(void)
@@ -6899,6 +6984,9 @@ static void volume_exit(void)
        }
 
        tpacpi_volume_checkpoint_nvram();
+
+       if (software_mute_active)
+               volume_exit_software_mute();
 }
 
 static int __init volume_create_alsa_mixer(void)
@@ -7083,16 +7171,20 @@ static int __init volume_init(struct ibm_init_struct *iibm)
                        "mute is supported, volume control is %s\n",
                        str_supported(!tp_features.mixer_no_level_control));
 
-       rc = volume_create_alsa_mixer();
-       if (rc) {
-               pr_err("Could not create the ALSA mixer interface\n");
-               return rc;
-       }
+       if (software_mute_requested && volume_set_software_mute(true) == 0) {
+               software_mute_active = true;
+       } else {
+               rc = volume_create_alsa_mixer();
+               if (rc) {
+                       pr_err("Could not create the ALSA mixer interface\n");
+                       return rc;
+               }
 
-       pr_info("Console audio control enabled, mode: %s\n",
-               (volume_control_allowed) ?
-                       "override (read/write)" :
-                       "monitor (read only)");
+               pr_info("Console audio control enabled, mode: %s\n",
+                       (volume_control_allowed) ?
+                               "override (read/write)" :
+                               "monitor (read only)");
+       }
 
        vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
                "registering volume hotkeys as change notification\n");
@@ -9089,6 +9181,10 @@ MODULE_PARM_DESC(volume_control,
                 "Enables software override for the console audio "
                 "control when true");
 
+module_param_named(software_mute, software_mute_requested, bool, 0444);
+MODULE_PARM_DESC(software_mute,
+                "Request full software mute control");
+
 /* ALSA module API parameters */
 module_param_named(index, alsa_index, int, 0444);
 MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer");
index ab6151f054204c46063ad824b14985c97e3b934c..fc34a71866ed067624c2a851c29a182e9be48f08 100644 (file)
@@ -186,6 +186,7 @@ static struct toshiba_acpi_dev *toshiba_acpi;
 
 static const struct acpi_device_id toshiba_device_ids[] = {
        {"TOS6200", 0},
+       {"TOS6207", 0},
        {"TOS6208", 0},
        {"TOS1900", 0},
        {"", 0},
@@ -928,9 +929,7 @@ static int lcd_proc_open(struct inode *inode, struct file *file)
 
 static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
 {
-       u32 in[TCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 };
-       u32 out[TCI_WORDS];
-       acpi_status status;
+       u32 hci_result;
 
        if (dev->tr_backlight_supported) {
                bool enable = !value;
@@ -941,20 +940,9 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
                        value--;
        }
 
-       in[2] = value << HCI_LCD_BRIGHTNESS_SHIFT;
-       status = tci_raw(dev, in, out);
-       if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
-               pr_err("ACPI call to set brightness failed");
-               return -EIO;
-       }
-       /* Extra check for "incomplete" backlight method, where the AML code
-        * doesn't check for HCI_SET or HCI_GET and returns TOS_SUCCESS,
-        * the actual brightness, and in some cases the max brightness.
-        */
-       if (out[2] > 0  || out[3] == 0xE000)
-               return -ENODEV;
-
-       return out[0] == TOS_SUCCESS ? 0 : -EIO;
+       value = value << HCI_LCD_BRIGHTNESS_SHIFT;
+       hci_result = hci_write1(dev, HCI_LCD_BRIGHTNESS, value);
+       return hci_result == TOS_SUCCESS ? 0 : -EIO;
 }
 
 static int set_lcd_status(struct backlight_device *bd)
@@ -1406,12 +1394,6 @@ static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
                if (ret)
                        return ret;
 
-               /* Update sysfs entries on successful mode change*/
-               ret = sysfs_update_group(&toshiba->acpi_dev->dev.kobj,
-                                        &toshiba_attr_group);
-               if (ret)
-                       return ret;
-
                toshiba->kbd_mode = mode;
        }
 
@@ -1586,10 +1568,32 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
        return exists ? attr->mode : 0;
 }
 
+/*
+ * Hotkeys
+ */
+static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
+{
+       acpi_status status;
+       u32 result;
+
+       status = acpi_evaluate_object(dev->acpi_dev->handle,
+                                     "ENAB", NULL, NULL);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
+       if (result == TOS_FAILURE)
+               return -EIO;
+       else if (result == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       return 0;
+}
+
 static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
                                      struct serio *port)
 {
-       if (str & 0x20)
+       if (str & I8042_STR_AUXDATA)
                return false;
 
        if (unlikely(data == 0xe0))
@@ -1648,9 +1652,45 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
                pr_info("Unknown key %x\n", scancode);
 }
 
+static void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev)
+{
+       u32 hci_result, value;
+       int retries = 3;
+       int scancode;
+
+       if (dev->info_supported) {
+               scancode = toshiba_acpi_query_hotkey(dev);
+               if (scancode < 0)
+                       pr_err("Failed to query hotkey event\n");
+               else if (scancode != 0)
+                       toshiba_acpi_report_hotkey(dev, scancode);
+       } else if (dev->system_event_supported) {
+               do {
+                       hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
+                       switch (hci_result) {
+                       case TOS_SUCCESS:
+                               toshiba_acpi_report_hotkey(dev, (int)value);
+                               break;
+                       case TOS_NOT_SUPPORTED:
+                               /*
+                                * This is a workaround for an unresolved
+                                * issue on some machines where system events
+                                * sporadically become disabled.
+                                */
+                               hci_result =
+                                       hci_write1(dev, HCI_SYSTEM_EVENT, 1);
+                               pr_notice("Re-enabled hotkeys\n");
+                               /* fall through */
+                       default:
+                               retries--;
+                               break;
+                       }
+               } while (retries && hci_result != TOS_FIFO_EMPTY);
+       }
+}
+
 static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
 {
-       acpi_status status;
        acpi_handle ec_handle;
        int error;
        u32 hci_result;
@@ -1677,7 +1717,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
         * supported, so if it's present set up an i8042 key filter
         * for this purpose.
         */
-       status = AE_ERROR;
        ec_handle = ec_get_handle();
        if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
                INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
@@ -1708,10 +1747,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
                goto err_remove_filter;
        }
 
-       status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
-       if (ACPI_FAILURE(status)) {
+       error = toshiba_acpi_enable_hotkeys(dev);
+       if (error) {
                pr_info("Unable to enable hotkeys\n");
-               error = -ENODEV;
                goto err_remove_filter;
        }
 
@@ -1721,7 +1759,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
                goto err_remove_filter;
        }
 
-       hci_result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
        return 0;
 
  err_remove_filter:
@@ -1810,8 +1847,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
                rfkill_destroy(dev->bt_rfk);
        }
 
-       if (dev->backlight_dev)
-               backlight_device_unregister(dev->backlight_dev);
+       backlight_device_unregister(dev->backlight_dev);
 
        if (dev->illumination_supported)
                led_classdev_unregister(&dev->led_dev);
@@ -1967,41 +2003,29 @@ error:
 static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
 {
        struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
-       u32 hci_result, value;
-       int retries = 3;
-       int scancode;
-
-       if (event != 0x80)
-               return;
+       int ret;
 
-       if (dev->info_supported) {
-               scancode = toshiba_acpi_query_hotkey(dev);
-               if (scancode < 0)
-                       pr_err("Failed to query hotkey event\n");
-               else if (scancode != 0)
-                       toshiba_acpi_report_hotkey(dev, scancode);
-       } else if (dev->system_event_supported) {
-               do {
-                       hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
-                       switch (hci_result) {
-                       case TOS_SUCCESS:
-                               toshiba_acpi_report_hotkey(dev, (int)value);
-                               break;
-                       case TOS_NOT_SUPPORTED:
-                               /*
-                                * This is a workaround for an unresolved
-                                * issue on some machines where system events
-                                * sporadically become disabled.
-                                */
-                               hci_result =
-                                       hci_write1(dev, HCI_SYSTEM_EVENT, 1);
-                               pr_notice("Re-enabled hotkeys\n");
-                               /* fall through */
-                       default:
-                               retries--;
-                               break;
-                       }
-               } while (retries && hci_result != TOS_FIFO_EMPTY);
+       switch (event) {
+       case 0x80: /* Hotkeys and some system events */
+               toshiba_acpi_process_hotkeys(dev);
+               break;
+       case 0x92: /* Keyboard backlight mode changed */
+               /* Update sysfs entries */
+               ret = sysfs_update_group(&acpi_dev->dev.kobj,
+                                        &toshiba_attr_group);
+               if (ret)
+                       pr_err("Unable to update sysfs entries\n");
+               break;
+       case 0x81: /* Unknown */
+       case 0x82: /* Unknown */
+       case 0x83: /* Unknown */
+       case 0x8c: /* Unknown */
+       case 0x8e: /* Unknown */
+       case 0x8f: /* Unknown */
+       case 0x90: /* Unknown */
+       default:
+               pr_info("Unknown event received %x\n", event);
+               break;
        }
 }
 
@@ -2020,16 +2044,12 @@ static int toshiba_acpi_suspend(struct device *device)
 static int toshiba_acpi_resume(struct device *device)
 {
        struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
-       u32 result;
-       acpi_status status;
+       int error;
 
        if (dev->hotkey_dev) {
-               status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB",
-                               NULL, NULL);
-               if (ACPI_FAILURE(status))
+               error = toshiba_acpi_enable_hotkeys(dev);
+               if (error)
                        pr_info("Unable to re-enable hotkeys\n");
-
-               result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
        }
 
        return 0;
This page took 0.12044 seconds and 5 git commands to generate.