Commit 4ba85265 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86

Pull x86 platform driver updates from Matthew Garrett:
 "Support for the new keyboard features on the Thinkpad Carbon, a bunch
  of updates for the Sony and Toshiba drivers, a new driver for upcoming
  Alienware hardware and a few misc fixes.  There's a couple of patches
  that got Acked today but aren't invasive, so I'll send a further PR
  for them next week"

* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (28 commits)
  alienware-wmi: cover some scenarios where memory allocations would fail
  Add WMI driver for controlling AlienFX features on some Alienware products
  fujitsu-tablet: add support for Lifebook T901 and T902
  x86, platform: Make HP_WIRELESS option text more descriptive
  x86, acpi: LLVMLinux: Remove nested functions from Thinkpad ACPI
  save and restore adaptive keyboard mode for suspend and,resume
  support Thinkpad X1 Carbon 2nd generation's adaptive keyboard
  toshiba_acpi: Fix whitespace
  toshiba_acpi: Update version and copyright info
  toshiba_acpi: Add accelerometer support
  toshiba_acpi: Add ECO mode led support
  toshiba_acpi: Add touchpad enable/disable support-
  toshiba_acpi: Add keyboard backlight support
  toshiba_acpi: Adapt Illumination code to use SCI
  toshiba_acpi: Add System Configuration Interface
  thinkpad_acpi: Fix inconsistent mute LED after resume
  sonypi: Simplify dependencies
  Revert "X86 platform: New BayTrail IOSF-SB MBI driver"
  sony-laptop: remove useless sony-laptop versioning
  sony-laptop: add smart connect control function
  ...
parents dd76a786 562c7cec
...@@ -408,7 +408,7 @@ config APPLICOM ...@@ -408,7 +408,7 @@ config APPLICOM
config SONYPI config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support" tristate "Sony Vaio Programmable I/O Control Device support"
depends on X86 && PCI && INPUT && !64BIT depends on X86_32 && PCI && INPUT
---help--- ---help---
This driver enables access to the Sony Programmable I/O Control This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops. Device which can be found in many (all ?) Sony Vaio laptops.
......
...@@ -53,6 +53,18 @@ config ACERHDF ...@@ -53,6 +53,18 @@ config ACERHDF
If you have an Acer Aspire One netbook, say Y or M If you have an Acer Aspire One netbook, say Y or M
here. here.
config ALIENWARE_WMI
tristate "Alienware Special feature control"
depends on ACPI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on ACPI_WMI
---help---
This is a driver for controlling Alienware BIOS driven
features. It exposes an interface for controlling the AlienFX
zones on Alienware machines that don't contain a dedicated AlienFX
USB MCU such as the X51 and X51-R2.
config ASUS_LAPTOP config ASUS_LAPTOP
tristate "Asus Laptop Extras" tristate "Asus Laptop Extras"
depends on ACPI depends on ACPI
...@@ -196,7 +208,7 @@ config HP_ACCEL ...@@ -196,7 +208,7 @@ config HP_ACCEL
be called hp_accel. be called hp_accel.
config HP_WIRELESS config HP_WIRELESS
tristate "HP WIRELESS" tristate "HP wireless button"
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
help help
...@@ -817,12 +829,4 @@ config PVPANIC ...@@ -817,12 +829,4 @@ config PVPANIC
a paravirtualized device provided by QEMU; it lets a virtual machine a paravirtualized device provided by QEMU; it lets a virtual machine
(guest) communicate panic events to the host. (guest) communicate panic events to the host.
config INTEL_BAYTRAIL_MBI
tristate
depends on PCI
---help---
Needed on Baytrail platforms for access to the IOSF Sideband Mailbox
Interface. This is a requirement for systems that need to configure
the PUNIT for power management features such as RAPL.
endif # X86_PLATFORM_DEVICES endif # X86_PLATFORM_DEVICES
...@@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o ...@@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o
obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
This diff is collapsed.
...@@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = { ...@@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
KEY_LEFTALT KEY_LEFTALT
}; };
static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_SCROLLDOWN,
KEY_SCROLLUP,
KEY_CYCLEWINDOWS,
KEY_LEFTCTRL,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_LEFTMETA
};
static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_VOLUMEDOWN,
KEY_VOLUMEUP,
KEY_CYCLEWINDOWS,
KEY_PROG1,
KEY_PROG2,
KEY_LEFTMETA,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
};
static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED,
...@@ -300,6 +338,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) ...@@ -300,6 +338,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
} }
static const struct dmi_system_id dmi_ids[] __initconst = { static const struct dmi_system_id dmi_ids[] __initconst = {
{
.callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Lifebook T901",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
},
.driver_data = keymap_Lifebook_T901
},
{
.callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Lifebook T901",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
},
.driver_data = keymap_Lifebook_T901
},
{
.callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Lifebook T902",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
},
.driver_data = keymap_Lifebook_T902
},
{ {
.callback = fujitsu_dmi_lifebook, .callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Siemens P/T Series", .ident = "Fujitsu Siemens P/T Series",
......
/*
* Baytrail IOSF-SB MailBox Interface Driver
* Copyright (c) 2013, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*
* The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
* mailbox interface (MBI) to communicate with mutiple devices. This
* driver implements BayTrail-specific access to this interface.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include "intel_baytrail.h"
static DEFINE_SPINLOCK(iosf_mbi_lock);
static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
{
return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE;
}
static struct pci_dev *mbi_pdev; /* one mbi device */
/* Hold lock before calling */
static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
{
int result;
if (!mbi_pdev)
return -ENODEV;
if (mcrx) {
result = pci_write_config_dword(mbi_pdev,
BT_MBI_MCRX_OFFSET, mcrx);
if (result < 0)
goto iosf_mbi_read_err;
}
result = pci_write_config_dword(mbi_pdev,
BT_MBI_MCR_OFFSET, mcr);
if (result < 0)
goto iosf_mbi_read_err;
result = pci_read_config_dword(mbi_pdev,
BT_MBI_MDR_OFFSET, mdr);
if (result < 0)
goto iosf_mbi_read_err;
return 0;
iosf_mbi_read_err:
dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
result);
return result;
}
/* Hold lock before calling */
static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
{
int result;
if (!mbi_pdev)
return -ENODEV;
result = pci_write_config_dword(mbi_pdev,
BT_MBI_MDR_OFFSET, mdr);
if (result < 0)
goto iosf_mbi_write_err;
if (mcrx) {
result = pci_write_config_dword(mbi_pdev,
BT_MBI_MCRX_OFFSET, mcrx);
if (result < 0)
goto iosf_mbi_write_err;
}
result = pci_write_config_dword(mbi_pdev,
BT_MBI_MCR_OFFSET, mcr);
if (result < 0)
goto iosf_mbi_write_err;
return 0;
iosf_mbi_write_err:
dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
result);
return result;
}
int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
{
u32 mcr, mcrx;
unsigned long flags;
int ret;
/*Access to the GFX unit is handled by GPU code */
BUG_ON(port == BT_MBI_UNIT_GFX);
mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
mcrx = offset & BT_MBI_MASK_HI;
spin_lock_irqsave(&iosf_mbi_lock, flags);
ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
return ret;
}
EXPORT_SYMBOL(bt_mbi_read);
int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
{
u32 mcr, mcrx;
unsigned long flags;
int ret;
/*Access to the GFX unit is handled by GPU code */
BUG_ON(port == BT_MBI_UNIT_GFX);
mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
mcrx = offset & BT_MBI_MASK_HI;
spin_lock_irqsave(&iosf_mbi_lock, flags);
ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
return ret;
}
EXPORT_SYMBOL(bt_mbi_write);
int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
{
u32 mcr, mcrx;
u32 value;
unsigned long flags;
int ret;
/*Access to the GFX unit is handled by GPU code */
BUG_ON(port == BT_MBI_UNIT_GFX);
mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
mcrx = offset & BT_MBI_MASK_HI;
spin_lock_irqsave(&iosf_mbi_lock, flags);
/* Read current mdr value */
ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value);
if (ret < 0) {
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
return ret;
}
/* Apply mask */
value &= ~mask;
mdr &= mask;
value |= mdr;
/* Write back */
ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value);
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
return ret;
}
EXPORT_SYMBOL(bt_mbi_modify);
static int iosf_mbi_probe(struct pci_dev *pdev,
const struct pci_device_id *unused)
{
int ret;
ret = pci_enable_device(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "error: could not enable device\n");
return ret;
}
mbi_pdev = pci_dev_get(pdev);
return 0;
}
static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
static struct pci_driver iosf_mbi_pci_driver = {
.name = "iosf_mbi_pci",
.probe = iosf_mbi_probe,
.id_table = iosf_mbi_pci_ids,
};
static int __init bt_mbi_init(void)
{
return pci_register_driver(&iosf_mbi_pci_driver);
}
static void __exit bt_mbi_exit(void)
{
pci_unregister_driver(&iosf_mbi_pci_driver);
if (mbi_pdev) {
pci_dev_put(mbi_pdev);
mbi_pdev = NULL;
}
}
module_init(bt_mbi_init);
module_exit(bt_mbi_exit);
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor");
MODULE_LICENSE("GPL v2");
/*
* intel_baytrail.h: MailBox access support for Intel BayTrail platforms
*/
#ifndef INTEL_BAYTRAIL_MBI_SYMS_H
#define INTEL_BAYTRAIL_MBI_SYMS_H
#define BT_MBI_MCR_OFFSET 0xD0
#define BT_MBI_MDR_OFFSET 0xD4
#define BT_MBI_MCRX_OFFSET 0xD8
#define BT_MBI_RD_MASK 0xFEFFFFFF
#define BT_MBI_WR_MASK 0X01000000
#define BT_MBI_MASK_HI 0xFFFFFF00
#define BT_MBI_MASK_LO 0x000000FF
#define BT_MBI_ENABLE 0xF0
/* BT-SB unit access methods */
#define BT_MBI_UNIT_AUNIT 0x00
#define BT_MBI_UNIT_SMC 0x01
#define BT_MBI_UNIT_CPU 0x02
#define BT_MBI_UNIT_BUNIT 0x03
#define BT_MBI_UNIT_PMC 0x04
#define BT_MBI_UNIT_GFX 0x06
#define BT_MBI_UNIT_SMI 0x0C
#define BT_MBI_UNIT_USB 0x43
#define BT_MBI_UNIT_SATA 0xA3
#define BT_MBI_UNIT_PCIE 0xA6
/* Read/write opcodes */
#define BT_MBI_AUNIT_READ 0x10
#define BT_MBI_AUNIT_WRITE 0x11
#define BT_MBI_SMC_READ 0x10
#define BT_MBI_SMC_WRITE 0x11
#define BT_MBI_CPU_READ 0x10
#define BT_MBI_CPU_WRITE 0x11
#define BT_MBI_BUNIT_READ 0x10
#define BT_MBI_BUNIT_WRITE 0x11
#define BT_MBI_PMC_READ 0x06
#define BT_MBI_PMC_WRITE 0x07
#define BT_MBI_GFX_READ 0x00
#define BT_MBI_GFX_WRITE 0x01
#define BT_MBI_SMIO_READ 0x06
#define BT_MBI_SMIO_WRITE 0x07
#define BT_MBI_USB_READ 0x06
#define BT_MBI_USB_WRITE 0x07
#define BT_MBI_SATA_READ 0x00
#define BT_MBI_SATA_WRITE 0x01
#define BT_MBI_PCIE_READ 0x00
#define BT_MBI_PCIE_WRITE 0x01
/**
* bt_mbi_read() - MailBox Interface read command
* @port: port indicating subunit being accessed
* @opcode: port specific read or write opcode
* @offset: register address offset
* @mdr: register data to be read
*
* Locking is handled by spinlock - cannot sleep.
* Return: Nonzero on error
*/
int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr);
/**
* bt_mbi_write() - MailBox unmasked write command
* @port: port indicating subunit being accessed
* @opcode: port specific read or write opcode
* @offset: register address offset
* @mdr: register data to be written
*
* Locking is handled by spinlock - cannot sleep.
* Return: Nonzero on error
*/
int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr);
/**
* bt_mbi_modify() - MailBox masked write command
* @port: port indicating subunit being accessed
* @opcode: port specific read or write opcode
* @offset: register address offset
* @mdr: register data being modified
* @mask: mask indicating bits in mdr to be modified
*
* Locking is handled by spinlock - cannot sleep.
* Return: Nonzero on error
*/
int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask);
#endif /* INTEL_BAYTRAIL_MBI_SYMS_H */
...@@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = { ...@@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */ /* hotkey input device driver */
static int sleep_keydown_seen;
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{ {
struct input_dev *hotk_input_dev = pcc->input_dev; struct input_dev *hotk_input_dev = pcc->input_dev;
...@@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) ...@@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
"error getting hotkey status\n")); "error getting hotkey status\n"));
return; return;
} }
/* hack: some firmware sends no key down for sleep / hibernate */
if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
if (result & 0x80)
sleep_keydown_seen = 1;
if (!sleep_keydown_seen)
sparse_keymap_report_event(hotk_input_dev,
result & 0xf, 0x80, false);
}
if (!sparse_keymap_report_event(hotk_input_dev, if (!sparse_keymap_report_event(hotk_input_dev,
result & 0xf, result & 0x80, false)) result & 0xf, result & 0x80, false))
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
......
This diff is collapsed.
...@@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) ...@@ -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) \ #define TPACPI_COMPARE_KEY(__scancode, __member) \
do { \ do { \
if ((event_mask & (1 << __scancode)) && \ if ((event_mask & (1 << __scancode)) && \
oldn->__member != newn->__member) \ oldn->__member != newn->__member) \
tpacpi_hotkey_send_key(__scancode); \ tpacpi_hotkey_send_key(__scancode); \
} while (0) } while (0)
#define TPACPI_MAY_SEND_KEY(__scancode) \ #define TPACPI_MAY_SEND_KEY(__scancode) \
do { \ do { \
if (event_mask & (1 << __scancode)) \ if (event_mask & (1 << __scancode)) \
tpacpi_hotkey_send_key(__scancode); \ tpacpi_hotkey_send_key(__scancode); \
} while (0) } while (0)
void issue_volchange(const unsigned int oldvol, static void issue_volchange(const unsigned int oldvol,
const unsigned int newvol) const unsigned int newvol,
{ const u32 event_mask)
unsigned int i = oldvol; {
unsigned int i = oldvol;
while (i > newvol) { while (i > newvol) {
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
i--; i--;
} }
while (i < newvol) { while (i < newvol) {
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
i++; i++;
}
} }
}
void issue_brightnesschange(const unsigned int oldbrt, static void issue_brightnesschange(const unsigned int oldbrt,
const unsigned int newbrt) const unsigned int newbrt,
{ const u32 event_mask)
unsigned int i = oldbrt; {
unsigned int i = oldbrt;
while (i > newbrt) { while (i > newbrt) {
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
i--; i--;
}
while (i < newbrt) {
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
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_THINKPAD, thinkpad_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_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, ...@@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
oldn->volume_level != newn->volume_level) { oldn->volume_level != newn->volume_level) {
/* recently muted, or repeated mute keypress, or /* recently muted, or repeated mute keypress, or
* multiple presses ending in mute */ * 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); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
} }
} else { } else {
...@@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, ...@@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
} }
if (oldn->volume_level != newn->volume_level) { 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) { } else if (oldn->volume_toggle != newn->volume_toggle) {
/* repeated vol up/down keypress at end of scale ? */ /* repeated vol up/down keypress at end of scale ? */
if (newn->volume_level == 0) if (newn->volume_level == 0)
...@@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, ...@@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
/* handle brightness */ /* handle brightness */
if (oldn->brightness_level != newn->brightness_level) { if (oldn->brightness_level != newn->brightness_level) {
issue_brightnesschange(oldn->brightness_level, issue_brightnesschange(oldn->brightness_level,
newn->brightness_level); newn->brightness_level, event_mask);
} else if (oldn->brightness_toggle != newn->brightness_toggle) { } else if (oldn->brightness_toggle != newn->brightness_toggle) {
/* repeated key presses that didn't change state */ /* repeated key presses that didn't change state */
if (newn->brightness_level == 0) if (newn->brightness_level == 0)
...@@ -3437,6 +3441,106 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3437,6 +3441,106 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
return (res < 0)? res : 1; 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, static bool hotkey_notify_hotkey(const u32 hkey,
bool *send_acpi_ev, bool *send_acpi_ev,
bool *ignore_acpi_ev) bool *ignore_acpi_ev)
...@@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey, ...@@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*ignore_acpi_ev = true; *ignore_acpi_ev = true;
} }
return true; return true;
} else {
return adaptive_keyboard_hotkey_notify_hotkey(scancode);
} }
return false; return false;
} }
...@@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) ...@@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
static void hotkey_suspend(void) static void hotkey_suspend(void)
{ {
int hkeyv;
/* Do these on suspend, we get the events on early resume! */ /* Do these on suspend, we get the events on early resume! */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
hotkey_autosleep_ack = 0; 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) static void hotkey_resume(void)
{ {
int hkeyv;
tpacpi_disable_brightness_delay(); tpacpi_disable_brightness_delay();
if (hotkey_status_set(true) < 0 || if (hotkey_status_set(true) < 0 ||
...@@ -3747,6 +3868,18 @@ static void hotkey_resume(void) ...@@ -3747,6 +3868,18 @@ static void hotkey_resume(void)
hotkey_wakeup_reason_notify_change(); hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change();
hotkey_poll_setup_safe(false); 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 -------------------------------------------------------------- */ /* procfs -------------------------------------------------------------- */
...@@ -8447,9 +8580,21 @@ static void mute_led_exit(void) ...@@ -8447,9 +8580,21 @@ static void mute_led_exit(void)
tpacpi_led_set(i, false); 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 = { static struct ibm_struct mute_led_driver_data = {
.name = "mute_led", .name = "mute_led",
.exit = mute_led_exit, .exit = mute_led_exit,
.resume = mute_led_resume,
}; };
/**************************************************************************** /****************************************************************************
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment