]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - drivers/platform/x86/toshiba_acpi.c
Merge tag 'for-4.2' of git://git.infradead.org/battery-2.6
[mirror_ubuntu-focal-kernel.git] / drivers / platform / x86 / toshiba_acpi.c
index dbcb7a8915b84fb8794fd4daea15825e55a49cdd..59bf27ed72d63a1e7d1fa253452081277473d5ee 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/acpi.h>
 #include <linux/dmi.h>
 #include <linux/uaccess.h>
+#include <acpi/video.h>
 
 MODULE_AUTHOR("John Belmonte");
 MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
@@ -116,6 +117,7 @@ MODULE_LICENSE("GPL");
 #define HCI_KBD_ILLUMINATION           0x0095
 #define HCI_ECO_MODE                   0x0097
 #define HCI_ACCELEROMETER2             0x00a6
+#define HCI_SYSTEM_INFO                        0xc000
 #define SCI_PANEL_POWER_ON             0x010d
 #define SCI_ILLUMINATION               0x014e
 #define SCI_USB_SLEEP_CHARGE           0x0150
@@ -129,10 +131,13 @@ MODULE_LICENSE("GPL");
 #define HCI_ACCEL_MASK                 0x7fff
 #define HCI_HOTKEY_DISABLE             0x0b
 #define HCI_HOTKEY_ENABLE              0x09
+#define HCI_HOTKEY_SPECIAL_FUNCTIONS   0x10
 #define HCI_LCD_BRIGHTNESS_BITS                3
 #define HCI_LCD_BRIGHTNESS_SHIFT       (16-HCI_LCD_BRIGHTNESS_BITS)
 #define HCI_LCD_BRIGHTNESS_LEVELS      (1 << HCI_LCD_BRIGHTNESS_BITS)
 #define HCI_MISC_SHIFT                 0x10
+#define HCI_SYSTEM_TYPE1               0x10
+#define HCI_SYSTEM_TYPE2               0x11
 #define HCI_VIDEO_OUT_LCD              0x1
 #define HCI_VIDEO_OUT_CRT              0x2
 #define HCI_VIDEO_OUT_TV               0x4
@@ -147,9 +152,10 @@ MODULE_LICENSE("GPL");
 #define SCI_KBD_MODE_OFF               0x10
 #define SCI_KBD_TIME_MAX               0x3c001a
 #define SCI_USB_CHARGE_MODE_MASK       0xff
-#define SCI_USB_CHARGE_DISABLED                0x30000
-#define SCI_USB_CHARGE_ALTERNATE       0x30009
-#define SCI_USB_CHARGE_AUTO            0x30021
+#define SCI_USB_CHARGE_DISABLED                0x00
+#define SCI_USB_CHARGE_ALTERNATE       0x09
+#define SCI_USB_CHARGE_TYPICAL         0x11
+#define SCI_USB_CHARGE_AUTO            0x21
 #define SCI_USB_CHARGE_BAT_MASK                0x7
 #define SCI_USB_CHARGE_BAT_LVL_OFF     0x1
 #define SCI_USB_CHARGE_BAT_LVL_ON      0x4
@@ -174,6 +180,8 @@ struct toshiba_acpi_dev {
        int kbd_mode;
        int kbd_time;
        int usbsc_bat_level;
+       int usbsc_mode_base;
+       int hotkey_event_type;
 
        unsigned int illumination_supported:1;
        unsigned int video_supported:1;
@@ -243,29 +251,6 @@ static const struct key_entry toshiba_acpi_keymap[] = {
        { KE_END, 0 },
 };
 
-/* alternative keymap */
-static const struct dmi_system_id toshiba_alt_keymap_dmi[] = {
-       {
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"),
-               },
-       },
-       {
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Qosmio X75-A"),
-               },
-       },
-       {
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A50-A"),
-               },
-       },
-       {}
-};
-
 static const struct key_entry toshiba_acpi_alt_keymap[] = {
        { KE_KEY, 0x157, { KEY_MUTE } },
        { KE_KEY, 0x102, { KEY_ZOOMOUT } },
@@ -280,6 +265,14 @@ static const struct key_entry toshiba_acpi_alt_keymap[] = {
        { KE_END, 0 },
 };
 
+/*
+ * List of models which have a broken acpi-video backlight interface and thus
+ * need to use the toshiba (vendor) interface instead.
+ */
+static const struct dmi_system_id toshiba_vendor_backlight_dmi[] = {
+       {}
+};
+
 /*
  * Utility
  */
@@ -819,6 +812,54 @@ static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
 }
 
 /* Sleep (Charge and Music) utilities support */
+static void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev)
+{
+       u32 in[TCI_WORDS] = { SCI_GET, SCI_USB_SLEEP_CHARGE, 0, 0, 0, 0 };
+       u32 out[TCI_WORDS];
+       acpi_status status;
+
+       /* Set the feature to "not supported" in case of error */
+       dev->usb_sleep_charge_supported = 0;
+
+       if (!sci_open(dev))
+               return;
+
+       status = tci_raw(dev, in, out);
+       if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+               pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
+               sci_close(dev);
+               return;
+       } else if (out[0] == TOS_NOT_SUPPORTED) {
+               pr_info("USB Sleep and Charge not supported\n");
+               sci_close(dev);
+               return;
+       } else if (out[0] == TOS_SUCCESS) {
+               dev->usbsc_mode_base = out[4];
+       }
+
+       in[5] = SCI_USB_CHARGE_BAT_LVL;
+       status = tci_raw(dev, in, out);
+       if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+               pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
+               sci_close(dev);
+               return;
+       } else if (out[0] == TOS_NOT_SUPPORTED) {
+               pr_info("USB Sleep and Charge not supported\n");
+               sci_close(dev);
+               return;
+       } else if (out[0] == TOS_SUCCESS) {
+               dev->usbsc_bat_level = out[2];
+               /*
+                * If we reach this point, it means that the laptop has support
+                * for this feature and all values are initialized.
+                * Set it as supported.
+                */
+               dev->usb_sleep_charge_supported = 1;
+       }
+
+       sci_close(dev);
+}
+
 static int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev,
                                        u32 *mode)
 {
@@ -934,11 +975,11 @@ static int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev,
        status = tci_raw(dev, in, out);
        sci_close(dev);
        if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
-               pr_err("ACPI call to get USB S&C battery level failed\n");
+               pr_err("ACPI call to get USB Rapid Charge failed\n");
                return -EIO;
        } else if (out[0] == TOS_NOT_SUPPORTED ||
                   out[0] == TOS_INPUT_DATA_ERROR) {
-               pr_info("USB Sleep and Charge not supported\n");
+               pr_info("USB Rapid Charge not supported\n");
                return -ENODEV;
        }
 
@@ -962,10 +1003,10 @@ static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
        status = tci_raw(dev, in, out);
        sci_close(dev);
        if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
-               pr_err("ACPI call to set USB S&C battery level failed\n");
+               pr_err("ACPI call to set USB Rapid Charge failed\n");
                return -EIO;
        } else if (out[0] == TOS_NOT_SUPPORTED) {
-               pr_info("USB Sleep and Charge not supported\n");
+               pr_info("USB Rapid Charge not supported\n");
                return -ENODEV;
        } else if (out[0] == TOS_INPUT_DATA_ERROR) {
                return -EIO;
@@ -984,10 +1025,10 @@ static int toshiba_usb_sleep_music_get(struct toshiba_acpi_dev *dev, u32 *state)
        result = sci_read(dev, SCI_USB_SLEEP_MUSIC, state);
        sci_close(dev);
        if (result == TOS_FAILURE) {
-               pr_err("ACPI call to set USB S&C mode failed\n");
+               pr_err("ACPI call to get Sleep and Music failed\n");
                return -EIO;
        } else if (result == TOS_NOT_SUPPORTED) {
-               pr_info("USB Sleep and Charge not supported\n");
+               pr_info("Sleep and Music not supported\n");
                return -ENODEV;
        } else if (result == TOS_INPUT_DATA_ERROR) {
                return -EIO;
@@ -1006,10 +1047,10 @@ static int toshiba_usb_sleep_music_set(struct toshiba_acpi_dev *dev, u32 state)
        result = sci_write(dev, SCI_USB_SLEEP_MUSIC, state);
        sci_close(dev);
        if (result == TOS_FAILURE) {
-               pr_err("ACPI call to set USB S&C mode failed\n");
+               pr_err("ACPI call to set Sleep and Music failed\n");
                return -EIO;
        } else if (result == TOS_NOT_SUPPORTED) {
-               pr_info("USB Sleep and Charge not supported\n");
+               pr_info("Sleep and Music not supported\n");
                return -ENODEV;
        } else if (result == TOS_INPUT_DATA_ERROR) {
                return -EIO;
@@ -1149,6 +1190,28 @@ static int toshiba_usb_three_set(struct toshiba_acpi_dev *dev, u32 state)
        return 0;
 }
 
+/* Hotkey Event type */
+static int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev,
+                                        u32 *type)
+{
+       u32 val1 = 0x03;
+       u32 val2 = 0;
+       u32 result;
+
+       result = hci_read2(dev, HCI_SYSTEM_INFO, &val1, &val2);
+       if (result == TOS_FAILURE) {
+               pr_err("ACPI call to get System type failed\n");
+               return -EIO;
+       } else if (result == TOS_NOT_SUPPORTED) {
+               pr_info("System type not supported\n");
+               return -ENODEV;
+       }
+
+       *type = val2;
+
+       return 0;
+}
+
 /* Bluetooth rfkill handlers */
 
 static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
@@ -1973,17 +2036,21 @@ static ssize_t usb_sleep_charge_store(struct device *dev,
         * 0 - Disabled
         * 1 - Alternate (Non USB conformant devices that require more power)
         * 2 - Auto (USB conformant devices)
+        * 3 - Typical
         */
-       if (state != 0 && state != 1 && state != 2)
+       if (state != 0 && state != 1 && state != 2 && state != 3)
                return -EINVAL;
 
        /* Set the USB charging mode to internal value */
+       mode = toshiba->usbsc_mode_base;
        if (state == 0)
-               mode = SCI_USB_CHARGE_DISABLED;
+               mode |= SCI_USB_CHARGE_DISABLED;
        else if (state == 1)
-               mode = SCI_USB_CHARGE_ALTERNATE;
+               mode |= SCI_USB_CHARGE_ALTERNATE;
        else if (state == 2)
-               mode = SCI_USB_CHARGE_AUTO;
+               mode |= SCI_USB_CHARGE_AUTO;
+       else if (state == 3)
+               mode |= SCI_USB_CHARGE_TYPICAL;
 
        ret = toshiba_usb_sleep_charge_set(toshiba, mode);
        if (ret)
@@ -2333,6 +2400,20 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
        return 0;
 }
 
+static void toshiba_acpi_enable_special_functions(struct toshiba_acpi_dev *dev)
+{
+       u32 result;
+
+       /*
+        * Re-activate the hotkeys, but this time, we are using the
+        * "Special Functions" mode.
+        */
+       result = hci_write1(dev, HCI_HOTKEY_EVENT,
+                           HCI_HOTKEY_SPECIAL_FUNCTIONS);
+       if (result != TOS_SUCCESS)
+               pr_err("Could not enable the Special Function mode\n");
+}
+
 static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
                                      struct serio *port)
 {
@@ -2434,10 +2515,22 @@ static void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev)
 
 static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
 {
+       const struct key_entry *keymap = toshiba_acpi_keymap;
        acpi_handle ec_handle;
-       int error;
+       u32 events_type;
        u32 hci_result;
-       const struct key_entry *keymap = toshiba_acpi_keymap;
+       int error;
+
+       error = toshiba_acpi_enable_hotkeys(dev);
+       if (error)
+               return error;
+
+       error = toshiba_hotkey_event_type_get(dev, &events_type);
+       if (error) {
+               pr_err("Unable to query Hotkey Event Type\n");
+               return error;
+       }
+       dev->hotkey_event_type = events_type;
 
        dev->hotkey_dev = input_allocate_device();
        if (!dev->hotkey_dev)
@@ -2447,8 +2540,14 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
        dev->hotkey_dev->phys = "toshiba_acpi/input0";
        dev->hotkey_dev->id.bustype = BUS_HOST;
 
-       if (dmi_check_system(toshiba_alt_keymap_dmi))
+       if (events_type == HCI_SYSTEM_TYPE1 ||
+           !dev->kbd_function_keys_supported)
+               keymap = toshiba_acpi_keymap;
+       else if (events_type == HCI_SYSTEM_TYPE2 ||
+                dev->kbd_function_keys_supported)
                keymap = toshiba_acpi_alt_keymap;
+       else
+               pr_info("Unknown event type received %x\n", events_type);
        error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL);
        if (error)
                goto err_free_dev;
@@ -2490,12 +2589,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
                goto err_remove_filter;
        }
 
-       error = toshiba_acpi_enable_hotkeys(dev);
-       if (error) {
-               pr_info("Unable to enable hotkeys\n");
-               goto err_remove_filter;
-       }
-
        error = input_register_device(dev->hotkey_dev);
        if (error) {
                pr_info("Unable to register input device\n");
@@ -2541,6 +2634,17 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
        ret = get_tr_backlight_status(dev, &enabled);
        dev->tr_backlight_supported = !ret;
 
+       /*
+        * Tell acpi-video-detect code to prefer vendor backlight on all
+        * systems with transflective backlight and on dmi matched systems.
+        */
+       if (dev->tr_backlight_supported ||
+           dmi_check_system(toshiba_vendor_backlight_dmi))
+               acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
+               return 0;
+
        memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_PLATFORM;
        props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
@@ -2624,6 +2728,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
 {
        struct toshiba_acpi_dev *dev;
        const char *hci_method;
+       u32 special_functions;
        u32 dummy;
        bool bt_present;
        int ret = 0;
@@ -2648,6 +2753,16 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
        acpi_dev->driver_data = dev;
        dev_set_drvdata(&acpi_dev->dev, dev);
 
+       /* Query the BIOS for supported features */
+
+       /*
+        * The "Special Functions" are always supported by the laptops
+        * with the new keyboard layout, query for its presence to help
+        * determine the keymap layout to use.
+        */
+       ret = toshiba_function_keys_get(dev, &special_functions);
+       dev->kbd_function_keys_supported = !ret;
+
        if (toshiba_acpi_setup_keyboard(dev))
                pr_info("Unable to activate hotkeys\n");
 
@@ -2716,8 +2831,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
        ret = toshiba_accelerometer_supported(dev);
        dev->accelerometer_supported = !ret;
 
-       ret = toshiba_usb_sleep_charge_get(dev, &dummy);
-       dev->usb_sleep_charge_supported = !ret;
+       toshiba_usb_sleep_charge_available(dev);
 
        ret = toshiba_usb_rapid_charge_get(dev, &dummy);
        dev->usb_rapid_charge_supported = !ret;
@@ -2725,23 +2839,25 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
        ret = toshiba_usb_sleep_music_get(dev, &dummy);
        dev->usb_sleep_music_supported = !ret;
 
-       ret = toshiba_function_keys_get(dev, &dummy);
-       dev->kbd_function_keys_supported = !ret;
-
        ret = toshiba_panel_power_on_get(dev, &dummy);
        dev->panel_power_on_supported = !ret;
 
        ret = toshiba_usb_three_get(dev, &dummy);
        dev->usb_three_supported = !ret;
 
-       /* Determine whether or not BIOS supports fan and video interfaces */
-
        ret = get_video_status(dev, &dummy);
        dev->video_supported = !ret;
 
        ret = get_fan_status(dev, &dummy);
        dev->fan_supported = !ret;
 
+       /*
+        * Enable the "Special Functions" mode only if they are
+        * supported and if they are activated.
+        */
+       if (dev->kbd_function_keys_supported && special_functions)
+               toshiba_acpi_enable_special_functions(dev);
+
        ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
                                 &toshiba_attr_group);
        if (ret) {
@@ -2770,6 +2886,21 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
        case 0x80: /* Hotkeys and some system events */
                toshiba_acpi_process_hotkeys(dev);
                break;
+       case 0x81: /* Dock events */
+       case 0x82:
+       case 0x83:
+               pr_info("Dock event received %x\n", event);
+               break;
+       case 0x88: /* Thermal events */
+               pr_info("Thermal event received\n");
+               break;
+       case 0x8f: /* LID closed */
+       case 0x90: /* LID is closed and Dock has been ejected */
+               break;
+       case 0x8c: /* SATA power events */
+       case 0x8b:
+               pr_info("SATA power event received %x\n", event);
+               break;
        case 0x92: /* Keyboard backlight mode changed */
                /* Update sysfs entries */
                ret = sysfs_update_group(&acpi_dev->dev.kobj,
@@ -2777,17 +2908,19 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
                if (ret)
                        pr_err("Unable to update sysfs entries\n");
                break;
-       case 0x81: /* Unknown */
-       case 0x82: /* Unknown */
-       case 0x83: /* Unknown */
-       case 0x8c: /* Unknown */
+       case 0x85: /* Unknown */
+       case 0x8d: /* Unknown */
        case 0x8e: /* Unknown */
-       case 0x8f: /* Unknown */
-       case 0x90: /* Unknown */
+       case 0x94: /* Unknown */
+       case 0x95: /* Unknown */
        default:
                pr_info("Unknown event received %x\n", event);
                break;
        }
+
+       acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class,
+                                       dev_name(&acpi_dev->dev),
+                                       event, 0);
 }
 
 #ifdef CONFIG_PM_SLEEP