Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux.git] / drivers / platform / x86 / thinkpad_acpi.c
index 94bb6157c957b495a955159ea4916769f373862f..15e61c16736ef7d68c0fc439baaed760262d2b94 100644 (file)
@@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
        }
 }
 
-static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
-                                          struct tp_nvram_state *newn,
-                                          const u32 event_mask)
-{
-
 #define TPACPI_COMPARE_KEY(__scancode, __member) \
-       do { \
-               if ((event_mask & (1 << __scancode)) && \
-                   oldn->__member != newn->__member) \
-                       tpacpi_hotkey_send_key(__scancode); \
-       } while (0)
+do { \
+       if ((event_mask & (1 << __scancode)) && \
+           oldn->__member != newn->__member) \
+               tpacpi_hotkey_send_key(__scancode); \
+} while (0)
 
 #define TPACPI_MAY_SEND_KEY(__scancode) \
-       do { \
-               if (event_mask & (1 << __scancode)) \
-                       tpacpi_hotkey_send_key(__scancode); \
-       } while (0)
+do { \
+       if (event_mask & (1 << __scancode)) \
+               tpacpi_hotkey_send_key(__scancode); \
+} while (0)
 
-       void issue_volchange(const unsigned int oldvol,
-                            const unsigned int newvol)
-       {
-               unsigned int i = oldvol;
+static void issue_volchange(const unsigned int oldvol,
+                           const unsigned int newvol,
+                           const u32 event_mask)
+{
+       unsigned int i = oldvol;
 
-               while (i > newvol) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
-                       i--;
-               }
-               while (i < newvol) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
-                       i++;
-               }
+       while (i > newvol) {
+               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+               i--;
+       }
+       while (i < newvol) {
+               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+               i++;
        }
+}
 
-       void issue_brightnesschange(const unsigned int oldbrt,
-                                   const unsigned int newbrt)
-       {
-               unsigned int i = oldbrt;
+static void issue_brightnesschange(const unsigned int oldbrt,
+                                  const unsigned int newbrt,
+                                  const u32 event_mask)
+{
+       unsigned int i = oldbrt;
 
-               while (i > newbrt) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
-                       i--;
-               }
-               while (i < newbrt) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
-                       i++;
-               }
+       while (i > newbrt) {
+               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+               i--;
        }
+       while (i < newbrt) {
+               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+               i++;
+       }
+}
+
+static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
+                                          struct tp_nvram_state *newn,
+                                          const u32 event_mask)
+{
 
        TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
        TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
@@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
                    oldn->volume_level != newn->volume_level) {
                        /* recently muted, or repeated mute keypress, or
                         * multiple presses ending in mute */
-                       issue_volchange(oldn->volume_level, newn->volume_level);
+                       issue_volchange(oldn->volume_level, newn->volume_level,
+                               event_mask);
                        TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
                }
        } else {
@@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
                        TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
                }
                if (oldn->volume_level != newn->volume_level) {
-                       issue_volchange(oldn->volume_level, newn->volume_level);
+                       issue_volchange(oldn->volume_level, newn->volume_level,
+                               event_mask);
                } else if (oldn->volume_toggle != newn->volume_toggle) {
                        /* repeated vol up/down keypress at end of scale ? */
                        if (newn->volume_level == 0)
@@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
        /* handle brightness */
        if (oldn->brightness_level != newn->brightness_level) {
                issue_brightnesschange(oldn->brightness_level,
-                                      newn->brightness_level);
+                                      newn->brightness_level, event_mask);
        } else if (oldn->brightness_toggle != newn->brightness_toggle) {
                /* repeated key presses that didn't change state */
                if (newn->brightness_level == 0)
@@ -3437,6 +3441,106 @@ err_exit:
        return (res < 0)? res : 1;
 }
 
+/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
+ * mode, Web conference mode, Function mode and Lay-flat mode.
+ * We support Home mode and Function mode currently.
+ *
+ * Will consider support rest of modes in future.
+ *
+ */
+enum ADAPTIVE_KEY_MODE {
+       HOME_MODE,
+       WEB_BROWSER_MODE,
+       WEB_CONFERENCE_MODE,
+       FUNCTION_MODE,
+       LAYFLAT_MODE
+};
+
+const int adaptive_keyboard_modes[] = {
+       HOME_MODE,
+/*     WEB_BROWSER_MODE = 2,
+       WEB_CONFERENCE_MODE = 3, */
+       FUNCTION_MODE
+};
+
+#define DFR_CHANGE_ROW                 0x101
+#define DFR_SHOW_QUICKVIEW_ROW         0x102
+
+/* press Fn key a while second, it will switch to Function Mode. Then
+ * release Fn key, previous mode be restored.
+ */
+static bool adaptive_keyboard_mode_is_saved;
+static int adaptive_keyboard_prev_mode;
+
+static int adaptive_keyboard_get_next_mode(int mode)
+{
+       size_t i;
+       size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1;
+
+       for (i = 0; i <= max_mode; i++) {
+               if (adaptive_keyboard_modes[i] == mode)
+                       break;
+       }
+
+       if (i >= max_mode)
+               i = 0;
+       else
+               i++;
+
+       return adaptive_keyboard_modes[i];
+}
+
+static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
+{
+       u32 current_mode = 0;
+       int new_mode = 0;
+
+       switch (scancode) {
+       case DFR_CHANGE_ROW:
+               if (adaptive_keyboard_mode_is_saved) {
+                       new_mode = adaptive_keyboard_prev_mode;
+                       adaptive_keyboard_mode_is_saved = false;
+               } else {
+                       if (!acpi_evalf(
+                                       hkey_handle, &current_mode,
+                                       "GTRW", "dd", 0)) {
+                               pr_err("Cannot read adaptive keyboard mode\n");
+                               return false;
+                       } else {
+                               new_mode = adaptive_keyboard_get_next_mode(
+                                               current_mode);
+                       }
+               }
+
+               if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) {
+                       pr_err("Cannot set adaptive keyboard mode\n");
+                       return false;
+               }
+
+               return true;
+
+       case DFR_SHOW_QUICKVIEW_ROW:
+               if (!acpi_evalf(hkey_handle,
+                               &adaptive_keyboard_prev_mode,
+                               "GTRW", "dd", 0)) {
+                       pr_err("Cannot read adaptive keyboard mode\n");
+                       return false;
+               } else {
+                       adaptive_keyboard_mode_is_saved = true;
+
+                       if (!acpi_evalf(hkey_handle,
+                                       NULL, "STRW", "vd", FUNCTION_MODE)) {
+                               pr_err("Cannot set adaptive keyboard mode\n");
+                               return false;
+                       }
+               }
+               return true;
+
+       default:
+               return false;
+       }
+}
+
 static bool hotkey_notify_hotkey(const u32 hkey,
                                 bool *send_acpi_ev,
                                 bool *ignore_acpi_ev)
@@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
                        *ignore_acpi_ev = true;
                }
                return true;
+       } else {
+               return adaptive_keyboard_hotkey_notify_hotkey(scancode);
        }
        return false;
 }
@@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
 
 static void hotkey_suspend(void)
 {
+       int hkeyv;
+
        /* Do these on suspend, we get the events on early resume! */
        hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
        hotkey_autosleep_ack = 0;
+
+       /* save previous mode of adaptive keyboard of X1 Carbon */
+       if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+               if ((hkeyv >> 8) == 2) {
+                       if (!acpi_evalf(hkey_handle,
+                                               &adaptive_keyboard_prev_mode,
+                                               "GTRW", "dd", 0)) {
+                               pr_err("Cannot read adaptive keyboard mode.\n");
+                       }
+               }
+       }
 }
 
 static void hotkey_resume(void)
 {
+       int hkeyv;
+
        tpacpi_disable_brightness_delay();
 
        if (hotkey_status_set(true) < 0 ||
@@ -3747,6 +3868,18 @@ static void hotkey_resume(void)
        hotkey_wakeup_reason_notify_change();
        hotkey_wakeup_hotunplug_complete_notify_change();
        hotkey_poll_setup_safe(false);
+
+       /* restore previous mode of adapive keyboard of X1 Carbon */
+       if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+               if ((hkeyv >> 8) == 2) {
+                       if (!acpi_evalf(hkey_handle,
+                                               NULL,
+                                               "STRW", "vd",
+                                               adaptive_keyboard_prev_mode)) {
+                               pr_err("Cannot set adaptive keyboard mode.\n");
+                       }
+               }
+       }
 }
 
 /* procfs -------------------------------------------------------------- */
@@ -8447,9 +8580,21 @@ static void mute_led_exit(void)
                tpacpi_led_set(i, false);
 }
 
+static void mute_led_resume(void)
+{
+       int i;
+
+       for (i = 0; i < TPACPI_LED_MAX; i++) {
+               struct tp_led_table *t = &led_tables[i];
+               if (t->state >= 0)
+                       mute_led_on_off(t, t->state);
+       }
+}
+
 static struct ibm_struct mute_led_driver_data = {
        .name = "mute_led",
        .exit = mute_led_exit,
+       .resume = mute_led_resume,
 };
 
 /****************************************************************************