Commit b0fbfb0c 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 update from Matthew Garrett:
 "Some small updates for a few drivers, and some hardware enablement for
  new Ideapads and the gmux hardware in the latest Macs.

  This code won't run on older devices and has been well tested on new
  ones, so low risk of regressions."

* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86:
  ideapad: add Lenovo IdeaPad Z570 support (part 3)
  ideapad: add Lenovo IdeaPad Z570 support (part 2)
  ideapad: add Lenovo IdeaPad Z570 support (part 1)
  classmate-laptop: always call input_sync() after input_report_switch()
  thinkpad-acpi: recognize latest V-Series using DMI_BIOS_VENDOR
  dell-laptop: Fixed typo in touchpad LED quirk
  vga_switcheroo: Don't require handler init callback
  vga_switcheroo: Remove assumptions about registration/unregistration ordering
  apple-gmux: Add display mux support
  apple-gmux: Fix kconfig dependencies
  asus-wmi: record wlan status while controlled by userapp
  apple_gmux: Fix ACPI video unregister
  apple_gmux: Add support for newer hardware
  gmux: Add generic write32 function
parents 807b5169 0c7bbeb9
...@@ -5,4 +5,15 @@ Contact: "Ike Panhc <ike.pan@canonical.com>" ...@@ -5,4 +5,15 @@ Contact: "Ike Panhc <ike.pan@canonical.com>"
Description: Description:
Control the power of camera module. 1 means on, 0 means off. Control the power of camera module. 1 means on, 0 means off.
What: /sys/devices/platform/ideapad/fan_mode
Date: June 2012
KernelVersion: 3.6
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
Description:
Change fan mode
There are four available modes:
* 0 -> Super Silent Mode
* 1 -> Standard Mode
* 2 -> Dust Cleaning
* 4 -> Efficient Thermal Dissipation Mode
...@@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, ...@@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
} }
static int nouveau_dsm_init(void)
{
return 0;
}
static int nouveau_dsm_get_client_id(struct pci_dev *pdev) static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
{ {
/* easy option one - intel vendor ID means Integrated */ /* easy option one - intel vendor ID means Integrated */
...@@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev) ...@@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
static struct vga_switcheroo_handler nouveau_dsm_handler = { static struct vga_switcheroo_handler nouveau_dsm_handler = {
.switchto = nouveau_dsm_switchto, .switchto = nouveau_dsm_switchto,
.power_state = nouveau_dsm_power_state, .power_state = nouveau_dsm_power_state,
.init = nouveau_dsm_init,
.get_client_id = nouveau_dsm_get_client_id, .get_client_id = nouveau_dsm_get_client_id,
}; };
......
...@@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = { ...@@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = {
.clients = LIST_HEAD_INIT(vgasr_priv.clients), .clients = LIST_HEAD_INIT(vgasr_priv.clients),
}; };
int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) static bool vga_switcheroo_ready(void)
{
mutex_lock(&vgasr_mutex);
if (vgasr_priv.handler) {
mutex_unlock(&vgasr_mutex);
return -EINVAL;
}
vgasr_priv.handler = handler;
mutex_unlock(&vgasr_mutex);
return 0;
}
EXPORT_SYMBOL(vga_switcheroo_register_handler);
void vga_switcheroo_unregister_handler(void)
{ {
mutex_lock(&vgasr_mutex); /* we're ready if we get two clients + handler */
vgasr_priv.handler = NULL; return !vgasr_priv.active &&
mutex_unlock(&vgasr_mutex); vgasr_priv.registered_clients == 2 && vgasr_priv.handler;
} }
EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
static void vga_switcheroo_enable(void) static void vga_switcheroo_enable(void)
{ {
...@@ -98,6 +83,7 @@ static void vga_switcheroo_enable(void) ...@@ -98,6 +83,7 @@ static void vga_switcheroo_enable(void)
struct vga_switcheroo_client *client; struct vga_switcheroo_client *client;
/* call the handler to init */ /* call the handler to init */
if (vgasr_priv.handler->init)
vgasr_priv.handler->init(); vgasr_priv.handler->init();
list_for_each_entry(client, &vgasr_priv.clients, list) { list_for_each_entry(client, &vgasr_priv.clients, list) {
...@@ -113,6 +99,37 @@ static void vga_switcheroo_enable(void) ...@@ -113,6 +99,37 @@ static void vga_switcheroo_enable(void)
vgasr_priv.active = true; vgasr_priv.active = true;
} }
int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
{
mutex_lock(&vgasr_mutex);
if (vgasr_priv.handler) {
mutex_unlock(&vgasr_mutex);
return -EINVAL;
}
vgasr_priv.handler = handler;
if (vga_switcheroo_ready()) {
printk(KERN_INFO "vga_switcheroo: enabled\n");
vga_switcheroo_enable();
}
mutex_unlock(&vgasr_mutex);
return 0;
}
EXPORT_SYMBOL(vga_switcheroo_register_handler);
void vga_switcheroo_unregister_handler(void)
{
mutex_lock(&vgasr_mutex);
vgasr_priv.handler = NULL;
if (vgasr_priv.active) {
pr_info("vga_switcheroo: disabled\n");
vga_switcheroo_debugfs_fini(&vgasr_priv);
vgasr_priv.active = false;
}
mutex_unlock(&vgasr_mutex);
}
EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
static int register_client(struct pci_dev *pdev, static int register_client(struct pci_dev *pdev,
const struct vga_switcheroo_client_ops *ops, const struct vga_switcheroo_client_ops *ops,
int id, bool active) int id, bool active)
...@@ -134,9 +151,7 @@ static int register_client(struct pci_dev *pdev, ...@@ -134,9 +151,7 @@ static int register_client(struct pci_dev *pdev,
if (client_is_vga(client)) if (client_is_vga(client))
vgasr_priv.registered_clients++; vgasr_priv.registered_clients++;
/* if we get two clients + handler */ if (vga_switcheroo_ready()) {
if (!vgasr_priv.active &&
vgasr_priv.registered_clients == 2 && vgasr_priv.handler) {
printk(KERN_INFO "vga_switcheroo: enabled\n"); printk(KERN_INFO "vga_switcheroo: enabled\n");
vga_switcheroo_enable(); vga_switcheroo_enable();
} }
......
...@@ -289,6 +289,7 @@ config IDEAPAD_LAPTOP ...@@ -289,6 +289,7 @@ config IDEAPAD_LAPTOP
tristate "Lenovo IdeaPad Laptop Extras" tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI depends on ACPI
depends on RFKILL && INPUT depends on RFKILL && INPUT
depends on SERIO_I8042
select INPUT_SPARSEKMAP select INPUT_SPARSEKMAP
help help
This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
...@@ -758,8 +759,11 @@ config SAMSUNG_Q10 ...@@ -758,8 +759,11 @@ config SAMSUNG_Q10
config APPLE_GMUX config APPLE_GMUX
tristate "Apple Gmux Driver" tristate "Apple Gmux Driver"
depends on ACPI
depends on PNP depends on PNP
select BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
depends on ACPI_VIDEO=n || ACPI_VIDEO
---help--- ---help---
This driver provides support for the gmux device found on many This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid Apple laptops, which controls the display mux for the hybrid
......
This diff is collapsed.
...@@ -101,6 +101,7 @@ MODULE_LICENSE("GPL"); ...@@ -101,6 +101,7 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 #define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
#define ASUS_WMI_DEVID_CWAP 0x00010003 #define ASUS_WMI_DEVID_CWAP 0x00010003
#define ASUS_WMI_DEVID_WLAN 0x00010011 #define ASUS_WMI_DEVID_WLAN 0x00010011
#define ASUS_WMI_DEVID_WLAN_LED 0x00010012
#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 #define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
#define ASUS_WMI_DEVID_GPS 0x00010015 #define ASUS_WMI_DEVID_GPS 0x00010015
#define ASUS_WMI_DEVID_WIMAX 0x00010017 #define ASUS_WMI_DEVID_WIMAX 0x00010017
...@@ -731,8 +732,21 @@ static int asus_rfkill_set(void *data, bool blocked) ...@@ -731,8 +732,21 @@ static int asus_rfkill_set(void *data, bool blocked)
{ {
struct asus_rfkill *priv = data; struct asus_rfkill *priv = data;
u32 ctrl_param = !blocked; u32 ctrl_param = !blocked;
u32 dev_id = priv->dev_id;
return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL); /*
* If the user bit is set, BIOS can't set and record the wlan status,
* it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
* while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
* So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
* while setting the wlan status through WMI.
* This is also the behavior that windows app will do.
*/
if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
priv->asus->driver->wlan_ctrl_by_user)
dev_id = ASUS_WMI_DEVID_WLAN_LED;
return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
} }
static void asus_rfkill_query(struct rfkill *rfkill, void *data) static void asus_rfkill_query(struct rfkill *rfkill, void *data)
...@@ -1653,6 +1667,7 @@ static int asus_wmi_add(struct platform_device *pdev) ...@@ -1653,6 +1667,7 @@ static int asus_wmi_add(struct platform_device *pdev)
struct asus_wmi *asus; struct asus_wmi *asus;
acpi_status status; acpi_status status;
int err; int err;
u32 result;
asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL); asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
if (!asus) if (!asus)
...@@ -1711,6 +1726,10 @@ static int asus_wmi_add(struct platform_device *pdev) ...@@ -1711,6 +1726,10 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err) if (err)
goto fail_debugfs; goto fail_debugfs;
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
asus->driver->wlan_ctrl_by_user = 1;
return 0; return 0;
fail_debugfs: fail_debugfs:
......
...@@ -46,6 +46,7 @@ struct quirk_entry { ...@@ -46,6 +46,7 @@ struct quirk_entry {
struct asus_wmi_driver { struct asus_wmi_driver {
int brightness; int brightness;
int panel_power; int panel_power;
int wlan_ctrl_by_user;
const char *name; const char *name;
struct module *owner; struct module *owner;
......
...@@ -725,8 +725,10 @@ static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) ...@@ -725,8 +725,10 @@ static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
struct input_dev *inputdev = dev_get_drvdata(&dev->dev); struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
if (event == 0x81) { if (event == 0x81) {
if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val); input_report_switch(inputdev, SW_TABLET_MODE, !val);
input_sync(inputdev);
}
} }
} }
...@@ -739,8 +741,10 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) ...@@ -739,8 +741,10 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev)
set_bit(SW_TABLET_MODE, inputdev->swbit); set_bit(SW_TABLET_MODE, inputdev->swbit);
acpi = to_acpi_device(inputdev->dev.parent); acpi = to_acpi_device(inputdev->dev.parent);
if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val); input_report_switch(inputdev, SW_TABLET_MODE, !val);
input_sync(inputdev);
}
} }
static int cmpc_tablet_add(struct acpi_device *acpi) static int cmpc_tablet_add(struct acpi_device *acpi)
...@@ -760,8 +764,10 @@ static int cmpc_tablet_resume(struct device *dev) ...@@ -760,8 +764,10 @@ static int cmpc_tablet_resume(struct device *dev)
struct input_dev *inputdev = dev_get_drvdata(dev); struct input_dev *inputdev = dev_get_drvdata(dev);
unsigned long long val = 0; unsigned long long val = 0;
if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val); input_report_switch(inputdev, SW_TABLET_MODE, !val);
input_sync(inputdev);
}
return 0; return 0;
} }
#endif #endif
......
...@@ -211,7 +211,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { ...@@ -211,7 +211,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 5420", .ident = "Dell Inspiron 5420",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5420"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
...@@ -220,7 +220,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { ...@@ -220,7 +220,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 5520", .ident = "Dell Inspiron 5520",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5520"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
...@@ -229,7 +229,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { ...@@ -229,7 +229,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 5720", .ident = "Dell Inspiron 5720",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5720"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
...@@ -238,7 +238,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { ...@@ -238,7 +238,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 7420", .ident = "Dell Inspiron 7420",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7420"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
...@@ -247,7 +247,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { ...@@ -247,7 +247,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 7520", .ident = "Dell Inspiron 7520",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7520"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
...@@ -256,7 +256,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { ...@@ -256,7 +256,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 7720", .ident = "Dell Inspiron 7720",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7720"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/i8042.h>
#define IDEAPAD_RFKILL_DEV_NUM (3) #define IDEAPAD_RFKILL_DEV_NUM (3)
...@@ -63,8 +64,11 @@ enum { ...@@ -63,8 +64,11 @@ enum {
VPCCMD_R_3G, VPCCMD_R_3G,
VPCCMD_W_3G, VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */ VPCCMD_R_ODD, /* 0x21 */
VPCCMD_R_RF = 0x23, VPCCMD_W_FAN,
VPCCMD_R_RF,
VPCCMD_W_RF, VPCCMD_W_RF,
VPCCMD_R_FAN = 0x2B,
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
VPCCMD_W_BL_POWER = 0x33, VPCCMD_W_BL_POWER = 0x33,
}; };
...@@ -356,14 +360,46 @@ static ssize_t store_ideapad_cam(struct device *dev, ...@@ -356,14 +360,46 @@ static ssize_t store_ideapad_cam(struct device *dev,
return -EINVAL; return -EINVAL;
ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state); ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
if (ret < 0) if (ret < 0)
return ret; return -EIO;
return count; return count;
} }
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
static ssize_t show_ideapad_fan(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned long result;
if (read_ec_data(ideapad_handle, VPCCMD_R_FAN, &result))
return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result);
}
static ssize_t store_ideapad_fan(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret, state;
if (!count)
return 0;
if (sscanf(buf, "%i", &state) != 1)
return -EINVAL;
if (state < 0 || state > 4 || state == 3)
return -EINVAL;
ret = write_ec_cmd(ideapad_handle, VPCCMD_W_FAN, state);
if (ret < 0)
return -EIO;
return count;
}
static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
static struct attribute *ideapad_attributes[] = { static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr, &dev_attr_camera_power.attr,
&dev_attr_fan_mode.attr,
NULL NULL
}; };
...@@ -377,7 +413,10 @@ static umode_t ideapad_is_visible(struct kobject *kobj, ...@@ -377,7 +413,10 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
if (attr == &dev_attr_camera_power.attr) if (attr == &dev_attr_camera_power.attr)
supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg)); supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
else else if (attr == &dev_attr_fan_mode.attr) {
unsigned long value;
supported = !read_ec_data(ideapad_handle, VPCCMD_R_FAN, &value);
} else
supported = true; supported = true;
return supported ? attr->mode : 0; return supported ? attr->mode : 0;
...@@ -518,9 +557,15 @@ static void ideapad_platform_exit(struct ideapad_private *priv) ...@@ -518,9 +557,15 @@ static void ideapad_platform_exit(struct ideapad_private *priv)
*/ */
static const struct key_entry ideapad_keymap[] = { static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 7, { KEY_CAMERA } },
{ KE_KEY, 11, { KEY_F16 } },
{ KE_KEY, 13, { KEY_WLAN } }, { KE_KEY, 13, { KEY_WLAN } },
{ KE_KEY, 16, { KEY_PROG1 } }, { KE_KEY, 16, { KEY_PROG1 } },
{ KE_KEY, 17, { KEY_PROG2 } }, { KE_KEY, 17, { KEY_PROG2 } },
{ KE_KEY, 64, { KEY_PROG3 } },
{ KE_KEY, 65, { KEY_PROG4 } },
{ KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
{ KE_KEY, 67, { KEY_TOUCHPAD_ON } },
{ KE_END, 0 }, { KE_END, 0 },
}; };
...@@ -587,6 +632,28 @@ static void ideapad_input_novokey(struct ideapad_private *priv) ...@@ -587,6 +632,28 @@ static void ideapad_input_novokey(struct ideapad_private *priv)
ideapad_input_report(priv, 16); ideapad_input_report(priv, 16);
} }
static void ideapad_check_special_buttons(struct ideapad_private *priv)
{
unsigned long bit, value;
read_ec_data(ideapad_handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
for (bit = 0; bit < 16; bit++) {
if (test_bit(bit, &value)) {
switch (bit) {
case 6:
/* Thermal Management button */
ideapad_input_report(priv, 65);
break;
case 1:
/* OneKey Theater button */
ideapad_input_report(priv, 64);
break;
}
}
}
}
/* /*
* backlight * backlight
*/ */
...@@ -691,6 +758,24 @@ static const struct acpi_device_id ideapad_device_ids[] = { ...@@ -691,6 +758,24 @@ static const struct acpi_device_id ideapad_device_ids[] = {
}; };
MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
static void ideapad_sync_touchpad_state(struct acpi_device *adevice)
{
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
unsigned long value;
/* Without reading from EC touchpad LED doesn't switch state */
if (!read_ec_data(adevice->handle, VPCCMD_R_TOUCHPAD, &value)) {
/* Some IdeaPads don't really turn off touchpad - they only
* switch the LED state. We (de)activate KBC AUX port to turn
* touchpad off and on. We send KEY_TOUCHPAD_OFF and
* KEY_TOUCHPAD_ON to not to get out of sync with LED */
unsigned char param;
i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
I8042_CMD_AUX_DISABLE);
ideapad_input_report(priv, value ? 67 : 66);
}
}
static int __devinit ideapad_acpi_add(struct acpi_device *adevice) static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
{ {
int ret, i; int ret, i;
...@@ -727,6 +812,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) ...@@ -727,6 +812,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
priv->rfk[i] = NULL; priv->rfk[i] = NULL;
} }
ideapad_sync_rfk_state(priv); ideapad_sync_rfk_state(priv);
ideapad_sync_touchpad_state(adevice);
if (!acpi_video_backlight_support()) { if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv); ret = ideapad_backlight_init(priv);
...@@ -785,9 +871,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -785,9 +871,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
ideapad_sync_rfk_state(priv); ideapad_sync_rfk_state(priv);
break; break;
case 13: case 13:
case 11:
case 7:
case 6: case 6:
ideapad_input_report(priv, vpc_bit); ideapad_input_report(priv, vpc_bit);
break; break;
case 5:
ideapad_sync_touchpad_state(adevice);
break;
case 4: case 4:
ideapad_backlight_notify_brightness(priv); ideapad_backlight_notify_brightness(priv);
break; break;
...@@ -797,6 +888,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -797,6 +888,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
case 2: case 2:
ideapad_backlight_notify_power(priv); ideapad_backlight_notify_power(priv);
break; break;
case 0:
ideapad_check_special_buttons(priv);
break;
default: default:
pr_info("Unknown event: %lu\n", vpc_bit); pr_info("Unknown event: %lu\n", vpc_bit);
} }
...@@ -804,6 +898,15 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -804,6 +898,15 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
} }
} }
static int ideapad_acpi_resume(struct device *device)
{
ideapad_sync_rfk_state(ideapad_priv);
ideapad_sync_touchpad_state(to_acpi_device(device));
return 0;
}
static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
static struct acpi_driver ideapad_acpi_driver = { static struct acpi_driver ideapad_acpi_driver = {
.name = "ideapad_acpi", .name = "ideapad_acpi",
.class = "IdeaPad", .class = "IdeaPad",
...@@ -811,6 +914,7 @@ static struct acpi_driver ideapad_acpi_driver = { ...@@ -811,6 +914,7 @@ static struct acpi_driver ideapad_acpi_driver = {
.ops.add = ideapad_acpi_add, .ops.add = ideapad_acpi_add,
.ops.remove = ideapad_acpi_remove, .ops.remove = ideapad_acpi_remove,
.ops.notify = ideapad_acpi_notify, .ops.notify = ideapad_acpi_notify,
.drv.pm = &ideapad_pm,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
......
...@@ -8664,6 +8664,13 @@ static int __must_check __init get_thinkpad_model_data( ...@@ -8664,6 +8664,13 @@ static int __must_check __init get_thinkpad_model_data(
tp->model_str = kstrdup(s, GFP_KERNEL); tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str) if (!tp->model_str)
return -ENOMEM; return -ENOMEM;
} else {
s = dmi_get_system_info(DMI_BIOS_VENDOR);
if (s && !(strnicmp(s, "Lenovo", 6))) {
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
}
} }
s = dmi_get_system_info(DMI_PRODUCT_NAME); s = dmi_get_system_info(DMI_PRODUCT_NAME);
......
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