fujitsu-laptop: Support radio LED
authorMichał Kępień <kernel@kempniu.pl>
Tue, 12 Apr 2016 12:36:34 +0000 (22:06 +0930)
committerDarren Hart <dvhart@linux.intel.com>
Fri, 15 Apr 2016 07:31:09 +0000 (00:31 -0700)
Lifebook E734/E744/E754 has a LED which the manual calls "radio
components indicator".  It should be lit when any radio transmitter is
enabled.  Its state can be read and set using ACPI (FUNC interface,
RFKILL method).

Since the Lifebook E734/E744/E754 only has a button (as compared to a
slider) for enabling/disabling radio transmitters, I believe the LED in
question is meant to indicate whether all radio transmitters are
currently on or off.  However, pressing the radio toggle button does not
automatically change the hardware state of the transmitters: it looks
like this machine relies on soft rfkill.

As for detecting whether the LED is present on a given machine, I had to
resort to educated guesswork.  I assumed this LED is present on all
devices which have a radio toggle button instead of a slider.  My
Lifebook E744 holds 0x01010001 in BTNI.  By comparing the bits and
buttons with those of a Lifebook E8420 (BTNI=0x000F0101, has a slider),
I put my money on bit 24 as the indicator of the radio toggle button
being present.  Furthermore, bit 24 is also clear on the S7020 which
does not have the toggle button or an RF LED.

Figuring out how the LED is controlled was more deterministic as all it
took was decompiling the DSDT and taking a look at method S000 (the
RFKILL method of the FUNC interface).

The LED control method implemented here is unsuitable for use with
"heavy" LED triggers, like phy0rx.  Once blinking frequency achieves a
certain level, the system hangs.

Signed-off-by: Michał Kępień <kernel@kempniu.pl>
[jwoithe: Comment on bit 24 in BTNI, expanded commit msg]
Signed-off-by: Jonathan Woithe <jwoithe@just42.net>
[dvhart: Minor style and commit log adjustments]
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
drivers/platform/x86/fujitsu-laptop.c

index ffc84cc7b1c79fc8b8bdecd5cd2ae1201b338194..1cb39d00d01411dc05b6972832598db4b2eaedad 100644 (file)
 #define KEYBOARD_LAMPS 0x100
 #define LOGOLAMP_POWERON 0x2000
 #define LOGOLAMP_ALWAYS  0x4000
+#define RADIO_LED_ON   0x20
 #endif
 
 /* Hotkey details */
@@ -174,6 +175,7 @@ struct fujitsu_hotkey_t {
        int rfkill_state;
        int logolamp_registered;
        int kblamps_registered;
+       int radio_led_registered;
 };
 
 static struct fujitsu_hotkey_t *fujitsu_hotkey;
@@ -200,6 +202,16 @@ static struct led_classdev kblamps_led = {
  .brightness_get = kblamps_get,
  .brightness_set = kblamps_set
 };
+
+static enum led_brightness radio_led_get(struct led_classdev *cdev);
+static void radio_led_set(struct led_classdev *cdev,
+                              enum led_brightness brightness);
+
+static struct led_classdev radio_led = {
+ .name = "fujitsu::radio_led",
+ .brightness_get = radio_led_get,
+ .brightness_set = radio_led_set
+};
 #endif
 
 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
@@ -275,6 +287,15 @@ static void kblamps_set(struct led_classdev *cdev,
                call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
 }
 
+static void radio_led_set(struct led_classdev *cdev,
+                               enum led_brightness brightness)
+{
+       if (brightness >= LED_FULL)
+               call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, RADIO_LED_ON);
+       else
+               call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
+}
+
 static enum led_brightness logolamp_get(struct led_classdev *cdev)
 {
        enum led_brightness brightness = LED_OFF;
@@ -299,6 +320,16 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev)
 
        return brightness;
 }
+
+static enum led_brightness radio_led_get(struct led_classdev *cdev)
+{
+       enum led_brightness brightness = LED_OFF;
+
+       if (call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0) & RADIO_LED_ON)
+               brightness = LED_FULL;
+
+       return brightness;
+}
 #endif
 
 /* Hardware access for LCD brightness control */
@@ -895,6 +926,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
                               result);
                }
        }
+
+       /*
+        * BTNI bit 24 seems to indicate the presence of a radio toggle
+        * button in place of a slide switch, and all such machines appear
+        * to also have an RF LED.  Therefore use bit 24 as an indicator
+        * that an RF LED is present.
+        */
+       if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
+               result = led_classdev_register(&fujitsu->pf_device->dev,
+                                               &radio_led);
+               if (result == 0) {
+                       fujitsu_hotkey->radio_led_registered = 1;
+               } else {
+                       pr_err("Could not register LED handler for radio LED, error %i\n",
+                              result);
+               }
+       }
 #endif
 
        return result;
@@ -921,6 +969,9 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
 
        if (fujitsu_hotkey->kblamps_registered)
                led_classdev_unregister(&kblamps_led);
+
+       if (fujitsu_hotkey->radio_led_registered)
+               led_classdev_unregister(&radio_led);
 #endif
 
        input_unregister_device(input);