Commit 7c27fd19 authored by Len Brown's avatar Len Brown
Browse files

Merge branch 'sony-laptop' into release

parents 336d63b8 16dd55f3
......@@ -165,6 +165,7 @@ config SONY_LAPTOP
depends on ACPI
select BACKLIGHT_CLASS_DEVICE
depends on INPUT
depends on RFKILL
---help---
This mini-driver drives the SNC and SPIC devices present in the ACPI
BIOS of the Sony Vaio laptops.
......
......@@ -2,7 +2,7 @@
* ACPI Sony Notebook Control Driver (SNC and SPIC)
*
* Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
* Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
* Copyright (C) 2007-2009 Mattia Dongili <malattia@linux.it>
*
* Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
* which are copyrighted by their respective authors.
......@@ -46,7 +46,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
......@@ -64,6 +63,7 @@
#include <asm/uaccess.h>
#include <linux/sonypi.h>
#include <linux/sony-laptop.h>
#include <linux/rfkill.h>
#ifdef CONFIG_SONYPI_COMPAT
#include <linux/poll.h>
#include <linux/miscdevice.h>
......@@ -123,6 +123,18 @@ MODULE_PARM_DESC(minor,
"default is -1 (automatic)");
#endif
enum sony_nc_rfkill {
SONY_WIFI,
SONY_BLUETOOTH,
SONY_WWAN,
SONY_WIMAX,
SONY_RFKILL_MAX,
};
static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
static void sony_nc_rfkill_update(void);
/*********** Input Devices ***********/
#define SONY_LAPTOP_BUF_SIZE 128
......@@ -134,6 +146,7 @@ struct sony_laptop_input_s {
spinlock_t fifo_lock;
struct workqueue_struct *wq;
};
static struct sony_laptop_input_s sony_laptop_input = {
.users = ATOMIC_INIT(0),
};
......@@ -211,6 +224,14 @@ static int sony_laptop_input_index[] = {
48, /* 61 SONYPI_EVENT_WIRELESS_OFF */
49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
51, /* 64 SONYPI_EVENT_CD_EJECT_PRESSED */
52, /* 65 SONYPI_EVENT_MODEKEY_PRESSED */
53, /* 66 SONYPI_EVENT_PKEY_P4 */
54, /* 67 SONYPI_EVENT_PKEY_P5 */
55, /* 68 SONYPI_EVENT_SETTINGKEY_PRESSED */
56, /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */
57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */
-1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */
};
static int sony_laptop_input_keycode_map[] = {
......@@ -264,7 +285,14 @@ static int sony_laptop_input_keycode_map[] = {
KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */
KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */
KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
KEY_ZOOMOUT, /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
KEY_EJECTCD, /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */
KEY_F13, /* 52 SONYPI_EVENT_MODEKEY_PRESSED */
KEY_PROG4, /* 53 SONYPI_EVENT_PKEY_P4 */
KEY_F14, /* 54 SONYPI_EVENT_PKEY_P5 */
KEY_F15, /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */
KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */
KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */
};
/* release buttons after a short delay if pressed */
......@@ -369,7 +397,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
if (!sony_laptop_input.wq) {
printk(KERN_ERR DRV_PFX
"Unabe to create workqueue.\n");
"Unable to create workqueue.\n");
error = -ENXIO;
goto err_free_kfifo;
}
......@@ -689,6 +717,31 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
return -1;
}
static int sony_find_snc_handle(int handle)
{
int i;
int result;
for (i = 0x20; i < 0x30; i++) {
acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result);
if (result == handle)
return i-0x20;
}
return -1;
}
static int sony_call_snc_handle(int handle, int argument, int *result)
{
int offset = sony_find_snc_handle(handle);
if (offset < 0)
return -1;
return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument,
result);
}
/*
* sony_nc_values input/output validate functions
*/
......@@ -809,87 +862,53 @@ struct sony_nc_event {
u8 event;
};
static struct sony_nc_event *sony_nc_events;
/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
* for Fn keys
*/
static int sony_nc_C_enable(const struct dmi_system_id *id)
{
int result = 0;
printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
sony_nc_events = id->driver_data;
if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
|| acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
|| acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
|| acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
|| acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
|| acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
"functionalities may be missing\n");
return 1;
}
return 0;
}
static struct sony_nc_event sony_C_events[] = {
static struct sony_nc_event sony_100_events[] = {
{ 0x90, SONYPI_EVENT_PKEY_P1 },
{ 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x91, SONYPI_EVENT_PKEY_P2 },
{ 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x81, SONYPI_EVENT_FNKEY_F1 },
{ 0x01, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x82, SONYPI_EVENT_FNKEY_F2 },
{ 0x02, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x83, SONYPI_EVENT_FNKEY_F3 },
{ 0x03, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x84, SONYPI_EVENT_FNKEY_F4 },
{ 0x04, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x85, SONYPI_EVENT_FNKEY_F5 },
{ 0x05, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x86, SONYPI_EVENT_FNKEY_F6 },
{ 0x06, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x87, SONYPI_EVENT_FNKEY_F7 },
{ 0x07, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x89, SONYPI_EVENT_FNKEY_F9 },
{ 0x09, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x8A, SONYPI_EVENT_FNKEY_F10 },
{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x8C, SONYPI_EVENT_FNKEY_F12 },
{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED },
{ 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0, 0 },
};
/* SNC-only model map */
static const struct dmi_system_id sony_nc_ids[] = {
{
.ident = "Sony Vaio FE Series",
.callback = sony_nc_C_enable,
.driver_data = sony_C_events,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"),
},
},
{
.ident = "Sony Vaio FZ Series",
.callback = sony_nc_C_enable,
.driver_data = sony_C_events,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"),
},
},
{
.ident = "Sony Vaio C Series",
.callback = sony_nc_C_enable,
.driver_data = sony_C_events,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
},
},
{
.ident = "Sony Vaio N Series",
.callback = sony_nc_C_enable,
.driver_data = sony_C_events,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"),
},
},
{ }
static struct sony_nc_event sony_127_events[] = {
{ 0x81, SONYPI_EVENT_MODEKEY_PRESSED },
{ 0x01, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x82, SONYPI_EVENT_PKEY_P1 },
{ 0x02, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x83, SONYPI_EVENT_PKEY_P2 },
{ 0x03, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x84, SONYPI_EVENT_PKEY_P3 },
{ 0x04, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x85, SONYPI_EVENT_PKEY_P4 },
{ 0x05, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x86, SONYPI_EVENT_PKEY_P5 },
{ 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x87, SONYPI_EVENT_SETTINGKEY_PRESSED },
{ 0x07, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0, 0 },
};
/*
......@@ -897,38 +916,59 @@ static const struct dmi_system_id sony_nc_ids[] = {
*/
static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
{
struct sony_nc_event *evmap;
u32 ev = event;
if (ev >= 0x90) {
/* New-style event */
int result;
int key_handle = 0;
ev -= 0x90;
if (sony_find_snc_handle(0x100) == ev)
key_handle = 0x100;
if (sony_find_snc_handle(0x127) == ev)
key_handle = 0x127;
if (key_handle) {
struct sony_nc_event *key_event;
if (sony_call_snc_handle(key_handle, 0x200, &result)) {
dprintk("sony_acpi_notify, unable to decode"
" event 0x%.2x 0x%.2x\n", key_handle,
ev);
/* restore the original event */
ev = event;
} else {
ev = result & 0xFF;
if (ev == 0x92) {
/* read the key pressed from EC.GECR
* A call to SN07 with 0x0202 will do it as well respecting
* the current protocol on different OSes
*
* Note: the path for GECR may be
* \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
* \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
*
* TODO: we may want to do the same for the older GHKE -need
* dmi list- so this snippet may become one more callback.
*/
if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
if (key_handle == 0x100)
key_event = sony_100_events;
else
ev = result & 0xFF;
}
key_event = sony_127_events;
if (sony_nc_events)
for (evmap = sony_nc_events; evmap->event; evmap++) {
if (evmap->data == ev) {
ev = evmap->event;
for (; key_event->data; key_event++) {
if (key_event->data == ev) {
ev = key_event->event;
break;
}
}
dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
if (!key_event->data)
printk(KERN_INFO DRV_PFX
"Unknown event: 0x%x 0x%x\n",
key_handle,
ev);
else
sony_laptop_report_input_event(ev);
}
} else if (sony_find_snc_handle(0x124) == ev) {
sony_nc_rfkill_update();
return;
}
} else
sony_laptop_report_input_event(ev);
dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
}
......@@ -953,9 +993,25 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
/*
* ACPI device
*/
static int sony_nc_function_setup(struct acpi_device *device)
{
int result;
/* Enable all events */
acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result);
/* Setup hotkeys */
sony_call_snc_handle(0x0100, 0, &result);
sony_call_snc_handle(0x0101, 0, &result);
sony_call_snc_handle(0x0102, 0x100, &result);
return 0;
}
static int sony_nc_resume(struct acpi_device *device)
{
struct sony_nc_value *item;
acpi_handle handle;
for (item = sony_nc_values; item->name; item++) {
int ret;
......@@ -970,13 +1026,188 @@ static int sony_nc_resume(struct acpi_device *device)
}
}
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
&handle))) {
if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL))
dprintk("ECON Method failed\n");
}
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
&handle))) {
dprintk("Doing SNC setup\n");
sony_nc_function_setup(device);
}
/* set the last requested brightness level */
if (sony_backlight_device &&
!sony_backlight_update_status(sony_backlight_device))
printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
/* re-initialize models with specific requirements */
dmi_check_system(sony_nc_ids);
return 0;
}
static void sony_nc_rfkill_cleanup(void)
{
int i;
for (i = 0; i < SONY_RFKILL_MAX; i++) {
if (sony_rfkill_devices[i])
rfkill_unregister(sony_rfkill_devices[i]);
}
}
static int sony_nc_rfkill_get(void *data, enum rfkill_state *state)
{
int result;
int argument = sony_rfkill_address[(long) data];
sony_call_snc_handle(0x124, 0x200, &result);
if (result & 0x1) {
sony_call_snc_handle(0x124, argument, &result);
if (result & 0xf)
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
} else {
*state = RFKILL_STATE_HARD_BLOCKED;
}
return 0;
}
static int sony_nc_rfkill_set(void *data, enum rfkill_state state)
{
int result;
int argument = sony_rfkill_address[(long) data] + 0x100;
if (state == RFKILL_STATE_UNBLOCKED)
argument |= 0xff0000;
return sony_call_snc_handle(0x124, argument, &result);
}
static int sony_nc_setup_wifi_rfkill(struct acpi_device *device)
{
int err = 0;
struct rfkill *sony_wifi_rfkill;
sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
if (!sony_wifi_rfkill)
return -1;
sony_wifi_rfkill->name = "sony-wifi";
sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_wifi_rfkill->get_state = sony_nc_rfkill_get;
sony_wifi_rfkill->user_claim_unsupported = 1;
sony_wifi_rfkill->data = (void *)SONY_WIFI;
err = rfkill_register(sony_wifi_rfkill);
if (err)
rfkill_free(sony_wifi_rfkill);
else
sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
return err;
}
static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
{
int err = 0;
struct rfkill *sony_bluetooth_rfkill;
sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
RFKILL_TYPE_BLUETOOTH);
if (!sony_bluetooth_rfkill)
return -1;
sony_bluetooth_rfkill->name = "sony-bluetooth";
sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get;
sony_bluetooth_rfkill->user_claim_unsupported = 1;
sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH;
err = rfkill_register(sony_bluetooth_rfkill);
if (err)
rfkill_free(sony_bluetooth_rfkill);
else
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
return err;
}
static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
{
int err = 0;
struct rfkill *sony_wwan_rfkill;
sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
if (!sony_wwan_rfkill)
return -1;
sony_wwan_rfkill->name = "sony-wwan";
sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_wwan_rfkill->get_state = sony_nc_rfkill_get;
sony_wwan_rfkill->user_claim_unsupported = 1;
sony_wwan_rfkill->data = (void *)SONY_WWAN;
err = rfkill_register(sony_wwan_rfkill);
if (err)
rfkill_free(sony_wwan_rfkill);
else
sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
return err;
}
static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
{
int err = 0;
struct rfkill *sony_wimax_rfkill;
sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
if (!sony_wimax_rfkill)
return -1;
sony_wimax_rfkill->name = "sony-wimax";
sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_wimax_rfkill->get_state = sony_nc_rfkill_get;
sony_wimax_rfkill->user_claim_unsupported = 1;
sony_wimax_rfkill->data = (void *)SONY_WIMAX;
err = rfkill_register(sony_wimax_rfkill);
if (err)
rfkill_free(sony_wimax_rfkill);
else
sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
return err;
}
static void sony_nc_rfkill_update()
{
int i;
enum rfkill_state state;
for (i = 0; i < SONY_RFKILL_MAX; i++) {
if (sony_rfkill_devices[i]) {
sony_rfkill_devices[i]->
get_state(sony_rfkill_devices[i]->data,
&state);
rfkill_force_state(sony_rfkill_devices[i], state);
}
}
}
static int sony_nc_rfkill_setup(struct acpi_device *device)
{
int result, ret;
if (sony_find_snc_handle(0x124) == -1)
return -1;
ret = sony_call_snc_handle(0x124, 0xb00, &result);
if (ret) {
printk(KERN_INFO DRV_PFX
"Unable to enumerate rfkill devices: %x\n", ret);
return ret;
}
if (result & 0x1)
sony_nc_setup_wifi_rfkill(device);
if (result & 0x2)
sony_nc_setup_bluetooth_rfkill(device);
if (result & 0x1c)
sony_nc_setup_wwan_rfkill(device);
if (result & 0x20)
sony_nc_setup_wimax_rfkill(device);
return 0;
}
......@@ -1024,11 +1255,24 @@ static int sony_nc_add(struct acpi_device *device)
dprintk("_INI Method failed\n");
}
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
&handle))) {
if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL))
dprintk("ECON Method failed\n");
}
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
&handle))) {
dprintk("Doing SNC setup\n");
sony_nc_function_setup(device);
sony_nc_rfkill_setup(device);
}
/* setup input devices and helper fifo */
result = sony_laptop_setup_input(device);
if (result) {
printk(KERN_ERR DRV_PFX
"Unabe to create input devices.\n");
"Unable to create input devices.\n");
goto outwalk;
}
......@@ -1063,9 +1307,6 @@ static int sony_nc_add(struct acpi_device *device)
}
/* initialize models with specific requirements */
dmi_check_system(sony_nc_ids);
result = sony_pf_add();
if (result)
goto outbacklight;
......@@ -1131,6 +1372,7 @@ static int sony_nc_add(struct acpi_device *device)
sony_laptop_remove_input();
outwalk:
sony_nc_rfkill_cleanup();
return result;
}
......@@ -1156,6 +1398,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
sony_pf_remove();
sony_laptop_remove_input();
sony_nc_rfkill_cleanup();
dprintk(SONY_NC_DRIVER_NAME " removed.\n");
return 0;
......@@ -1195,7 +1438,6 @@ static struct acpi_driver sony_nc_driver = {
#define SONYPI_TYPE1_OFFSET 0x04
#define SONYPI_TYPE2_OFFSET 0x12
#define SONYPI_TYPE3_OFFSET 0x12
#define SONYPI_TYPE4_OFFSET 0x12
struct sony_pic_ioport {
struct acpi_resource_io io1;
......@@ -1328,6 +1570,7 @@ static struct sonypi_event sonypi_pkeyev[] = {
{ 0x01, SONYPI_EVENT_PKEY_P1 },
{ 0x02, SONYPI_EVENT_PKEY_P2 },
{ 0x04, SONYPI_EVENT_PKEY_P3 },
{ 0x20, SONYPI_EVENT_PKEY_P1 },
{ 0, 0 }
};
......@@ -1371,6 +1614,7 @@ static struct sonypi_event sonypi_zoomev[] = {
{ 0x39, SONYPI_EVENT_ZOOM_PRESSED },
{ 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },
{ 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },
{ 0x04, SONYPI_EVENT_ZOOM_PRESSED },
{ 0, 0 }
};
......@@ -1401,6 +1645,19 @@ static struct sonypi_event sonypi_batteryev[] = {
{ 0, 0 }
};
<